# 2021-01-18 SQL Training Day 2 ## 06 Error Handling ```sql= use tempdb set language 繁體中文 begin try drop table if exists #t create table #t(c1 int primary key) insert #t values(1),(1) end try begin catch select ERROR_LINE(),ERROR_MESSAGE(),ERROR_NUMBER(),ERROR_PROCEDURE(),ERROR_SEVERITY(),ERROR_STATE() end catch ``` ![](https://i.imgur.com/px3O9fU.png) - 同一錯誤訊息的各國語言版本查詢 ```sql= select * from sys.messages where message_id = 2627 ``` ![](https://i.imgur.com/Y3ltT4U.png) ```sql= raiserror('hello %s',16,1,'sql') raiserror('hello %s',20,1,'sql') with log ``` - Severity (ERROR_SEVERITY(), 錯誤層級): - 10 以下是 info - 11 以上是 error,客製的應拋 16 - 20 以上是致命錯誤 - Error number (ERROR_NUMBER()): - 50000 是預設 - 50001 以上是客製 - SQL Server Profiler ![](https://i.imgur.com/22Oa7DC.png) ```sql= use tempdb set language 繁體中文 create or alter proc sp as begin try drop table if exists #t create table #t(c1 int primary key) insert #t values(1),(1) end try begin catch declare @msg nvarchar(max)=error_message() select ERROR_LINE(),@msg,ERROR_MESSAGE(),ERROR_NUMBER(),ERROR_PROCEDURE(),ERROR_SEVERITY(),ERROR_STATE(); --raiserror(@mag,16,1) throw; end catch go exec sp ``` - RAISERROR 無法拋出原錯誤代碼,THROW 可以 - THROW 前後都要 ; - @@error 為舊寫法,寫新的時不要再用 ```sql= declare @err int insert #t values(1),(1) set @err=@@error if @err <> 0 begin print @err goto end begin ``` - ms sql server <-> windows events log <-> ms sql agent - 要強制進 windows events log 要下 ```sql= exec sp_altermessage 2627, 'WITH_LOG','true' ``` - 可用以下語法查是否有 WITH_LOG ```sql= select * from sys.messages where message_id = 2627 ``` - SQL Server 要先有執行計畫再執行 cost-based,若建立執行計畫失敗,則放棄執行 ## 07 Indexes ```sql= create table t(c1 int identity, c2 int, c3 varchar(100)) insert t(c2) select c2 from t go 20 create index idx on t(c1) select object_id('t') ``` - 查 index 結構狀態 ```sql= select * from sys.dm_db_index_physical_stats(db_id(),index的ID,null,null,null) ``` - Clustered:table 有序擺放 > 書 - Heaps:無序擺放 > 散亂的紙 - Selectivity: 要不要用 - Fragmentation: 只對硬碟讀取有影響,但實際上大都是讀記憶體 - 大量資料中找少資料會用 seek,找多資料會用 scan,若用同一 SP ,會有用到快取的執行計畫的情況,導致不必要的 scan - Indexing Computed Columns: 例如 Branch,如果每次查的時候都不塞空白去查,可以建一個計算欄位 AS LTRIM(Branch) 然後對這個欄位建立索引 - Heaps: 沒有順序,儲存在第一個可用空間,如此分頁有任何空間,直接塞入,空間不足會開新分頁,可用在:log table ```sql= insert t values(1),(10),(2),(9),(3) delete t where c1 = 2 insert t values(15) select * from t --輸出值 : (1),(10),(15),(9),(3) ``` - 一旦建立過 Clustered Index,table 中的值就會被排序,即使移除了 Index ,先前的排序仍然存在 - Clustered 的情況下,若要在塞滿了的分頁中再塞,因為要放在該放的位置,會切成兩個新分頁 - PK(唯一,不能為null):盡量不要用複合欄位,不要存在意義,有意義就會因為意義變更而需要變更,ex: 身分證字號(若不知道對方身分證就不能建資料了) - 預設 pk 就是 clustered index - Update A row lock,若另 update B row,但走的是 scan 仍會因為 A row 的 lock 而被 lock;若是 seek 就不會被 lock - Clustering key 若沒有 unique ,sql server 會另外用 unique 欄位形成 unique - 只要有 where 就是查詢,不管是用在 CRUD 哪個裡 - 越 unique、Selectivity 的放越前面 - 看 index 相關的資料分布統計 ```sql= dbcc show_statistics('PaymentRequest', 'INX_PaymentRequest_NextSequence') ``` ![](https://i.imgur.com/VCRMx79.png) - DW 通常排程更新統計 - 更新統計時,執行計畫會被棄用 ## 08 Optimized Index Strategies - Filtered Index: 依情況可避免增加 CUD 的時間 - [DMO](https://onedrive.live.com/?authkey=%21AM58hx2vOseWkhM&cid=BF14192BD27975CB&id=BF14192BD27975CB%21958185&parId=BF14192BD27975CB%21127478&o=OneUp)