【问题标题】:Why does SQL Server skip over an IF statement?为什么 SQL Server 会跳过 IF 语句?
【发布时间】:2018-01-05 21:42:04
【问题描述】:

尝试运行以下查询:

IF LEFT(CAST(SERVERPROPERTY('ProductVersion') as varchar),2) LIKE '1[2-9]'
SELECT
    @@servername AS [Server]
    , d.name AS [Database]
    , CONVERT(char(10), d.create_date, 121) AS [Created]
    , sp.name AS [Owner]
    , d.recovery_model_desc AS [Recovery]
    , CASE d.state_desc                 WHEN 'OFFLINE' THEN '***OFFLINE***' ELSE d.state_desc END AS [Status]
    , d.user_access_desc AS [Access]
    , CASE d.is_read_only               WHEN 0 THEN 'READ_WRITE' WHEN 1 THEN 'READ_ONLY' END AS Updateability
    , CASE d.is_fulltext_enabled        WHEN 0 THEN '' WHEN 1 THEN 'YES' END AS [FullText]
    , CASE d.is_auto_create_stats_on    WHEN 0 THEN '' WHEN 1 THEN 'YES' END AS [CreateStats]
    , CASE d.is_auto_update_stats_on    WHEN 0 THEN '' WHEN 1 THEN 'YES' END AS [UpdateStats]
    , CASE d.page_verify_option
        WHEN 0 THEN '***NONE***'
        WHEN 1 THEN '***TORN PAGE DETECTION***' -- outdated in 2005+. Change to checksum.
        ELSE d.page_verify_option_desc
    END AS [Page Verify]
    , d.compatibility_level AS [Level]
    , d.target_recovery_time_in_seconds as ckp_s
    , d.log_reuse_wait_desc AS [Log Wait]
    , d.collation_name AS [Collation]
    , CASE d.is_read_committed_snapshot_on  WHEN 0 THEN '' WHEN 1 THEN 'YES' END AS [RCS]
    , CASE d.snapshot_isolation_state   WHEN 0 THEN '' WHEN 1 THEN 'YES' END AS [SI]
    , CASE d.is_query_store_on WHEN 0 THEN 'NO' WHEN 1 THEN 'YES' END AS QS
    , CASE d.is_auto_close_on           WHEN 0 THEN '' WHEN 1 THEN '***YES***' /*always disable auto close*/ END AS [AutoClose]
    , CASE d.is_auto_shrink_on          WHEN 0 THEN '' WHEN 1 THEN '***YES***' /*always disable auto shrink*/ END AS [AutoShrink]
    , d.delayed_durability_desc as [Durability]
FROM sys.databases AS d
LEFT JOIN sys.server_principals AS sp /*get database owner name */ ON sp.sid = d.owner_sid
WHERE d.database_id > 4 -- exclude system DBs
ORDER BY d.name; 
ELSE
SELECT
    @@servername AS [Server]    
    , d.name AS [Database]
    , CONVERT(char(10), d.create_date, 121) AS [Created]
    , sp.name AS [Owner]
    , d.recovery_model_desc AS [Recovery]
    , CASE d.state_desc                 WHEN 'OFFLINE' THEN '***OFFLINE***' ELSE d.state_desc END AS [Status]
    , d.user_access_desc AS [Access]
    , CASE d.is_read_only               WHEN 0 THEN 'READ_WRITE' WHEN 1 THEN 'READ_ONLY' END AS Updateability
    , CASE d.is_fulltext_enabled        WHEN 0 THEN '' WHEN 1 THEN 'YES' END AS [FullText]
    , CASE d.is_auto_create_stats_on    WHEN 0 THEN '' WHEN 1 THEN 'YES' END AS [CreateStats]
    , CASE d.is_auto_update_stats_on    WHEN 0 THEN '' WHEN 1 THEN 'YES' END AS [UpdateStats]
    , CASE d.page_verify_option
        WHEN 0 THEN '***NONE***'
        WHEN 1 THEN '***TORN PAGE DETECTION***' -- outdated in 2005+. Change to checksum.
        ELSE d.page_verify_option_desc
    END AS [Page Verify]
    , d.compatibility_level AS [Level]
    , 'N/A' AS ckp_s
    , d.log_reuse_wait_desc AS [Log Wait]
    , d.collation_name AS [Collation]
    , CASE d.is_read_committed_snapshot_on  WHEN 0 THEN '' WHEN 1 THEN 'YES' END AS [RCS]
    , CASE d.snapshot_isolation_state   WHEN 0 THEN '' WHEN 1 THEN 'YES' END AS [SI]
    , 'N/A' AS QS
    , CASE d.is_auto_close_on           WHEN 0 THEN '' WHEN 1 THEN '***YES***' /*always disable auto close*/ END AS [AutoClose]
    , CASE d.is_auto_shrink_on          WHEN 0 THEN '' WHEN 1 THEN '***YES***' /*always disable auto shrink*/ END AS [AutoShrink]
    , 'N/A' AS [Durability]
FROM sys.databases AS d
LEFT JOIN sys.server_principals AS sp /*get database owner name */ ON sp.sid = d.owner_sid
WHERE d.database_id > 4 -- exclude system DBs
ORDER BY d.name;

问题在于 SQL Server 2005/2008/2012。 sys.databases 表中不存在列 target_recovery_time_in_seconds, is_query_store_on, and delayed_durability_desc

顶部的 IF 语句查找它是什么版本的 SQL Server。如果是 2014+,请使用这 3 列运行查询。如果没有,请在没有这 3 列的情况下运行查询。我在其他查询中使用了这个逻辑并且它有效,但在这种情况下它没有。我收到以下错误:

消息 207,第 16 级,状态 1,第 20 行
列名“target_recovery_time_in_seconds”无效。

消息 207,第 16 级,状态 1,第 25 行
列名“is_query_store_on”无效。

消息 207,第 16 级,状态 1,第 25 行
列名“is_query_store_on”无效。

消息 207,第 16 层,状态 1,第 28 行
列名“delayed_durability_desc”无效。`

我的问题是为什么 SQL Server 不读取 IF 语句?如果是这样,它将跳过包含 3 列的查询并运行没有它的查询。

【问题讨论】:

  • 它不会跳过IF 语句......它只会在执行时验证它;但在执行之前,SQL Server 会验证您的整个查询。

标签: sql sql-server


【解决方案1】:

SQL Server 将尝试编译批处理中的所有语句(除非它们引用了不存在的整个对象 - 编译将被延迟。引用现有对象的缺失列不会导致延迟编译。)。

虽然有一个trick you can use,但只有一个查询可以在您定位的所有版本中工作并避免使用动态 SQL。

如果下面标有<-- 的列存在,则将从sys.databases 解析,否则将退回到dummy 派生表。

WITH d
     AS (SELECT x.*
         FROM   (SELECT  'N/A', 'N/A', 'N/A')
                AS dummy (is_query_store_on, delayed_durability_desc, target_recovery_time_in_seconds)
                CROSS APPLY (SELECT d.collation_name,
                                    d.compatibility_level,
                                    d.create_date,
                                    d.database_id,
                                    delayed_durability_desc, /* <-- resolved from d or dummy */
                                    d.is_auto_close_on,
                                    d.is_auto_create_stats_on,
                                    d.is_auto_shrink_on,
                                    d.is_auto_update_stats_on,
                                    d.is_fulltext_enabled,
                                    is_query_store_on, /* <-- resolved from d or dummy */
                                    d.is_read_committed_snapshot_on,
                                    d.is_read_only,
                                    d.log_reuse_wait_desc,
                                    d.name,
                                    d.owner_sid,
                                    d.page_verify_option,
                                    d.page_verify_option_desc,
                                    d.recovery_model_desc,
                                    d.snapshot_isolation_state,
                                    d.state_desc,
                                    target_recovery_time_in_seconds, /* <-- resolved from d or dummy */
                                    d.user_access_desc
                             FROM   sys.databases AS d) AS x)
SELECT
    @@servername AS [Server]
    , d.name AS [Database]
    , CONVERT(char(10), d.create_date, 121) AS [Created]
    , sp.name AS [Owner]
    , d.recovery_model_desc AS [Recovery]
    , CASE d.state_desc                 WHEN 'OFFLINE' THEN '***OFFLINE***' ELSE d.state_desc END AS [Status]
    , d.user_access_desc AS [Access]
    , CASE d.is_read_only               WHEN 0 THEN 'READ_WRITE' WHEN 1 THEN 'READ_ONLY' END AS Updateability
    , CASE d.is_fulltext_enabled        WHEN 0 THEN '' WHEN 1 THEN 'YES' END AS [FullText]
    , CASE d.is_auto_create_stats_on    WHEN 0 THEN '' WHEN 1 THEN 'YES' END AS [CreateStats]
    , CASE d.is_auto_update_stats_on    WHEN 0 THEN '' WHEN 1 THEN 'YES' END AS [UpdateStats]
    , CASE d.page_verify_option
        WHEN 0 THEN '***NONE***'
        WHEN 1 THEN '***TORN PAGE DETECTION***' -- outdated in 2005+. Change to checksum.
        ELSE d.page_verify_option_desc
    END AS [Page Verify]
    , d.compatibility_level AS [Level]
    , d.target_recovery_time_in_seconds as ckp_s
    , d.log_reuse_wait_desc AS [Log Wait]
    , d.collation_name AS [Collation]
    , CASE d.is_read_committed_snapshot_on  WHEN 0 THEN '' WHEN 1 THEN 'YES' END AS [RCS]
    , CASE d.snapshot_isolation_state   WHEN 0 THEN '' WHEN 1 THEN 'YES' END AS [SI]
    , CASE d.is_query_store_on WHEN 'false' THEN 'NO' WHEN 'true' THEN 'YES' ELSE 'N/A' END AS QS
    , CASE d.is_auto_close_on           WHEN 0 THEN '' WHEN 1 THEN '***YES***' /*always disable auto close*/ END AS [AutoClose]
    , CASE d.is_auto_shrink_on          WHEN 0 THEN '' WHEN 1 THEN '***YES***' /*always disable auto shrink*/ END AS [AutoShrink]
    , d.delayed_durability_desc as [Durability]
FROM d
LEFT JOIN sys.server_principals AS sp /*get database owner name */ ON sp.sid = d.owner_sid
WHERE d.database_id > 4 -- exclude system DBs
ORDER BY d.name;

【讨论】:

  • 我总是尽可能避免使用动态 sql。这里有很好的提示。
  • @MartinSmith,效果很好!感谢您的课程和提示!
【解决方案2】:

当一个 SQL 查询被执行时,整个事情首先被评估。因此,当您在 SQL2012 或更早版本上运行它时,这些字段不存在,因此您的查询无效,因为这些字段不存在,即使该部分不会被执行。

您可以尝试使用一些动态 sql 来解决这个问题 - 将整个命令构建成一个包含您需要的各种元素的字符串,然后使用 sp_executesql 执行它

例如:

 declare @sql nvarchar(4000)

 select @sql = 'SELECT @@servername AS [Server], d.name AS [Database] '
 -- some fields omitted here for brevity

 IF LEFT(CAST(SERVERPROPERTY('ProductVersion') as varchar),2) LIKE '1[2-9]'
 begin
     select @sql = @sql + ', CASE d.is_query_store_on WHEN 0 THEN ''NO'' WHEN 1 THEN ''YES'' END AS QS '
 end
 else
 begin
    select @sql = @sql + ', ''N/A'' as QS '
 end 
 select @sql = @sql + ' FROM sys.databases AS d LEFT JOIN sys.server_principals AS sp /*get database owner name */ ON sp.sid = d.owner_sid WHERE d.database_id > 4 -- exclude system DBs ORDER BY d.name; '

 exec sp_executesql @sql

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-08-25
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-06-04
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多