【问题标题】:Why do the return results of IIF and ISNULL not match my expectation when using date values?为什么使用日期值时,IIF 和 ISNULL 的返回结果不符合我的预期?
【发布时间】:2019-05-03 14:30:30
【问题描述】:

我正在尝试使用 IIF() 将列的强制转换作为日期返回,但仅当值不是空白字符串时 [这些值未在我正在使用的数据库中存储为空值] .

我尝试使用 ISNULL 作为更好的中介,但由于日期返回为 1900-01-01,它无法正常工作。它似乎适用于其他数据类型(int)。

示例表

CREATE TABLE #temp_test (
[reg_dt] varchar(10)
)
insert into #temp_test SELECT ''
insert into #temp_test SELECT ''
insert into #temp_test SELECT ''
insert into #temp_test SELECT ''
INSERT INTO #temp_test select '20080509'
select * from #temp_test

IIF 测试示例(似乎功能正常,我还测试了 inverse ='', 'false', 'true');当我取消注释 try_convert 并删除“true”时,遇到错误

SELECT IIF([reg_dt] != '', 'true'
       --TRY_CONVERT(date, [reg_dt], 112)
       , 'false') [reg_dt]
FROM   [#temp_test];

ISNULL 示例(返回 1900-01-01)

SELECT ISNULL(TRY_CONVERT( DATE, [reg_dt], 112), '') [reg_dt] from [#temp_test]

如果现有值为空白,我希望输出为空白值,如果不是,则返回日期值。 我认为在检查空值之前转换为日期可能是最好的,因为如果传递了一个无效的日期(例如 2019 年 4 月 31 日),那么它将被转换为一个空白值,这是在这种情况下的首选功能。

下面的代码确实有效,但我真的不确定这是否是最好的方法。

select ISNULL(NULLIF(CONVERT(VARCHAR(10), CAST([reg_dt] as date), 112), '19000101'), '') as [reg_dt] from #temp_test

【问题讨论】:

  • 不存在作为日期的空白字符串。当您将空字符串传递给日期时间时,它将执行隐式转换并获得 1900-01-01。如果你真的想要空字符串,你将不得不使用字符数据,而不是日期。实际上,您应该对日期使用正确的数据类型而不是 varchar。
  • 嗨,肖恩 - 正在传递的数据都是 varchar,我正在尝试成功转换它,这就是我遇到这个问题的方式。我在下面评论(并且应该编辑我的主要内容)选择 ISNULL(NULLIF(CONVERT(VARCHAR(10), CAST([reg_dt] as date), 112), '19000101'), '') as [reg_dt] from #temp_test 有效,但我不明白这是否真的是最好的方法。因为正如您所确认的那样,行为正在将 '' 更改为 1900-01-01,所以我想这就是我在这种情况下必须检测到的?如果我从头开始设计,我永远不会接受所有 varchar 数据。

标签: sql sql-server


【解决方案1】:

NULL 不等于空字符串。空字符串转换为有效日期“1900-01-01”。如果你想有 NULL,你需要指定 NULL。

顺便说一句,与 NULL 的比较不会返回 true 或 false。它将返回未知。

这是一个基于你的例子。

CREATE TABLE #temp_test (
[reg_dt] varchar(10)
)
insert into #temp_test SELECT ''
insert into #temp_test SELECT NULL
INSERT INTO #temp_test select '20080509'

SELECT IIF([reg_dt] != '', 'true', 'false') [reg_dt]
FROM   [#temp_test];

SELECT [reg_dt],
    TRY_CONVERT( DATE, [reg_dt], 112) [converted_reg_dt],
    ISNULL(TRY_CONVERT( DATE, [reg_dt], 112), '') [formatted_reg_dt] 
from [#temp_test];

【讨论】:

  • 嗨 Luis,我知道 NULL != '' 但这正是我表达的问题。在表上,我从空白字符串中获取这些值是嵌入的 - 没有空值。到目前为止,我能够完成这项工作的唯一方法是使用类似 ``` select ISNULL(NULLIF(CONVERT(VARCHAR(10), CAST([reg_dt] as date), 112), '19000101'), '') 作为来自 #temp_test ``` 的 [reg_dt]
【解决方案2】:

如果现有值是 空白,如果不是,则返回一个日期值。

结果集中的每一列都必须有一个数据类型。 DATEDATETIME 数据类型不能保存空字符串,只能保存有效日期或 NULL。如果要查看空字符串,则必须使用字符串数据类型,例如 VARCHAR

查看这些示例:

DECLARE @Varchar VARCHAR(100) = ''

SELECT
    EmptyVarchar = @Varchar,
    EmptyVarcharAsDate = CONVERT(DATE, @Varchar),
    CaseExpression = CASE WHEN @Varchar = '' THEN '' ELSE CONVERT(DATE, @Varchar) END

结果:

EmptyVarchar    EmptyVarcharAsDate  CaseExpression
                1900-01-01          1900-01-01

如果我们检查 CASE 实际返回的数据类型...

DECLARE @Varchar VARCHAR(100) = ''

SELECT
    ResultType = SQL_VARIANT_PROPERTY(
        CASE WHEN @Varchar = '' THEN '' ELSE CONVERT(DATE, @Varchar) END, 
        'BaseType')

结果:

ResultType
date

您会看到 CASEIIF 的工作方式类似)正在返回日期类型,因此空字符串隐式转换为默认日期 1900-01-01

处理日期并在无法转换时将它们作为空字符串返回的方法如下:

DECLARE @VarcharDates TABLE (VarcharDate VARCHAR(100))

INSERT INTO @VarcharDates (VarcharDate)
VALUES
    (''),
    (NULL),
    ('NULL'),
    ('15'),
    ('2019'),
    ('2019-05'),
    ('31/01/2000'),
    ('1800-01-01'),
    ('20190503'),
    ('20190503111111')

SELECT
    V.VarcharDate,
    DateAsVarchar = CASE 
        WHEN V.VarcharDate = '' THEN ''
        ELSE ISNULL(
            CONVERT(VARCHAR(100), TRY_CONVERT(DATE, V.VarcharDate, 112)),
            '') END
FROM
    @VarcharDates AS V

结果:

VarcharDate     DateAsVarchar

NULL    
NULL    
15  
2019            2019-01-01
2019-05 
31/01/2000  
1800-01-01  
20190503        2019-05-03
20190503111111  

【讨论】:

  • 您好,Ezlo,感谢您的回复。这是有道理的,但它让我感到困惑 - IIF 被 SQL 引擎转换为 case 语句,这是我的理解,那么为什么你的代码可以工作而不是我的?当我使用以下 SQL 在 SQL 中运行选择时,它似乎可以工作,但是数据并没有被解释为最终结果的日期。 ``` DECLARE @TestTable TABLE ( [reg_dt] varchar(12) ) 插入@TestTable (reg_dt) 值 (''), (' '), ('20001111'), (NULL) 选择 ISNULL(NULLIF(CONVERT( VARCHAR(10), CAST([reg_dt] as date), 112), '19000101'), '') as [reg_dt] from @TestTable ```
  • 找出导致错误的原因,我认为 - 例如:DECLARE @TestTable TABLE ( [reg_dt] varchar(12) ) insert into @TestTable (reg_dt) values (''), (' '), ('20001111'), (NULL), ('20190431') select ISNULL(NULLIF(CONVERT(VARCHAR(10), CAST([reg_dt] as date), 112), '19000101'), '') as [reg_dt] from @TestTable; 由于日期格式无效,这会导致转换失败。所以它必须也需要一个 try-convert 语句,我在之前的迭代中尝试过,你推荐过。我认为这应该足以让我走到最后。
【解决方案3】:

SQL 不能为单个列返回多个数据类型,因此它必须将所有值转换为 DATE。如果我理解正确,您似乎想检查日期是否有效,然后返回 varchar(空白) 那么这个怎么样:

SELECT CASE reg_dt WHEN '' THEN '' ELSE CAST(TRY_CONVERT(date, [reg_dt], 112) AS varchar(20)) END AS YourDate FROM [#temp_test]

【讨论】:

  • 这是有道理的,但如果是这样的话,为什么我的 sn-p 工作 - 它是返回一个 varchar 吗?我什至将其简化为: ISNULL(NULLIF(CONVERT(VARCHAR(10),[CB38E588-DBA4-428F-B1B5-D30E2C661187], 112), '19000101'), '') as [purchase_dt] 作为成功线。
  • 我认为是的; BOL: NULLIF 返回与第一个表达式相同的类型。
  • ISNULL(NULLIF(CONVERT(VARCHAR(10), cast([6BCFDECC-01C6-4647-88DF-F13766F0B8F4] 作为日期), 112), '19000101'), '') 作为 [first_seen_vin ] 要试一试。
猜你喜欢
  • 2017-10-31
  • 1970-01-01
  • 2021-03-16
  • 1970-01-01
  • 2023-03-17
  • 2011-01-29
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多