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

- 同一錯誤訊息的各國語言版本查詢
```sql=
select * from sys.messages where message_id = 2627
```

```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

```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')
```

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