2014-02-28 95 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)