---
# System prepended metadata

title: 2021-01-18 SQL Training Day 2

---

# 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)