SQL Server如何在未指定时确定转换样式?

时间:2010-09-03 13:41:52

标签: sql-server tsql sql-server-2008

更新:这是一个错误,但won't get fixed until the next release of SQL Server due to backward compatibility concerns.

这是我追回的this question之后的事,但仍然感到困惑。

TOP (1)添加到查询中足以将结果从“2010年9月3日”更改为“2010-09-03”(至少在我的英国设置机器上)可以解释为什么?这是一个错误还是在某处记录?

注意:我在下面还发现,如果我使用了#temp表,那么两个查询都会返回2010-09-03

USE tempdb

BEGIN TRAN

CREATE TABLE t (d DATETIME NOT NULL)
INSERT INTO t VALUES (GETDATE())

SELECT (CONVERT(VARCHAR(50),CONVERT(DATE, d))) + CONVERT(VARCHAR(50), '')
FROM t
/*
Returns "Sep  3 2010"

[Expr1004] = Scalar Operator(CONVERT(varchar(50),
                                     CONVERT(date,[tempdb].[dbo].[t].[d],0),
                                     0)+
                             CONVERT(varchar(50),[@1],0))
*/

SELECT TOP 1 (CONVERT(VARCHAR(50),CONVERT(DATE, d))) + CONVERT(VARCHAR(50), '')
FROM t
/*
[Expr1004] = Scalar Operator(CONVERT(varchar(50),
                                     CONVERT(date,[tempdb].[dbo].[t].[d],0),
                                     121)+
                             '')
Returns "2010-09-03"
*/

ROLLBACK

4 个答案:

答案 0 :(得分:6)

似乎自动参数化是造成不一致的原因。

联机丛书documents DATE,TIME,DATETIME2和DATETIMEOFFSET默认使用CONVERT样式121,而样式0用于DATETIME和SMALLDATETIME。有人忘了更新新类型的自动参数化规则:)

如果查询可以自动参数化,则如果进行隐式转换或没有指定样式的显式转换,则样式0将错误地应用于新的DATE / TIME类型。没有TOP的查询是自动参数化的(参数[@ 1]出现而不是TIME文字)。 TOP是阻止自动参数化的(许多)查询功能之一。

明显的解决方法是在使用CONVERT时始终指定所需的样式。

答案 1 :(得分:1)

怪异。我复制了yoru代码,删除了评论,并删除了日期的添加内容,并且它们都很好。

    BEGIN TRAN 

CREATE TABLE t (d DATETIME NOT NULL) 
INSERT INTO t VALUES (GETDATE()) 

SELECT (CONVERT(VARCHAR(50),CONVERT(DATE, d)))
FROM t 


SELECT TOP 1 (CONVERT(VARCHAR(50),CONVERT(DATE, d)))
FROM t 

ROLLBACK 

答案 2 :(得分:1)

我可以重现,SQL 2008 R2 x64。

TOP (n)DISTINCTGROUP BY和窗口函数都会生成YYYY-MM-DD日期。 ORDER BY没有。

因此,我猜它在tempdb,内部与本地化表示以及应该发生的来回自动切换等方面都有事情要做,但在某种程度上不是这种情况。

--------------------------------  
-- these return Sep  4 2010
--------------------------------  
SELECT (CONVERT(VARCHAR(50),CONVERT(DATE, d))) + CONVERT(VARCHAR(50), '') FROM t
SELECT (CONVERT(VARCHAR(50),CONVERT(DATE, d))) + CONVERT(VARCHAR(50), '') FROM t ORDER BY 1 ASC
SELECT (CONVERT(VARCHAR(50),CONVERT(DATE, d))) + CONVERT(VARCHAR(50), '') FROM t ORDER BY 1 DESC

--------------------------------  
-- these return 2010-09-04
--------------------------------  
-- GROUP BY
SELECT c FROM (SELECT (CONVERT(VARCHAR(50),CONVERT(DATE, d))) + CONVERT(VARCHAR(50), '') FROM t) t (c) GROUP BY c
-- DISTINCT 
SELECT DISTINCT (CONVERT(VARCHAR(50),CONVERT(DATE, d))) + CONVERT(VARCHAR(50), '') FROM t
-- TOP (n)
SELECT TOP (5) (CONVERT(VARCHAR(50),CONVERT(DATE, d))) + CONVERT(VARCHAR(50), '') FROM t
-- COUNT(*) OVER ()
SELECT COUNT(*) OVER (), (CONVERT(VARCHAR(50),CONVERT(DATE, d))) + CONVERT(VARCHAR(50), '') FROM t
-- ROW_NUMBER() OVER ()
SELECT ROW_NUMBER() OVER (ORDER BY (SELECT 1)), (CONVERT(VARCHAR(50),CONVERT(DATE, d))) + CONVERT(VARCHAR(50), '') FROM t

答案 3 :(得分:1)

这看起来像一个bug;我可以在2008 x64开发人员上重现。

我确实发现了一些很奇怪的东西;向varchar强制转换为查询添加另一个日期(或日期时间)会导致格式规范化。所以:

USE tempdb

BEGIN TRAN

declare @d date = getdate()

CREATE TABLE t (d DATETIME NOT NULL)
INSERT INTO t VALUES (GETDATE())

SELECT (CONVERT(VARCHAR(50),CONVERT(DATE, d))) + CONVERT(VARCHAR(50), '')
,(CONVERT(VARCHAR(50),@d))
FROM t


SELECT TOP 1 (CONVERT(VARCHAR(50),CONVERT(DATE, d))) + CONVERT(VARCHAR(50), '')
,(CONVERT(VARCHAR(50),@d))
FROM t

ROLLBACK

(注意将转换后的变量@d添加到两个查询中) 生产:

2010-09-06  2010-09-06
2010-09-06  2010-09-06

作为输出。

格式121似乎是DATE的默认隐藏格式,而0是DATETIME的默认隐藏格式。我想知道问题是由第一个查询忽略最内层的CONVERT还是不按顺序应用?引起的?