简体   繁体   English

在 SQL where 子句中使用带有 IsDate 的 case 语句

[英]Using case statements with IsDate in a SQL where clause

I am trying to clean up the where clause statement in the following code:我正在尝试清理以下代码中的 where 子句语句:

SELECT
    CONVERT(datetime, [UTC_Time_Stamp], 127) AS TimeStamp
FROM 
    Table 
WHERE 
    CASE 
       WHEN ISDATE([UTC_Time_Stamp]) = 1 
       THEN CONVERT(datetime, [UTC_Time_Stamp], 127) 
       ELSE CAST('1/1/1900' AS datetime) 
    END > CAST('11/09/2012' AS datetime) 
    AND 
       CASE 
          WHEN ISDATE([UTC_Time_Stamp]) = 1 
          THEN CONVERT(datetime, [UTC_Time_Stamp], 127) 
          ELSE CAST('1/1/3000' AS datetime) 
       END < CAST('11/10/2012' as datetime) 
ORDER BY 
    TimeStamp;

UTC_Time_Stamp is stored as a string and is sometimes null. UTC_Time_Stamp存储为字符串,有时为空。 I was previously running into a conversion error inside my where clause.我以前在 where 子句中遇到转换错误。 I fixed the error following advice from this question here , but I feel like there has to be a simpler way to achieve the same result.我根据this question here的建议修复了错误,但我觉得必须有一种更简单的方法来实现相同的结果。

You need to do the conversion within a case statement.需要case语句中进行转换。 SQL is a descriptive language and does not guarantee the order of processing. SQL 是一种描述性语言,不保证处理的顺序。 So, a where clause does not necessarily happen before other processing, even when it is in a subquery or a CTE.因此, where子句不一定发生在其他处理之前,即使它在子查询或 CTE 中也是如此。 However, SQL does guarantee the order of processing in a case statement (that contains no expressions with aggregation functions).但是,SQL 确实保证了case语句(不包含带有聚合函数的表达式)中的处理顺序。

You can simplify your statement by using a subquery:您可以使用子查询来简化您的语句:

select TimeStamp
FROM (select t.*,
             Case When ISDATE([UTC_Time_Stamp]) = 1 Then CONVERT(datetime, UTC_Time_Stamp, 127) end) as TimeStamp
      from Table t
     ) t
WHERE coalesce(TimeStamp, cast('1/1/1900' as datetime)) > cast('11/09/2012' as datetime) and
      coalesce(TimeStamp, cast('1/1/3000' as datetime)) < cast('11/10/2012' as datetime) 
ORDER BY TimeStamp;

This does the conversion to TimeStamp, for valid values.对于有效值,这会转换为时间戳。 You can then use the variable in the outer query.然后,您可以在外部查询中使用该变量。

I would also encourage you to get used to the ANSI standard format for dates (YYYYMMDD or YYYY-MM-DD) instead of ambiguous formats like '11/10/2012'.我还鼓励您习惯日期的 ANSI 标准格式(YYYYMMDD 或 YYYY-MM-DD),而不是像“11/10/2012”这样的模棱两可的格式。 It may be clear to you that this means 2012-11-10 .您可能很清楚这意味着 2012-11-10 。 . . . . or is that 2012-10-11 .或者是 2012-10-11 。 . . . . but the format is ambiguous.但格式不明确。

I like CTEs for this, or you could also do temp tables, table variables or an inline derived table like @Derek.我喜欢 CTE,或者你也可以做临时表、表变量或像 @Derek 这样的内联派生表。

Basically, we're going to grab the proper datatype first, and then have a much easier time creating our query:基本上,我们将首先获取正确的数据类型,然后更容易地创建我们的查询:

;with CTE as (
    -- Bring back the column as datetime
    select case when isdate(UTC_Time_Stamp) = 1 then cast(UTC_Time_Stamp as datetime) end as UTC_Time_Stamp
    from [Table]
)
-- Simple select with the proper datatype
select convert(varchar(50), UTC_Time_Stamp, 127) as [TimeStamp]
from CTE
-- May still need gt and lt functionality
where UTC_Time_Stamp between cast('11/09/2012' as datetime) and cast('11/10/2012' as datetime)

It seems like you're using some arbitrarily small and large values for TimeStamp when you have non-dates, which is probably unnecessary given your comparison, so I've removed them.当您有非日期时,似乎您对 TimeStamp 使用了一些任意大小的值,鉴于您的比较,这可能是不必要的,因此我已将其删除。

Note that I am using the datetime datatype in the CTE and for the comparison, only converting it to a string for presentation.请注意,我在 CTE 中使用 datetime 数据类型并进行比较,仅将其转换为字符串以进行演示。

Also note that between is inclusive, so you might need to go back to your separate > and < where clause.另请注意, between是包含性的,因此您可能需要返回单独的>< where 子句。

It's easier to do something like this (using whatever convert/between clause in the predicate that you see fit):做这样的事情更容易(在谓词中使用任何你认为合适的 convert/between 子句):

SELECT CONVERT(datetime, [UTC_Time_Stamp], 127) as TimeStamp
FROM (
  select [UTC_Time_Stamp] 
  from Table 
  WHERE ISDATE([UTC_Time_Stamp]) = 1 
) a
WHERE
  convert(datetime, [UTC_Time_Stamp], 127) between '11/9/2012' and '11/10/2012'
ORDER BY TimeStamp;

In this time for me this solves the problem of working with temporary table alias or sub queries that can slow down the selection of millions of records.这一次对我来说,这解决了使用临时表别名或子查询的问题,这些问题可能会减慢数百万条记录的选择速度。

select your_column 
from your_table 
where case when ISDATE(your_column) = 1 
           then Cast(your_column as date) 
      end Between '01/01/2016' and '01/01/2017' 
order by your_column

Regards问候

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM