全域變數、變數與參數
兩種以小老鼠開頭的清單:@@ 之後的 T-SQL 全域變數,以及 @ 之後、由這份指令碼 自己宣告的變數與參數。兩者都牽涉到「小老鼠算不算識別字的一部分」這個共用判斷。 清單本身怎麼排名與觸發見 completion.md。
全域變數
打出 @@ 就列出 T-SQL 的 32 個全域變數,右側寫著它是什麼:
SELECT @@ → @@ROWCOUNT(上一個敘述影響的資料列數)、@@VERSION…
IF @@ERR → @@ERROR
SET @rows = @@ROWC → @@ROWCOUNT這一份與內建函式一樣只能手寫:它們在文法上是變數不是關鍵字,ScriptDom 的 token 列舉裡沒有它們。@@REMSERVER 刻意不收——它回報的遠端伺服器功能整個被拿掉了, 打出來也得不到有意義的值。
只在 @@ 之後出現
全域變數不混進一般清單。它與下一節的變數是僅有的兩個由「正在輸入的詞元」而不是由 前導關鍵字決定的目標:@@ 開頭的名稱在 T-SQL 裡只有這一種意思,前面是哪一個子句 都不改變這件事。反過來把 32 個 @@ 塞進每一次按鍵的候選清單,只會讓真正要找的東西 更難找。
也因此這個位置不再判斷關鍵字位置:使用者已經打出 @@ 了,此時再判一次,判對沒有 好處(清單本來就只剩這一類),判錯的代價是清單整個空掉。同樣的理由,它與 USE 之後的資料庫一樣跳過「輸入幾個字元之後才建議」——那兩個小老鼠已經把話說完了。
小老鼠算識別字的一部分
詞元起點必須落在第一個小老鼠上。切在 ROW 的話適用範圍只蓋住 ROW, 提交 @@ROWCOUNT 之後編輯器裡留下的是 @@@@ROWCOUNT。
分辨兩份清單的依據就是前綴本身,不是周圍的文法:兩個小老鼠開頭是系統的封閉清單, 一個是使用者自己寫的名字,見下。
變數與參數
打出 @ 就列出這份指令碼在游標之前寫過的變數與參數,說明欄是宣告時寫的型別:
DECLARE @readerId INT
SELECT @| → @readerId(INT)
EXEC dbo.usp_Renew @| → 同上資料表變數在純量位置提交時寫成 [@rows],那裡它只能當欄位的限定字, 見插入文字。
這與 CTE、暫存資料表是同一條推理:名稱只存在於這份指令碼裡,中繼資料一個都看不到, 而使用者會去補字正是因為那個名稱是他剛取的、還沒背起來。因此也與那一份一樣, 不對資料庫送出任何查詢,與「列出資料庫物件」的設定無關。
收的是每一個單小老鼠詞元,不分辨它出現在宣告還是使用的位置——理由同樣與暫存資料表 一致:DECLARE、程序參數、函式參數各認一次的話,漏掉的那一種寫法就會安靜地少一個 名稱,而多收的那些本來就是使用者自己打過的字。只有兩個限制:結束於游標之後的不收 (打到一半的 @rea 自己會出現在清單裡,而選它等於什麼都沒做),兩個小老鼠開頭的 不收(那是上面那份封閉清單)。
宣告的位置仍然不開清單
分辨的是「他在宣告」與「他在引用」:
| 位置 | 開不開 |
|---|---|
DECLARE @、DECLARE @a INT = NULL, @ | 不開,他正在取名字 |
CREATE PROCEDURE p @、CREATE PROC p @a int OUTPUT, @、CREATE FUNCTION f (@ | 同上 |
SET @、SELECT @、WHERE a = @、EXEC p @ | 開,他要的是上面宣告過的名稱 |
SET 打完空白時選項清單已經開著,變數放在那一份裡,見片語。
判斷方式是從那個小老鼠往回走,走出這一句之前碰到 DECLARE、PROCEDURE、PROC、 FUNCTION 就是宣告,先碰到能開始一句的關鍵字(SET、SELECT、EXEC…)就是引用。 其餘的關鍵字屬於前一項的定義:@a int = NULL、@a int OUTPUT、@a AS int、 DECLARE @t TABLE (…) 之後的逗號仍在宣告清單裡。以前停在第一個關鍵字,程序只有第一個 參數認得出來,第二個參數之後的型別位置也跟著不見。途中的括號整組跳過,分號代表前一個 敘述已經結束。
走到頭都沒有關鍵字時當成引用。這裡的 fail-open 換來的是「多列幾個他自己打過的名字」, 而反過來猜錯的代價是他打的名字被清單換掉。
EXEC 的引數清單裡連參數一起列
EXEC dbo.usp_Renew @| → @readerId(int)、@days(int)…(那個程序的參數)
再接他自己宣告過的變數參數與變數在這個位置都對——EXEC p @readerId = 1 是具名傳值,EXEC p @myVar 是照 順序傳一個變數——所以兩份併在一起,參數排前面:他打出小老鼠通常是為了具名傳值, 而那個名字是被呼叫端定的、比較不容易記得。這也是唯一一份會同時出現兩種類別的清單。
提交參數時連 = 一起寫進去,理由與內建函式補左括號相同。
「他在呼叫誰」由 Core 回答,參數清單由中繼資料層換:從小老鼠往回跳過已經打好的 引數,落點必須剛好是 EXEC/EXECUTE。這比「往回找最近的 EXEC」嚴格,而嚴格 正是重點——中間只要夾著任何一個別的關鍵字,那就是另一個敘述:
EXEC dbo.usp_Renew
SELECT * FROM dbo.Loan WHERE ReaderId = @| → 不是那個 EXEC 的引數,只列變數三段式的 LibraryDb.dbo.usp_Renew 取後兩段:中繼資料只看得到目前連線的資料庫。 EXEC ('SELECT 1') 與 EXEC @procName 讀不出名稱,直接放棄。