T-SQL 查詢撰寫規範:參數化查詢、索引友善寫法、效能陷阱迴避與可讀性格式要求。適用於 SQL Server 與 Oracle 雙資料庫。
當使用者要求撰寫、檢視或最佳化 SQL 查詢時,請自動套用以下規範。規範以 SQL Server(T-SQL) 為主,Oracle 專用語法另見末節。
sp_executesql 搭配參數,禁止 EXEC('SELECT ...' + @input)。-- ✅ 正確
EXEC sp_executesql
N'SELECT * FROM Products WHERE CategoryId = @CategoryId',
N'@CategoryId INT',
@CategoryId = @inputCategoryId;
-- ❌ 錯誤
EXEC('SELECT * FROM Products WHERE CategoryId = ' + @inputCategoryId);
SELECT, FROM, WHERE, JOIN, ON, GROUP BY, ORDER BY, INSERT, , 等 SQL 關鍵字一律大寫。UPDATEDELETESELECT, FROM, WHERE, JOIN, GROUP BY, ORDER BY, HAVING 各佔一行。SELECT 時,逗號置於行首以利增刪欄位時的 diff 乾淨度。SELECT
o.OrderId
,o.OrderDate
,c.CustomerName
,SUM(d.Quantity * d.UnitPrice) AS TotalAmount
FROM Orders AS o
INNER JOIN Customers AS c
ON o.CustomerId = c.CustomerId
INNER JOIN OrderDetails AS d
ON o.OrderId = d.OrderId
WHERE o.OrderDate >= @StartDate
AND o.Status = @Status
GROUP BY
o.OrderId
,o.OrderDate
,c.CustomerName
HAVING SUM(d.Quantity * d.UnitPrice) > @MinAmount
ORDER BY o.OrderDate DESC;
AS 關鍵字指定別名(如 Orders AS o),不使用隱式別名(Orders o)。Orders AS o、CustomerAddresses AS ca)。-- ❌ 索引失效
WHERE YEAR(OrderDate) = 2024
WHERE CONVERT(VARCHAR, OrderDate, 112) = '20240101'
WHERE ISNULL(Status, 0) = 1
-- ✅ 索引友善
WHERE OrderDate >= '2024-01-01' AND OrderDate < '2025-01-01'
WHERE Status = 1
NVARCHAR 欄位使用 N'...' 前綴)。LIKE 的前綴萬用字元(LIKE '%keyword')無法使用索引,應盡量避免。LIKE '%...%'。SELECT *:必須明確列出所需欄位,減少 I/O 開銷並避免結構變更時的隱性問題。EXISTS (SELECT 1 FROM ...) 中的子查詢允許使用 SELECT 1。OFFSET ... FETCH NEXT 語法(SQL Server 2012+),不使用舊式 ROW_NUMBER() 子查詢包裝。SELECT
ProductId
,ProductName
FROM Products
ORDER BY ProductId
OFFSET @PageSize * (@PageNumber - 1) ROWS
FETCH NEXT @PageSize ROWS ONLY;
IS NULL / IS NOT NULL,禁止 = NULL。NOT IN 與 NULL 值的陷阱:若子查詢可能回傳 NULL,改用 NOT EXISTS。-- ❌ 當子查詢含 NULL 時,NOT IN 會回傳空結果
SELECT * FROM Products
WHERE CategoryId NOT IN (SELECT CategoryId FROM ExcludedCategories);
-- ✅ NOT EXISTS 不受 NULL 影響
SELECT p.*
FROM Products AS p
WHERE NOT EXISTS (
SELECT 1
FROM ExcludedCategories AS e
WHERE e.CategoryId = p.CategoryId
);
WITH (NOLOCK) 或 READ UNCOMMITTED 隔離等級(僅限可容忍 Dirty Read 的報表查詢)。UPDLOCK 或適當的隔離等級防止競爭條件。WITH) 或暫存資料表提升可讀性與效能。INSERT, UPDATE, DELETE 大量資料列時,應分批處理(如每批 5,000~10,000 筆),避免長時間鎖定與交易日誌暴增。UNION ALL,避免不必要的排序開銷。以下規則僅適用於 Oracle 資料庫,與上方 T-SQL 章節不重疊。
:paramName),禁止字串串接:-- ✅ 正確
EXECUTE IMMEDIATE 'SELECT * FROM Products WHERE CategoryId = :cid'
USING v_category_id;
-- ❌ 錯誤
EXECUTE IMMEDIATE 'SELECT * FROM Products WHERE CategoryId = ' || v_category_id;
OFFSET ... FETCH NEXT(與 T-SQL 語法相同):SELECT ProductId, ProductName
FROM Products
ORDER BY ProductId
OFFSET :offset ROWS
FETCH NEXT :page_size ROWS ONLY;
ROWNUM 雙層包裝:SELECT *
FROM (
SELECT inner_q.*, ROWNUM AS rn
FROM (
SELECT ProductId, ProductName
FROM Products
ORDER BY ProductId
) inner_q
WHERE ROWNUM <= :end_row
)
WHERE rn > :start_row;
IDENTITY 欄位,使用 SEQUENCE 搭配 NEXTVAL:CREATE SEQUENCE seq_products START WITH 1 INCREMENT BY 1 NOCACHE;
INSERT INTO Products (ProductId, ProductName)
VALUES (seq_products.NEXTVAL, :product_name);
GENERATED AS IDENTITY:ProductId NUMBER GENERATED BY DEFAULT AS IDENTITY
TO_DATE / TO_CHAR,必須明確指定格式遮罩:-- 字串轉日期
WHERE OrderDate >= TO_DATE(:date_str, 'YYYY-MM-DD')
-- 日期轉字串
SELECT TO_CHAR(OrderDate, 'YYYY-MM-DD HH24:MI:SS') AS FormattedDate
FROM Orders;
SELECT 中省略 FROM,計算純運算式時使用 DUAL:SELECT SYSDATE FROM DUAL;
SELECT seq_products.NEXTVAL FROM DUAL;
NVL(expr, default) 或 COALESCE(expr1, expr2, ...)(標準 SQL,優先使用 COALESCE)。ISNULL()(T-SQL 專屬)。|| 運算子串接字串,不支援 T-SQL 的 +:SELECT FirstName || ' ' || LastName AS FullName FROM Employees;
BEGIN ... END; 包裝:BEGIN
UPDATE Orders SET Status = :new_status
WHERE OrderId = :order_id;
COMMIT;
EXCEPTION
WHEN OTHERS THEN
ROLLBACK;
RAISE;
END;
Spring Boot中的JPA/Hibernate模式,用于实体设计、关系处理、查询优化、事务管理、审计、索引、分页和连接池。