【问题标题】:SQL Server : calculating ages in stored procedure returns conversion failedSQL Server:计算存储过程中的年龄返回转换失败
【发布时间】:2017-12-14 01:18:16
【问题描述】:

我编写了一个存储过程,用于根据表列中的值计算年龄。

这就是那个存储过程:

WITH ages AS
(
    SELECT 
        DATEDIFF(YEAR, CONVERT(datetime, pf.Value, 104), pf.CreatedOn) END as Age
    FROM 
        Fields pf
    WHERE
        pf.Value IS NOT NULL
)
SELECT  
    (SELECT COUNT(Age) 
     FROM ages 
     WHERE ages.Age > 17 AND ages.Age < 25) AS '18-24',
    (SELECT COUNT(Age) 
     FROM ages 
     WHERE ages.Age > 24 AND ages.Age < 31) AS 25-30',
    (SELECT COUNT(Age) 
     FROM ages 
     WHERE ages.Age > 30 AND ages.Age < 36) AS '31-35',
    (SELECT COUNT(Age) 
     FROM ages 
     WHERE ages.Age > 35) AS '> 35'

在此列(值)中,生日存储为字符串值,格式如下:02.10.1987 00:00:00 (DD.MM.YYY HH:mm:ss)。

在这个 Fields 表中,我有 3500 行。

数据如下所示:

02.10.1987 00:00:00
29.07.1967 12:33:11
02.10.1987 00:00:00
15.11.1959 00:00:00
07.11.1975 00:00:00

当我这样运行程序时,SQL 服务器返回以下错误:

从字符串转换日期和/或时间时转换失败

程序正常执行,如果我把Top 100000放在存储过程中,像这样:

WITH ages AS
(
SELECT TOP 100000 datediff(year, CONVERT(datetime, pf.Value, 104), pf.CreatedOn)
       END as Age
FROM Fields pf
where pf.Value is not null
)
SELECT  (select count(Age) FROM ages where ages.Age>17 and ages.Age<25) as '18-24',
        (select count(Age) FROM ages where ages.Age>24 and ages.Age<31)  as '25-30',
        (select count(Age) FROM ages where ages.Age>30 and ages.Age<36)  as '31-35',        
        (select count(Age) FROM ages where ages.Age>35)  as '>35'

正如我所写,我只有 3500 行。

我所有的数据格式都正确,存储过程得到的都是非空值。

有人知道为什么会这样吗?为什么它可以与插入的 Top 一起使用,但没有它却不能?

SQL Server 所做的与内存分配有关吗?

【问题讨论】:

    标签: sql-server stored-procedures


    【解决方案1】:

    这些都是远射,但不妨试试:

    • 重建表上的所有索引
    • 运行 EXEC sp_recompile N'Fields';

    TOP 10000 和直接 SELECT 完全有可能产生不同的执行计划,但我仍然不明白为什么这会影响数据。

    你确定没有其他事情发生吗?查询的两个连接都使用同一个用户?两个连接的区域设置是否相同?

    【讨论】:

      【解决方案2】:

      尝试将 CONVERT(datetime, pf.Value, 104) 更改为 Cast(pf.Value as DateTime) 或 Cast(pf.Value as Date) 如果您真的不关心日期的时间戳部分。

      另外,如果 pf.CreatedOn 是 varchar 类型,对它做同样的事情。

          WITH ages AS
      (
          SELECT 
              DATEDIFF(YEAR, cast(pf.Value AS DateTime), CAST(pf.CreatedOn AS DATETIME)) END as Age
          FROM 
              Fields pf
          WHERE
              pf.Value IS NOT NULL
      )
      

      啊对不起...我没有意识到你的约会是在“你不是来自这里吗?”格式。试试这个看起来不太吸引人的解决方案。

      --TestData
       DECLARE @x NVARCHAR(MAX) = '29.11.1956 00:00:00'
       DECLARE @x NVARCHAR(MAX) = '2.1.1956 00:00:00'
      
      --Solution
       select    
       -- Monthpart dd.mm.yyy
       SUBSTRING(@x, 
          CHARINDEX('.', @x) + 1 ,
              CHARINDEX('.',@x,CHARINDEX('.', @x) + 1 )-  CHARINDEX('.', @x)  -1) + '/' 
       --DayPart dd.mm.yyy
        + SUBSTRING(@x, 1, CHARINDEX('.', @X) - 1)  + '/'
       --YearPart   
        + RIGHT(LEFT (@x, CHARINDEX(' ',@x) -1),4)
      

      【讨论】:

      • 当我实施您的建议时,我收到以下错误:将 nvarchar 数据类型转换为 datetime 数据类型导致值超出范围。它发生在数据库中的值“29.11.1956 00:00:00”上。这是表中的第 20 条记录。所以对于前 19 名来说,它是有效的,但对所有人来说,不是 :(
      • 抱歉,我认为日期的格式类似于 mmddyyyy。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2021-11-19
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-12-12
      • 2017-05-26
      • 2010-11-23
      相关资源
      最近更新 更多