星期日, 5月 30, 2021

[SQL] Like - 萬用字元

改寫 AP 時注意到 TSQL 上有使用到 LIKE _ 萬用字元,傳入參數會像是 A_____12345,所以好奇去觀察統計資訊和執行計畫,在執行計畫中注意到神奇的 operator

該 operator 竟然是 nested loop + nonclustered index seek 讀取全部 Table 資料後,卻只輸出一筆資料
仔細閱讀這 12 行 TSQL 和執行計畫後發現
  • 隱含轉換:因為是 ORM 產生 TSQL 語法,沒有注意到 Table 欄位是 char,但傳入參數是 nvarchar 造成隱含轉換,四個條就有三個是隱含轉換
  • 違反 SARG 原則,使用反向條件:明明條件可以寫成 [欄位 = '未結案']就好,卻要寫[欄位 <> '結案'],資料就[結案]和[未結案]這兩個狀態而已啊
修正上述兩點後,執行計畫就恢復正常

該案例只觀察統計資訊的話,大概不會想要花時間 turning,其實數據都還蠻低的
 
修正前修正後
關鍵 Table Logical Read56810
CPU Time281ms0ms

關鍵 operator:[讀取的資料列數] 從 244,679 降低為 743,743 就是未結案資料
執行計畫成本差異

星期五, 5月 28, 2021

[Godex] NetSetting 搜尋網路印表機

使用 Godex NetSetting 搜尋網內標籤機,竟然完全找不到標籤機

在介面上找到 Network Cards 選項

NetSetting - 搜尋網路印表機-1 

在 Network Cards 內可以發現筆電上有多張網路卡,可能跟有開啟 Hyper-V 有關係,每張網卡後方都有 IP 資訊,很容易識別該選哪一張網卡

  NetSetting - 搜尋網路印表機-2 

選定網路卡後再重新搜尋,區域內的標籤機就全部出現啦

  NetSetting - 搜尋網路印表機-3

星期四, 5月 27, 2021

[C#] SplitContainer

根據官方文章
建立一個 Outlook 樣式的 layout

C# Code
using System;
using System.Windows.Forms;

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

            treeView1.Nodes.Add("treeView");
            listView1.Items.Add("listView");

            // 表示分隔器的寬度 (以像素為單位)。 預設為 4 個像素
            splitContainer1.SplitterWidth = 4;
            // 表示從 SplitContainer 的左邊緣或上邊緣算起的分隔器位置 (以像素為單位)。 預設值為 50 像素。
            splitContainer1.SplitterDistance = 200;
            // 表示 SplitContainer 面板的水平或垂直方向,預設為 Vertical
            splitContainer1.Orientation = Orientation.Vertical;
            // 表示分隔器移動的增量 (以像素為單位)。 預設值為 1 個像素。
            splitContainer1.SplitterIncrement = 100;
            // 表示分隔器為固定,則為 true,否則為 false。 預設為 false。
            splitContainer1.IsSplitterFixed = false;

            splitContainer2.SplitterWidth = 4;
            splitContainer2.SplitterDistance = 100;
            splitContainer2.Orientation = Orientation.Horizontal;
        }

        private void checkBox1_CheckedChanged(object sender, EventArgs e)
        {
            // 建立一個 CheckBox 來顯示、隱藏 TreeView
            splitContainer1.Panel1Collapsed = !splitContainer1.Panel1Collapsed;
        }
    }
}
執行結果

[C#] SpliterContainer

SplitContainer 控制項預設為鍵盤存取;,如果 IsSplitterFixed 設定為 false,則使用者可以按方向鍵移動分隔器

星期一, 5月 24, 2021

[C#] TextBox 的 Enter 鍵執行

TextBox 內按下 Enter 來進行程式執行是很常見的應用,寫成一個自訂控件使用,避免一直重覆寫判斷按下 Enter 的 Code

使用者自訂控件:UCTextBox
using System;
using System.Windows.Forms;

namespace UCPressEnter
{
    public class UCTextBox : TextBox
    {
        public event EventHandler PressEnter;

        public void OnPressEnter()
        {
            PressEnter?.Invoke(this, new EventArgs());
        }

        // 方法一:透過 KeyDown 事件觸發
        protected override void OnKeyDown(KeyEventArgs e)
        {
            if (e.KeyCode == Keys.Enter || e.KeyCode == Keys.Return)
                this.OnPressEnter();

            base.OnKeyDown(e);
        }

        // 方法二:透過 ProcessCmdKey
        protected override bool ProcessCmdKey(ref Message msg, Keys keyData)
        {
            if (keyData == Keys.Enter || keyData == Keys.Return)
                this.OnPressEnter();

            return base.ProcessCmdKey(ref msg, keyData);
        }
    }
}
Form 內使用 UCTextBox 並註冊 PressEnter 事件來使用
using System;
using System.Windows.Forms;

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

        private void ucTextBox1_PressEnter(object sender, EventArgs e)
        {
            MessageBox.Show("PressEnter 事件觸發" , " PressEvent 事件" , MessageBoxButtons.OK , MessageBoxIcon.Information);
        }
    }
}
執行結果

星期日, 5月 23, 2021

[C#] ToolTip

官方文章 - ToolTip 類別 內的範例說明,下圖為 ToolTip 效果

[C#] ToolTip

C# Code
using System;
using System.Windows.Forms;

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

        private void Form1_Load(object sender, EventArgs e)
        {
            ToolTip tp = new ToolTip();

            // 設定提示視窗的圖式
            tp.ToolTipIcon = ToolTipIcon.Info;

            // 設定提示視窗的標題。
            tp.ToolTipTitle = "提示訊息";

            // false:標準矩形視窗,此為預設;true:汽球樣式視窗
            tp.IsBalloon = true;

            // 當指標靜止於控制項上時,ToolTip 保持可見的時間 (以毫秒為單位)。 預設值為 5000。
            tp.AutoPopDelay = 5000;

            // 在顯示工具提示視窗之前,指標必須保持靜止在控制項上的時間 (以毫秒為單位)。
            tp.InitialDelay = 1000;

            // 後續工具提示視窗出現之前經過的時間長度 (以毫秒為單位)。
            tp.ReshowDelay = 500;

            // 父控制項為非現用時,是否也會顯示工具提示視窗
            tp.ShowAlways = true;

            // 設定控件顯示文字
            tp.SetToolTip(this.button1, "My button1");
            tp.SetToolTip(this.checkBox1, "My checkBox1");
        }
    }
}

星期五, 5月 21, 2021

[SQL] Foreign Key - Delete Cascade 執行計畫

觀察 Foreign Key 的 Delete Cascade (重疊顯示) 刪除時的執行計畫

SSMS 內 Foreign Key 的設定截圖

[SQL] Foreign Key - Delete Cascade 執行計畫-1

TSQL 測試語法
USE tempdb
GO

DROP TABLE IF EXISTS tblMaster
DROP TABLE IF EXISTS tblDetail

CREATE TABLE tblMaster (MasterID int Primary Key)
CREATE TABLE tblDetail (DetailID int Primary Key, MasterID int)

-- 建立 FK
ALTER TABLE tblDetail DROP CONSTRAINT IF EXISTS FK_tblDetail_tblMaster
ALTER TABLE dbo.tblDetail WITH CHECK 
	ADD CONSTRAINT FK_tblDetail_tblMaster 
	FOREIGN KEY (MasterID) REFERENCES dbo.tblMaster(MasterID) 
		ON UPDATE NO ACTION 
		ON DELETE CASCADE -- Delete Cascade (重疊顯示)

-- 建立 Detail 上,Foreign Key 對應欄位 Index
CREATE INDEX IX_tblDetail_MasterID ON tblDetail (MasterID)

-- 直接刪除資料並觀察執行計畫
DELETE FROM tblMaster WHERE MasterID = 13
執行計畫

[SQL] Foreign Key - Delete Cascade 執行計畫-2
發現刪除過程會用到 Temp DB 來暫存

星期五, 5月 14, 2021

[SQL] 死結解析工具

在網路上看見有人利用 SentryOne Plan Explorer 來閱讀 DeadLock xml 檔案,之前都只用它來觀察執行計畫,原來也可以用來看 DeadLock 資訊

  [SQL] DeadLock 解析工具

功能簡易說明
  • 上方資訊和下方圖式彼此互相連動
  • 上方資訊的 TextData 為相關 TSQL 語法
  • 犧牲方會利用紅色來表示
  • 線條圖示為鎖定類型

星期五, 5月 07, 2021

[SQL] 本機伺服器

在 product 上發現一個不存在的 Linked Server,有一隻 Store Procedure 是在 remote 上透過 Linked Server 把資料送回 product,但被同事改為在 product 上透過 Linked Server 把 remote 資料拉回來,看到語法時 remote 和 product 都是用四部分名稱,但 Lniked Server 清單內明明就只有 remote 而已,最訝異的這正常執行很長一段時間,手動執行也是正常的
INSERT INTO ProductServerName.DBName.SchemaName.TableName (ColList)
SELECT ColList
FROM RemoteServerName.DBName.SchemaName.TableName
研究發現,原來以前放在 remote 上時的 Linked Server 取名跟 product 名稱是一樣的,移轉回來後同事也沒有特別把 product 的四部份名稱修正,所以在 product 上用四部分名稱讀取 product 本身資料,完全沒想過可以這樣用
 
sys.servers 官方文件上找到 server_id 說明
When server_id = 0, the returned value is the server name.
When server_id > 0, the returned value is the local name of linked server.

 從 sys.servers 可以查到本機,is_linked = 0

[SQL] 本機伺服器-1

透過四部份名稱存取自身資料

[SQL] 本機伺服器-2 

該案例打破一個觀念,不是用四部分名稱存取就是 Linked Server 啦

星期三, 5月 05, 2021

[SQL] 遺失索引案例

從 [資料收集-查詢統計資料紀錄] 報表內發現,有一個 Query 統計圖特別突出

[SQL] 遺失索引案例-1

該 Query 是 Linked Server 抓取 remote 資料並進行比對後 insert 回 Local 端保留,一看到是 Linked Server 原以為是把 remote 資料都抓回 Local 端後才進行篩選導致,實際執行後發現是 Local Table 缺少 Index 造成,把 missing index 補上去就結案

TSQL 語法示意
INSERT INTO LocalERTable
(
	ColList
)
SELECT
	ColList
FROM LinkedServerRemoteTable AS L
	JOIN LocalECTable AS C ON L.KeyCol = C.KeyCol
WHERE NOT EXISTS (
		SELECT 1 AS data
		FROM LocalERTable AS R
		WHERE R.KeyCol = L.KeyCol
			AND R.SmallDateTimeCol = L.SmallDateTimeCol)
	AND L.SmallDateTimeCol >= @Date
	AND L.SmallDateTimeCol < DATEADD(D,1,@Date)
	AND C.IsValidCol = 0

星期一, 5月 03, 2021

[SQL] SQL Agent 10016 錯誤

在事件檢視器內發現該錯誤,發現是 SQL Server Agent 服務帳號沒有權限所產生的錯誤
找不到來源 'DCOM' 中事件 ID '10016' 的描述。本機電腦可能沒有所需的登錄資訊或訊息 DLL 檔案,因此無法顯示訊息,或者您可能沒有存取的使用權限。下列資訊是部分事件:'應用程式特定', '本機', '啟用', '{2DC39BD2-9CFF-405D-A2FE-D246C976278C}', '{DB336D8E-32E5-42B9-B14B-58AAA87CEB06}', 'NT SERVICE', 'SQLAgent$InstanceName, 'S-1-5-80-1209425544-326843345-2586489880-3248839733-3906339120', 'LocalHost (使用 LRPC)', '無法使用', '無法使用'

應用程式特定 權限設定無法將含有 CLSID {2DC39BD2-9CFF-405D-A2FE-D246C976278C}與 APPID {DB336D8E-32E5-42B9-B14B-58AAA87CEB06}之 COM 伺服器應用程式的 本機 啟用 權限授與來自位址 LocalHost (使用 LRPC) (在應用程式容器 無法使用 SID (無法使用) 中執行) 的使用者 NT SERVICE\SQLAgent$InstanceName SID (S-1-5-80-1209425544-326843345-2586489880-3248839733-3906339120)。您可以使用元件服務系統管理工具修改此安全性權限。
  [SQL] SQL Agent 10016 錯誤-1

該問題似乎存在很久,Google 一下就有解法

系統管理工具 => 元件服務 => 電腦 => 我的電腦 => DCOM 設定 => Microsoft SQL Server Integartion Services 13.0

[SQL] SQL Agent 10016 錯誤-2

內容 => 安全性 => 啟動和啟用權限 => 編輯

[SQL] SQL Agent 10016 錯誤-3

把 SQL Server Agent 服務帳號 - NT Service\SQLAgent$InstanceName (錯誤訊息上的服務帳號) 加入,並授予 [本機啟動 (預設已勾選)] 和 [本機啟用] 

[SQL] SQL Agent 10016 錯誤-4