星期五, 10月 18, 2024

IBM x3250 M3 - 指示橘燈

要拿這台 12 年的 [IBM x3250 M3] 來當成特定機台的 File Server,一開機就發現指示燈亮橘燈且風扇狂轉,發現是 BIOS System Log 滿造成亮橘燈,最新的 System Log 還在 2016 年,把 System Log 清除並完全斷電後再重啟,指示橘燈才熄滅且風扇轉速恢復正常

指示橘燈
第一直覺是 Disk 出問題,但 MegaRAID 顯示正常
發現 BIOS System Log 已滿訊息

星期三, 10月 16, 2024

[SQL] 視窗函數應用 - 連續字串分組

延續 [SQL] 分析函數應用 - 排班班表,班別基本上是連續字串,想要直接處理字串就產生群組,而不依賴日期欄位,資料來源和 TSQL 流程如下圖
TSQL
DECLARE @Temp TABLE
(
    ID int identity(1,1) ,
    Col1 char(1)
)

INSERT INTO @Temp VALUES
    ('A') , ('A') , ('A') , ('B') , ('C') , ('C') , ('A') , ('A') ,  ('A')

;
WITH T1 AS
(
    SELECT 
        * , 
        -- 第三參數:null 的預設值
        LAG(Col1 , 1 , Col1) OVER (ORDER BY ID)AS PreValue
    FROM @Temp
)
, T2 AS
(
    SELECT
        * ,
        -- 判斷連續字串是否有變
        CAST(IIF(Col1 = PreValue , 0 , 1) AS int) AS GroupChange
    FROM T1
)
, T3 AS
(
    SELECT
        * ,
        -- 使用彙總視窗函數跑 Running Total
        SUM(GroupChange) OVER (ORDER BY ID ROWS UNBOUNDED PRECEDING) AS GroupNO 
	FROM T2
)
SELECT * 
FROM T3
ORDER BY ID
LAG default 參數

官方文件說明
The value to return when offset is beyond the scope of the partition. If a default value is not specified, NULL is returned. default can be a column, subquery, or other expression, but it cannot be an analytic function. default must be type-compatible with scalar_expression.
該範例就是把第一筆資料的 null 塞 A 來取代

星期二, 10月 15, 2024

[SQL] 分析函數應用 - 排班班表

社群問題,簡化為下圖來理解,資料來源只顯示單一位員工資訊
相關商業邏輯
  • WorkWay 班別會混雜 [特、休] 休假資訊,A 代表 08-16 早班時段、B 代表 13-21 午班時段 
  • [特、休] 所屬班別判斷邏輯:班別是若遇當月休假換班別,就歸屬前一個班別,例如 10/13 休 10/14 換成 A 班,那麼 10/13 會往前抓到最後一天的班別,但是像月初特休就會往後抓班別來判斷

資料來源
USE TempDB
GO

DROP TABLE IF EXISTS #Attend

CREATE TABLE #Attend(
	Name nvarchar(20) ,
	WorkDate date ,
	WorkWay nvarchar(5))

INSERT INTO #Attend
VALUES 
	(N'小明','2024/10/1' ,N'A' ) , (N'小明','2024/10/2' ,N'A' ) , (N'小明','2024/10/3' ,N'A' ) , (N'小明','2024/10/4' ,N'A' ) , (N'小明','2024/10/5' ,N'休') , 
	(N'小明','2024/10/6' ,N'休') , (N'小明','2024/10/7' ,N'A' ) , (N'小明','2024/10/8' ,N'A' ) , (N'小明','2024/10/9' ,N'A' ) , (N'小明','2024/10/10',N'休') ,
	(N'小明','2024/10/11',N'A' ) , (N'小明','2024/10/12',N'休') , (N'小明','2024/10/13',N'休') , (N'小明','2024/10/14',N'B' ) , (N'小明','2024/10/15',N'B' ) ,
	(N'小明','2024/10/16',N'B' ) , (N'小明','2024/10/17',N'B' ) , (N'小明','2024/10/18',N'B' ) , (N'小明','2024/10/19',N'休') , (N'小明','2024/10/20',N'休') ,
	(N'小明','2024/10/21',N'A' ) , (N'小明','2024/10/22',N'A' ) , (N'小明','2024/10/23',N'A' ) , (N'小明','2024/10/24',N'A' ) , (N'小明','2024/10/25',N'A' ) ,
	(N'小明','2024/10/26',N'休') , (N'小明','2024/10/27',N'休') , (N'小明','2024/10/28',N'A' ) , (N'小明','2024/10/29',N'A' ) , (N'小明','2024/10/30',N'A' ) , (N'小明','2024/10/31',N'A') ,
	(N'大白','2024/10/1' ,N'特') , (N'大白','2024/10/2' ,N'特') , (N'大白','2024/10/3' ,N'B' ) , (N'大白','2024/10/4' ,N'B' ) , (N'大白','2024/10/5' ,N'休') ,	(N'大白','2024/10/6' ,N'休') , (N'大白','2024/10/7' ,N'B' ) , (N'大白','2024/10/8' ,N'B' ) , (N'大白','2024/10/9' ,N'B' ) , (N'大白','2024/10/10',N'休') ,
	(N'大白','2024/10/11',N'B' ) , (N'大白','2024/10/12',N'休') , (N'大白','2024/10/13',N'休') , (N'大白','2024/10/14',N'A' ) , (N'大白','2024/10/15',N'A' ) ,
	(N'大白','2024/10/16',N'A' ) , (N'大白','2024/10/17',N'A' ) , (N'大白','2024/10/18',N'A' ) , (N'大白','2024/10/19',N'休') , (N'大白','2024/10/20',N'休') ,
	(N'大白','2024/10/21',N'B' ) , (N'大白','2024/10/22',N'B' ) , (N'大白','2024/10/23',N'B' ) , (N'大白','2024/10/24',N'B' ) , (N'大白','2024/10/25',N'B' ) , 
	(N'大白','2024/10/26',N'休') , (N'大白','2024/10/27',N'休') , (N'大白','2024/10/28',N'B' ) , (N'大白','2024/10/29',N'B' ) , (N'大白','2024/10/30',N'B ') , (N'大白','2024/10/31',N'B')
TSQL 寫法

主要分為三個步驟,分別為
  • 把 [特、休] 字樣改成所屬班別,使用分析視窗函數參數 IGNORE NULLS 來整理
  • 透過連續日期、連續班別來判斷群組
  • 產生對應需求資料
; 
WITH T1 AS 
( 
    SELECT * ,
        IIF(WorkWay IN ('A' , 'B') , WorkWay , NULL) AS NewWorkWay
    FROM #Attend
)
, T2 AS
(
    SELECT * ,
        COALESCE(
            NewWorkWay ,
            LAG(NewWorkWay) IGNORE NULLS OVER (PARTITION BY Name ORDER BY WorkDate) ,
            LEAD(NewWorkWay) IGNORE NULLS OVER (PARTITION BY Name ORDER BY WorkDate)) AS GroupNO
    FROM T1
)
, T3 AS 
(
    SELECT 
        * ,
        DATEADD(
            d , 
            ROW_NUMBER() OVER (PARTITION BY Name , GroupNO ORDER BY WorkDate) * -1 , 
            WorkDate) AS GroupDate
        FROM T2
)
, T4 AS
(
    SELECT 
        T3.Name , 
        T3.GroupDate ,
        T3.GroupNO ,
        MIN(WorkDate) AS MinDate ,
        T3.GroupNO + ':' + CAST(DAY(MIN(WorkDate)) AS varchar(2)) + '~' + CAST(DAY(MAX(WorkDate)) AS varchar(2)) AS WorkInfo
    FROM T3
    GROUP BY Name , GroupDate , GroupNO
)
SELECT
    Name ,
    STRING_AGG(WorkInfo , ' , ') WITHIN GROUP (ORDER BY MinDate)
FROM T4
GROUP BY Name
分析視窗函數參數 IGNORE NULLS

因為 WorkWay 有 [特、休] 字樣來呈現該員工休假狀態,所以要把 [特、休] 改為 null,方便後續分析函數搭配 IGNORE NULLS,下面語法故意把 LAG 產生的 PreviousValue 和 LEAD 產生的 NextValue 資料獨立出來顯示,從圖片就可以觀察到 T1 和 T2 這兩個階段處理事項
; 
WITH T1 AS 
( 
    SELECT * ,
        IIF(WorkWay IN ('A' , 'B') , WorkWay , NULL) AS NewWorkWay
    FROM #Attend
    WHERE Name = N'大白'
)
, T2 AS
(
    SELECT * ,
        -- 顯示資料來理解
        LAG(NewWorkWay) IGNORE NULLS OVER (PARTITION BY Name ORDER BY WorkDate) AS PreviousValue ,
        -- 顯示資料來理解
        LEAD(NewWorkWay) IGNORE NULLS OVER (PARTITION BY Name ORDER BY WorkDate) AS NextValue ,
        COALESCE(
            NewWorkWay ,
            LAG(NewWorkWay) IGNORE NULLS OVER (PARTITION BY Name ORDER BY WorkDate) ,
            LEAD(NewWorkWay) IGNORE NULLS OVER (PARTITION BY Name ORDER BY WorkDate)) AS GroupNO
    FROM T1
)
SELECT * FROM T2
ORDER BY Name , WorkDate

透過連續日期、連續班別來判斷群組

透過 Name 和 GroupNO 來跑 Row_Number() 並和工作日期運算來判斷群組
, T3 AS 
(
    SELECT 
        * ,
        -- 顯示資料來理解
        ROW_NUMBER() OVER (PARTITION BY Name , GroupNO ORDER BY WorkDate) * -1 AS DisplayRowNO,
        DATEADD(
            d , 
            ROW_NUMBER() OVER (PARTITION BY Name , GroupNO ORDER BY WorkDate) * -1 , 
            WorkDate) AS GroupDate
    FROM T2
)
SELECT * FROM T3
ORDER BY Name , WorkDate

產生對應需求資料

分出員工連續工作日後,就可以找出每個群組起始和結束日期,再把資料整理成問題需求,最後使用 STRING_AGG 把資料串在一起呈現
, T4 AS
(
    SELECT 
        T3.Name , 
        T3.GroupDate ,
        T3.GroupNO ,
        MIN(WorkDate) AS MinDate ,
        T3.GroupNO + ':' + CAST(DAY(MIN(WorkDate)) AS varchar(2)) + '~' + CAST(DAY(MAX(WorkDate)) AS varchar(2)) AS WorkInfo
    FROM T3
    GROUP BY Name , GroupDate , GroupNO
)
SELECT
    Name ,
    STRING_AGG(WorkInfo , ' , ') WITHIN GROUP (ORDER BY MinDate)
FROM T4
GROUP BY Name

星期六, 10月 12, 2024

[SQL] 伺服器定序 - 變數大小寫

Line 討論發現到,原來定序會影響 TSQL 撰寫時的大小寫,原以為定序只對資料排序有影響而已,在官方文件 - 設定或變更伺服器定序 上發現文字說明
變數、資料指標和 GOTO 標籤的名稱。 例如,如果伺服器層級定序區分大小寫,變數 @pi 和 @PI 會視為不同的變數;如果伺服器層級定序不區分大小寫,則會視為相同的變數。
下列 TSQL 語法和圖示可以觀察到伺服器定序為 Chinese_Taiwan_Stroke_CS_AS 區分大小寫,可以看見變數大小寫在 IDE 上會有錯誤提示
SELECT SERVERPROPERTY('collation')

星期四, 10月 10, 2024

[C#] DataGridViewColumn.Clone()

延續 [C#] 提供資料行已經屬於 DataGridView 控制項 筆記,發現原來 DataGridViewColumn.Clone() 產生 DataGridViewColumn 是可以再放進其他 DataGridViw

C# Code
private void btnClone_Click(object sender, EventArgs e)
{
    // 寫法一
    List<DataGridViewColumn> cloneColumn = new List<DataGridViewColumn>();
    foreach (DataGridViewColumn col in dataGridView1.Columns)
        cloneColumn.Add(col.Clone() as DataGridViewColumn);

    // 寫法二
    List<DataGridViewColumn> cloneColumn = dataGridView1.Columns.OfType<DataGridViewColumn>()
        .Select(col => col.Clone() as DataGridViewColumn)
        .ToList();

    dataGridView2.Columns.AddRange(cloneColumn.ToArray());
}
從中斷點內可以觀察到 DataGridViewColumn.DataGridView 屬性變成 null
實際執行