進行查詢運算式上錯誤及警告的疑難排解
有時候,SQL Server 2005 評估查詢中運算式的速度比 SQL Server 2000 還快。此行為提供下列重要優點:
- 計算資料行上的索引可以與查詢中的運算式 (與計算資料行運算式相同) 進行比對。
- 防止運算式結果的重複計算。
不過,視查詢的本質和資料庫的資料而定,如果查詢包含現有的不安全運算式,SQL Server 2005 可能發生執行階段例外狀況。這些執行階段例外狀況包含下列各項:
- 算術例外狀況:除以零、溢位和反向溢位。
- 轉換失敗,例如缺少有效位數,及嘗試將非數字字串轉換成數字。
- 彙總一組不保證全都是非 Null 的值。
在 SQL Server 2000 中,這些例外狀況可能不會發生在使用特定資料的特定應用程式中。不過,因為變更統計資料而改變的查詢計劃可能會導致 SQL Server 2005 發生例外狀況。您可以透過將查詢修改為包含條件運算式 (如 NULLIF 或 CASE),以防止這些執行階段例外狀況。
重要事項: |
---|
您可以將搜尋條件、選取清單,或查詢內任何其他位置中出現的運算式加以細分並重新組織為一或多個獨立運算式。SQL Server 可以依照這些獨立運算式彼此間的任何相對順序來評估它們。在計算結果資料行之前,不一定要套用篩選作業,包括聯結。 |
在下列範例中,即使是最終未限定為查詢輸出的資料列,仍可隨時評估選取清單中的 x/y
運算式。
USE tempdb
GO
IF OBJECT_ID('T','U') IS NOT NULL
DROP TABLE T
IF OBJECT_ID('S','U') IS NOT NULL
DROP TABLE S
GO
CREATE TABLE T(x float, y float, z nvarchar(30))
CREATE TABLE S(a float, b float)
GO
INSERT INTO T VALUES (1, 0, 'unknown')
INSERT INTO T VALUES(1, 2, '10')
GO
INSERT INTO S VALUES (1, 1)
INSERT INTO S VALUES (1, 2)
INSERT INTO S VALUES (1, 3)
INSERT INTO S VALUES (1, 4)
INSERT INTO S VALUES (1, 5)
下列查詢在 SQL Server 2005 中會失敗,但可於 SQL Server 2000 中完成。
SELECT x/y FROM T INNER JOIN S ON x = a AND y > b
OPTION(HASH JOIN)
查詢失敗的原因是在對 y=0
評估 x/y
運算式時,該運算式會導致除數為零的錯誤。
下列程式碼是可以讓查詢正確執行的解決方案:
SELECT x/NULLIF(y,0) FROM T INNER JOIN S ON x = a AND y > b
OPTION(HASH JOIN)
如果 y = 0
,NULLIF(y,0)
運算式會傳回 NULL
。否則,運算式便會傳回 y
的值。運算式 x/NULL
則會產生 NULL
,而且不會發生任何例外狀況。
請考量下列涉及將字元資料轉換成數值類型的範例。
SELECT CONVERT(tinyint, z) FROM T INNER JOIN S ON x = a AND y > b
OPTION(HASH JOIN)
因為當查詢嘗試將字串 'unknown'
轉換為 tinyint
時發生轉換錯誤,所以查詢會失敗。解決這個問題的其中一種方法是藉由引進如下的 CASE
陳述式,將查詢修改成只在 z
是 numeric
時才執行轉換:
SELECT CASE WHEN ISNUMERIC(z) = 1
THEN CONVERT(tinyint, z)
ELSE 0
END
FROM T INNER JOIN S ON x = a AND y > b
OPTION(HASH JOIN)
第二個解決方法是放棄在資料庫中使用特殊字串值 'unknown'
,而改用 NULL
。第三個解決方法則是將 z 資料行的類型變更為 tinyint,徹底地避免轉換。因為這類解決方法需要個別變更資料及結構描述,所以採用這些解決方法可能牽涉到比修改查詢還多的工作。然而,如果這些解決方法也能讓其他查詢更容易撰寫,您可能會考慮使用它們。
彙總函數發出的 NULL 輸入警告
如果彙總函數 (如 MIN) 的輸入包含 NULL,則彙總函數會發出已刪除 Null 值的警告。這個警告可能會與計劃相依。如果不想讓 NULL 輸入要處理的彙總中,且不想發出警告,您可以在本機修改查詢,以刪除 Null 值。請考慮使用下列範例中的 SELECT
陳述式:
USE tempdb
GO
IF OBJECT_ID('newtitles','U') IS NOT NULL
....DROP TABLE newtitles
GO
CREATE TABLE dbo.newtitles
(title varchar (80) NULL ,
pubdate datetime NULL)
GO
INSERT dbo.newtitles VALUES('Title 1', NULL)
INSERT dbo.newtitles VALUES('Title 2', '20050311')
GO
SELECT t.title, t.pubdate, m.min_pubdate
FROM newtitles AS t,
(SELECT MIN(pubdate) AS min_pubdate
FROM newtitles) AS m
WHERE t.pubdate = m.min_pubdate
GO
在 SQL Server 2005 中,這項查詢會產生警告。若要防止警告,請加入條件 WHERE pubdate IS NOT NULL
來變更查詢,以便於彙總前篩選出 Null 值:
SELECT t.title, t.pubdate, m.min_pubdate
FROM newtitles AS t,
(SELECT MIN(pubdate) AS min_pubdate
FROM newtitles
WHERE pubdate IS NOT NULL) AS m
WHERE t.pubdate = m.min_pubdate
GO