星期五, 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
實際執行

星期二, 10月 08, 2024

[CCNA] 動態路由 - OSPF - 優先權

延續 [CCNA] 動態路由 - OSPF[CCNA] 動態路由 - OSPF - 負載平衡 筆記,從 router0 => router1 有兩條路由,如下圖
上述兩條路由 AD 相同,但 Cost 不同,優先是以 router0 => router1 為主,當 router0 => router1 不通時,才會改成 router0 => router3 => router2 => router1 ,Cost 計算可以參考 認識OSPF路由協定 活用相關指令設定參數值 內的公式介紹,兩條路由成本分別為
  • router0 => router1 :65
  • router0 => router3 => router2 => router1:193
OSPF AD 值預設為 110

router0 => router1

router0 => router3 => router2 => router1

故意關閉 router0 上往 router1 網卡

星期一, 10月 07, 2024

[CCNA] 動態路由 - OSPF - 負載平衡

延續 [CCNA] 動態路由 - OSPF 筆記,從 router0 至 router2 有兩條路由可以到達,可以達到負載平衡效果
show ip route

重新開啟 Packet Tracer 進入 router0 CLI 時發現 OSPF 自動進行同步,Router IP 以 192.168.120.1 (深綠) 和 192.168.140.1 (淺綠) 來表示,是因為該 IP 為 Router 上最大的 IPAddress 

紫框為 router0 至 router2 的 OSPF 路由設定

星期日, 10月 06, 2024

[CCNA] 動態路由 - OSPF

OSPF (Open Shortest Path First),簡單說就是讓 router 自行交換路由,不用每個都手動建立
在 Router 上啟動 OSPF 並進行設定

紀錄 router0、router1、router2 啟用和設定方式,router3 就省略囉,主要設定語法為
router ospf ProcessID => ProcessID 為識別用
network IPAddress wildcard area AreaID => 該範例會設定在同一個 area 內
wildcard 請參考該篇文章 - Wild Card反遮罩

router0 上啟用 OSPF, network 192.168.0.0 0.0.255.255 area 0,代表 192.168 開頭都會啟用 OSPF
router1 啟用 OSPF,network 192.0.0.0 0.255.255.255 area 0,代表 192 開頭都會啟用 OSPF
router2 啟用 OSPF,network 0.0.0.0 255.255.255.255 area 0,代表全部 IP 都會啟用 OSPF 
當 router OSPF 啟動後,每個 router 會出現同步訊息,Full 代表已經交換完,之後就是定期發出 Hello 封包確認 router 路由設定是否有異動
show ip route

四個 router 都啟用 OSPF 後,在 router0 上確認 OSPF 路由
show ip protocols

該語法可以查詢 Router 上 OSPF 設定,下圖以 router0 為例紀錄
Cient 端 PC 進行測試 OSPF 設定檢查
 

星期二, 10月 01, 2024

[SSRS] 雙面列印

被告知要製作郵件報表 (不知道這樣稱呼適不適當就是),一張 A4 紙兩頁都會有文字說明
  • 第一頁 (正面):為郵寄相關地址
  • 第二頁 (反面):我方提供給對方確認內容
第一頁三摺就可以寄出去,對方確認後反摺可以寄回來

透過雙面列印這想法完成該需求,重點如下
  • 報表來源
  • 資料成對
  • 識別第一頁、第二頁
以下會針對這三點來筆記

報表來源

報表來源是以 class 為主,所以該 class 會同時有第一頁和第二頁 property,用個 super class 承載全部資料,且該 class 必須自訂 PageNumber 來判斷是第一頁、第二頁
namespace PageNumberVisible
{
    public class ReportModel
    {
        public int PageNumber { get; set; }

        // 第一頁 Property

        // 第二頁 Property
    }
}

資料成對

是在 C# WinForm 上搭配 ReportViewer 來進行列印,所以在 AP 端要把資料變成兩筆,並同時給 PageNumber 值
using Microsoft.Reporting.WinForms;
using System;
using System.Collections.Generic;
using System.Windows.Forms;

namespace PageNumberVisible
{
    public partial class Form1 : Form
    {
        public Form1()
        {
            InitializeComponent();
        }

        private void Form1_Load(object sender, EventArgs e)
        {
            // 理論上資料來源變成兩筆並要特別注意資料排序,筆記就簡易紀錄就好
            List<ReportModel> reportSource = new List<ReportModel>();
            reportSource.Add(new ReportModel() { PageNumber = 1 });
            reportSource.Add(new ReportModel() { PageNumber = 2 });
            reportSource.Add(new ReportModel() { PageNumber = 1 });
            reportSource.Add(new ReportModel() { PageNumber = 2 });

            this.reportViewer1.LocalReport.DataSources.Add(new ReportDataSource(nameof(ReportModel), reportSource));
            this.reportViewer1.RefreshReport();
        }
    }
}

識別第一頁、第二頁

控件可見性屬性內使用 VB.NET MOD 函數搭配頁碼來判斷是第一頁、第二頁
  • 頁首、頁尾:使用 SSRS 內建欄位 PageNumber 
  • 報表主體:使用 class 內自訂的 PageNumber 欄位
另外不是每個控件 GUI 內都會有可見性可以進行設定,沒有的話,可以在屬性視窗內找到 Hide,英文和中文翻譯剛好相反詞

內建欄位 PageNumber 只能應用在頁首、頁尾 


線條控件就沒有 GUI 可見性,只能在屬性視窗內使用 Hide 設定
報表呈現設計

在報表頁首和實體內放三個控見示意,左側控件是顯示頁碼,右側控件則是根據頁碼來決定是否顯示

報表執行

第三頁 (正面)
第四頁 (反面)

輸出時選擇印表機雙面列印,兩兩一份就可以完成該需求

星期六, 9月 28, 2024

[SSRS] Data retrieval failed for the subreport

使用子報表時出現該錯誤訊息

原以為是資料異常造成,後來發現是 SSRS 內的資料集名稱打錯,造成該情況

星期四, 9月 26, 2024

[Shopify] hCaptcha

被告知對外聯絡信箱最近垃圾信件增加不少才去了解,發現 Shopify 垃圾郵件防護沒有啟用,還很意外注意到垃圾郵件防護是使用 hCaptcha,直覺是 Google reCaptcha 才是,在 Shopify 官方 changelog 有找到這兩篇文章

官方文件 - 使用 Google reCAPTCHA 保護您的網路商店 上還是說是使用 Google reCaptcha

網路商店 => 偏好設定 => 垃圾郵件防護,畫面上是 hCaptcha

Shopify 網站右下角有出現 hCaptcha 圖示

星期一, 9月 23, 2024

[CCNA] 預設路由

當設定靜態路由都不通、預設路由就是最後的 routing 選擇或是不論網段一律往特定路由跑時,可以以預設路由來取代一個一個網段設定

簡易網路拓樸圖
使用 ip route connected 確認目前連線
建立預設路由,要透過它連往 192.168.30.0 網段
在 PC0 上 ping Laptop0 確認預設路由是正常的

星期日, 9月 22, 2024

[SSRS] TextDecoration

要在 SSRS Textbox 內設定文字下斜線,該設定沒有 GUI 畫面可以設定,必須在屬性視窗內選擇,分別為 Default、None、Underline 、Overline、LineThrough,Underline 和 LineThrough 可以猜到文字效果,但 Overline 就完全沒有不知道
Underline 、Overline、LineThrough 文字效果,Overline 查資料應該是文字上橫線,但實際設定沒有效果出來
在該官方文章 - TextBox.TextDecorations Property 內的 Overline 效果,沒有找到 SSRS 專屬文件說明
開發環境是 VS2022 WinForm 搭配 ReportViewer

星期六, 9月 21, 2024

[SSRS] TextBox 運算式內換行

SSRS TextBox 純文字輸入換行,基本上是很直覺的操作,但常常是運算式內同時有固定文字、參數和資料集欄位等混在一起呈現內容,該情況要換行的話,要使用 VB.NET 換行符號 - vbcrlf

運算式內設定

星期五, 9月 20, 2024

[CCNA] 靜態路由 - 優先權

延續 [CCNA] 靜態路由 - 負載平衡 筆記,在負載平衡練習時設定靜態路由並沒有指定 AD/Cost,預設為 [1/0] 如下圖
接下來會修改 192.168.100.2 的 Administrator Distance (AD) 來達到路由備援機制,AD 越小表示優先權越大,所以預設會是以 192.168.20.2 為主要線路,當 192.168.20.2 發生異常時,才會切至 192.168.100.2

CLI 內設定 AD

重建 AD 為 3 的 192.168.100.2 靜態路由,先使用 no ip route 刪除後,再使用 ip route 並指定 AD (下圖紅線),設定完成後查詢只會看見 [1/0] 192.168.20.2 靜態路由而已 (下圖藍框),當切換至 [3/0] 192.168.100.2 時,查詢才會出現 192.168.100.2 靜態路由
使用 Tracert 進行測試

從 1921.68.10.2 使用 tracert 192.168.30.2,第一次可以看見是從 192.168.20.2 過去,故意把網卡關閉後,再進行 tracert 就可以看見切至 192.168.100.2 過去
再次查詢靜態路由,就會出現 [3/0] 192.168.100.2
Router1 設定方式是一樣的,就不紀錄囉