首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 數據庫知識 >  >> SQL Server 資料庫 >> 關於SqlServer

實戰 SQL 語句大全:從基礎查詢到進階視圖與預存程式——持續更新的開發者隨身筆記

     編輯:關於SqlServer

前言:為什麼要有這份筆記?

資料庫開發中,語法細節易忘、效能寫法易混、特殊場景易卡關。這份筆記記錄實戰驗證過的 SQL 片段,拒絕學院派空談,只收錄「能直接複製跑、能解決問題、能優化效能」的硬核內容。持續更新,直到不再需要新增為止。

一、核心查詢語句模式庫

1.1 基礎單表查詢

-- 精確查詢
SELECT col1, col2 FROM table_name WHERE col1 = 'value';

-- 模糊查詢(前綴索引友善)
SELECT * FROM users WHERE username LIKE 'john%';

-- 分頁查詢 (SQL Server 2012+ 標準語法)
SELECT * FROM orders 
ORDER BY order_date DESC 
OFFSET 20 ROWS FETCH NEXT 10 ROWS ONLY;

1.2 多表連接查詢完全圖解

連接是關聯資料庫的靈魂。以下以 Orders (訂單) 與 Customers (客戶) 為例:

連接類型語法關鍵字結果集特徵適用場景
內連接INNER JOIN僅返回雙邊均匹配的行主從表關聯查詢(訂單+客戶資料)
左外連接LEFT [OUTER] JOIN左表全保留,右表無匹配則 NULL所有客戶及其訂單(含無訂單客戶)
右外連接RIGHT [OUTER] JOIN右表全保留,左表無匹配則 NULL所有訂單及其客戶(含無客戶訂單)
全外連接FULL [OUTER] JOIN雙表全保留,無匹配處填 NULL雙向完整性對帳
交叉連接CROSS JOIN笛卡爾積(無 ON 條件)生成測試資料、排程矩陣

內連接三細分

  • 等值連接:ON a.id = b.id(最常用)
  • 自然連接:同名欄位自動匹配(不建議生產使用,隱含風險)
  • 不等值連接:ON a.price > b.budget(範圍匹配、分級統計)

大物件欄位 (LOB) 連接限制與變通

鐵律:text、ntext、image 型別不可直接出現在 ON 條件。

-- 錯誤示範
SELECT * FROM docs d1 INNER JOIN docs d2 ON d1.content = d2.content; -- 報錯

-- 正確變通:比較長度、校驗和、或轉型
SELECT d1.id, d2.id 
FROM docs d1 INNER JOIN docs d2 
ON DATALENGTH(d1.content) = DATALENGTH(d2.content);

1.3 WHERE 條件構建藝術

運算符類別運算符範例效能提示
比較= <> > >= < <=age > 18配合索引最佳
範圍BETWEEN ... ANDage BETWEEN 18 AND 65等價於 >= AND <=,含邊界
列表IN / NOT INstatus IN (1,2,3)列表過長 (>1000) 改用暫存表 JOIN
模式LIKE / NOT LIKEname LIKE 'A%'前綴 % 可用索引,前導 % 導致掃描
空值IS NULL / IS NOT NULLphone IS NULL索引通常不含 NULL,需過濾索引
邏輯AND / OR / NOTage>18 AND city='NY'AND 優先級高於 OR,善用括號

LIKE 通配符進階用法

-- 結尾匹配
WHERE filename LIKE '%.pdf'

-- 單字元佔位
WHERE code LIKE 'AB_'  -- 匹配 AB0, AB1, ABX...

-- 字元集合
WHERE grade LIKE '[ABC]%'  -- 以 A/B/C 開頭
WHERE grade LIKE '[^A]%'   -- 不以 A 開頭

-- 範圍
WHERE serial LIKE '[0-9][0-9][0-9]'  -- 三位數字

-- 中文雙位元組特殊處理
WHERE cname LIKE '%中文%'  -- 直接寫入
-- 部分舊版驅動需雙百分號
WHERE cname LIKE '%%中文%%'

二、資料異動語句 (DML) 標準範本

2.1 INSERT 多樣化寫法

-- 單列顯式指定欄位(推薦,防 schema 變更失效)
INSERT INTO publishers (pub_id, pub_name, city, state)
VALUES ('9001', 'Acme Publishing', 'New York', 'NY');

-- 多列單次插入 (SQL Server 2008+)
INSERT INTO publishers (pub_id, pub_name, city, state)
VALUES 
('9002', 'Beta Books', 'Chicago', 'IL'),
('9003', 'Gamma Press', 'Boston', 'MA');

-- 從查詢結果插入 (跨表複製/歸檔)
INSERT INTO pub_archive (pub_id, pub_name, city, state, archived_at)
SELECT pub_id, pub_name, city, state, GETDATE()
FROM publishers
WHERE state IN ('CA', 'TX');

2.2 UPDATE 安全模式

-- 永遠先跑 SELECT 確認影響範圍
SELECT * FROM products WHERE category_id = 5 AND price < 100;

-- 確認無誤後執行 UPDATE
UPDATE products 
SET price = price * 1.1, 
    updated_at = GETDATE(),
    updated_by = SYSTEM_USER
WHERE category_id = 5 AND price < 100;

-- 多表 UPDATE (SQL Server 語法)
UPDATE p
SET p.stock = p.stock - o.qty
FROM products p
INNER JOIN order_details o ON p.product_id = o.product_id
WHERE o.order_id = 12345;

2.3 DELETE 謹慎守則

-- 軟刪除優於硬刪除
UPDATE orders SET is_deleted = 1, deleted_at = GETDATE() WHERE order_id = 999;

-- 必須硬刪時:先 SELECT 驗證,再包在交易中
BEGIN TRAN;
DELETE FROM order_details WHERE order_id = 999;
DELETE FROM orders WHERE order_id = 999;
COMMIT TRAN;

-- 子查詢刪除(刪除某教室下的所有套餐關聯)
DELETE FROM suite_to_classroom
WHERE classroom_sect_id IN (
    SELECT classroom_sect_id FROM classroom_sections 
    WHERE classroom_id = '112'
);

三、視圖設計最佳實踐

3.1 典型業務視圖範例:社群活躍度聚合

CREATE VIEW vw_GroupActivitySummary AS
SELECT 
    g.group_id,
    g.group_name,
    gt.main_type,
    gt.group_type_name,
    g.group_icon_url,
    -- 會員數權重 50
    ISNULL((SELECT COUNT(*) FROM group_users gu WHERE gu.group_id = g.group_id), 0) * 50 +
    -- 基金數權重 1
    ISNULL(g.fund_count, 0) +
    -- 文章數權重 5
    ISNULL((SELECT COUNT(*) FROM topics t WHERE t.board_id = g.subforum_id), 0) * 5 +
    -- 相簿數權重 10
    ISNULL((SELECT COUNT(*) FROM group_photos gp WHERE gp.group_id = g.group_id), 0) * 10 +
    -- 主題數權重 2
    ISNULL(g.topic_account, 0) * 2 +
    -- 點擊數權重 1
    ISNULL(g.hit_account, 0) AS group_active_degree,
    -- 明細欄位
    ISNULL((SELECT COUNT(*) FROM group_users gu WHERE gu.group_id = g.group_id), 0) AS group_member_num,
    ISNULL(g.fund_count, 0) AS fund_count,
    (SELECT COUNT(*) FROM topics t WHERE t.board_id = g.subforum_id) AS group_article_num,
    (SELECT COUNT(*) FROM group_photos gp WHERE gp.group_id = g.group_id) AS group_photo_num,
    g.topic_account,
    g.hit_account,
    (SELECT user_name FROM users u WHERE u.user_id = g.creator_id) AS group_creator_name,
    g.create_time
FROM group_info g
INNER JOIN group_type gt ON g.group_type_id = gt.group_type_id;

3.2 視圖效能優化三原則

  1. 避免巢狀視圖:視圖引用視圖會導致查詢計畫膨脹,優化器難以展開
  2. 慎用標量子查詢:上例中多個 (SELECT COUNT...) 若頻繁查詢大量群組,應改用 APPLY 或預先彙總表
  3. 加入 SCHEMABINDING:防止底層表結構變更導致視圖失效,並允許建立索引視圖

四、預存程序專業開發範本

4.1 呼叫語法標準化

-- 命名參數呼叫(推薦,順序無關、可讀性高)
EXEC usp_DeleteMiniClassroom @miniClassroom_id = 123;

-- 位置參數呼叫(僅簡單測試用)
EXEC usp_DeleteMiniClassroom 123;

4.2 企業級預存程序完整範本:刪除小課堂及所有關聯資料

CREATE PROCEDURE usp_DeleteMiniClassroom
    @miniClassroom_id INT
AS
BEGIN
    SET NOCOUNT ON;           -- 抑制「影響 n 行」訊息,降低網路負載
    SET XACT_ABORT ON;        -- 任何運行時錯誤自動回滾整個交易

    -- 參數驗證
    IF @miniClassroom_id IS NULL
    BEGIN
        RAISERROR('miniClassroom_id 不可為 NULL', 16, 1);
        RETURN -1;
    END

    BEGIN TRY
        BEGIN TRANSACTION;

        -- 1. 刪除套餐關聯
        DELETE FROM suite_to_classroom
        WHERE classroom_sect_id IN (
            SELECT classroom_sect_id FROM classroom_sections 
            WHERE classroom_id = @miniClassroom_id
        );

        -- 2. 刪除課堂段落
        DELETE FROM classroom_sections 
        WHERE classroom_id = @miniClassroom_id;

        -- 3. 刪除用戶購買記錄
        DELETE FROM user_buy_classroom 
        WHERE classroom_id = @miniClassroom_id;

        -- 4. 刪除年級學科關聯
        DELETE FROM classroom_grade_subject 
        WHERE classroom_id = @miniClassroom_id;

        -- 5. 刪除課堂討論
        DELETE FROM classroom_discuss 
        WHERE classroom_id = @miniClassroom_id;

        -- 6. 刪除課程討論 (透過課程表關聯)
        DELETE FROM course_discuss
        WHERE course_id IN (
            SELECT course_id FROM course_info 
            WHERE classroom_id = @miniClassroom_id
        );

        -- 7. 刪除用戶課程收藏
        DELETE FROM user_course_favorite
        WHERE course_id IN (
            SELECT course_id FROM course_info 
            WHERE classroom_id = @miniClassroom_id
        );

        COMMIT TRANSACTION;
        RETURN 0;  -- 成功
    END TRY
    BEGIN CATCH
        IF @@TRANCOUNT > 0 ROLLBACK TRANSACTION;
        DECLARE @err_msg NVARCHAR(4000) = ERROR_MESSAGE();
        RAISERROR('刪除小課堂失敗: %s', 16, 1, @err_msg);
        RETURN 99;  -- 失敗
    END CATCH
END;

4.3 預存程序開發鐵律

  1. 永遠 SET NOCOUNT ON:除非客戶端明確需要 DONE_IN_PROC 訊息
  2. 永遠 SET XACT_ABORT ON:配合 TRY/CATCH 確保交易原子性
  3. 參數必驗證:NULL、範圍、權限、存在性
  4. 業務邏輯封裝:單一職責,避免超級大預存程序
  5. 錯誤資訊標準化:RAISERROR 統一格式,便於應用層捕獲
  6. 避免游標:集合思維,集合操作;不得已用 FAST_FORWARD 唯讀順向游標
  7. 動態 SQL 最小化:必須用時採 sp_executesql 參數化,防注入、利用計畫快取

五、持續更新區(最新收錄)

  • 2024-01-15:新增 STRING_AGG 字串聚合與 JSON_VALUE 解析範例 (SQL 2017+)
  • 2024-02-20:補充 MERGE UPSERT 模式與 OUTPUT 子句捕獲異動資料
  • 2024-03-10:記錄 QUERY_STORE 強制計畫實戰案例

這份筆記將伴隨專案成長持續演進。歡迎在團隊 Wiki 同步維護,讓知識在實戰中沉澱、在分享中增值。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved