2014-02-28 112 views
2

我創建了一個非常簡單的示例,顯示我在使用動態SQL的更復雜的查詢時遇到的問題,並且我無法弄清楚爲什麼CTE在標準版本不導致錯誤。任何想法或建議都會非常有幫助。CTE導致Converison失敗錯誤

使用CTE時出錯: 從字符串轉換日期和/或時間時轉換失敗。

我想實現我的動態查詢如下:不符合特定數據類型(日期在這種情況下) 標誌的記錄。如果他們通過了這一步驟,他們稍後會對照查找表進行檢查,以驗證他們是我期望的值(驗證指定範圍內的日期)。

-- Table Setup 
CREATE TABLE #ValidDate 
(
    V INT IDENTITY (1, 1) NOT NULL, 
    VDate DATE NULL 
) 

INSERT INTO #ValidDate 
VALUES ('02/20/2014'), ('02/21/2014'), ('02/22/2014'), ('02/23/2014'), ('02/25/2014') 

CREATE TABLE #DatesToValidate 
(
    I INT IDENTITY (1, 1) NOT NULL, 
    IDate VARCHAR(30) NULL 
) 

INSERT INTO #DatesToValidate 
VALUES ('apple'), ('02/21/2014'), ('orange'), ('02/23/2014') 

CREATE TABLE #Errors 
(
    ID INT, 
    Dates VARCHAR(30) 
) 

INSERT INTO #Errors 
SELECT * 
FROM #DatesToValidate 
WHERE ISDATE(IDate) <> 1 

下面是標準的查詢(工程):

SELECT * 
FROM #DatesToValidate 
WHERE I NOT IN ( SELECT ID 
        FROM #Errors) 
AND IDate NOT IN ( SELECT VDate 
        FROM #ValidDate) 

下面是CTE(不工作):

;WITH CTE AS 
(
    SELECT * 
    FROM #DatesToValidate 
    WHERE I NOT IN ( SELECT ID 
         FROM #Errors) 
) 

SELECT * 
FROM CTE 
WHERE IDate NOT IN ( SELECT VDate 
         FROM #ValidDate) 
+0

以供將來參考MySQL是比SQL-Server中的完全不同DBMS和他們不應該被標記相同。 – Zane

+0

你能提供更多關於「不工作」的細節嗎? –

回答

3

ThisSQL Fiddle說明問題了。這兩個版本都不能保證工作。如果您交換and中的條件順序,則會導致轉換失敗。

原因and版本發生在這種情況下工作是因爲短路。當第一個條件失敗時,第二個條件不會運行。這是一個優化,SQL Server 可以利用。而且在這種情況下,所以你認爲它的工作原理。

CTE版本不起作用,因爲不能保證操作的順序。而SQL Server決定做一些不同的事情。即,它試圖評估'apple'作爲日期。

兩者的正確解決方案是在比較之前施放。因爲cast可能不起作用,所以您需要小心。您應該在SQL Server中使用case來達到此目的;它是保證順序評估的唯一結構。

所以,試試這個:

;WITH CTE AS 
(
    SELECT * 
    FROM DatesToValidate 
    WHERE I NOT IN ( SELECT ID 
         FROM Errors) 
) 
SELECT * 
FROM CTE 
WHERE (case when isdate(IDate) = 1 then cast(IDate as date) 
     end) NOT IN ( SELECT VDate 
         FROM ValidDate) 
1

這個特定部分失敗原因VDatedate type列和IDatevarchar。所以你需要投VDatecast(VDate as varchar(30)如下所示。

看到這裏http://sqlfiddle.com/#!3/abfc0/7

WHERE IDate NOT IN (SELECT VDate FROM ValidDate) <--- Failing 

演示小提琴試試你的SQL這樣

;WITH CTE AS 
(
    SELECT * 
    FROM DatesToValidate 
    WHERE I NOT IN ( SELECT ID 
         FROM Errors) 
) 

SELECT * 
FROM CTE 
WHERE IDate NOT IN (SELECT cast(VDate as varchar(30)) <--- Cast Here 
         FROM ValidDate)