【问题标题】:Using complex SQL queries in conditional if/else statements在条件 if/else 语句中使用复杂的 SQL 查询
【发布时间】:2016-10-06 12:53:17
【问题描述】:

我正在尝试设置一些 SQL 测试查询,以在 SQL 表中记录测试是通过还是失败。这些测试查询在几个单独的数据库上运行。下面是一个使用基本查询的示例(在 IF 语句内):

DECLARE @dbname NVARCHAR(200);
DECLARE @query NVARCHAR(MAX);

DECLARE db_cursor CURSOR FOR
SELECT name FROM sys.databases
WHERE name LIKE '%JMPTIPR%'

OPEN db_cursor
FETCH NEXT FROM db_cursor INTO @dbname

WHILE @@FETCH_STATUS = 0

BEGIN

SET @query = CAST('
DECLARE @testStatus NVARCHAR(MAX);
DECLARE @name NVARCHAR(MAX);
DECLARE @version NCHAR(10);
DECLARE @testid INT;
DECLARE @repid INT;

SELECT @name = Column1 from [' +@dbname+ '].dbo.Table1
where Column1 = ''File Name''
SELECT @version = attributedata from [' +@dbname+ '].dbo.Table1 
where Column1 = ''Version Number''
SELECT @testid = COALESCE(MAX(TestNum), 0) FROM [Database1].dbo.Table2;
SELECT @repid = RepNum FROM [Database1].dbo.Table2 
WHERE Date = (SELECT MAX(Date) FROM [Database1].dbo.Table2)

IF (
    SELECT COUNT(*)
    FROM [' +@dbname+ '].dbo.Table1
    WHERE [' +@dbname+ '].dbo.Table1.Column1 LIKE ''%execution%''
) IS NOT NULL
    SET @testStatus = ''Test Passed''
ELSE
    SET @testStatus = ''Test Failed''

INSERT INTO [Database1].dbo.Table3 (FileName, Version, Result, Date, TestNum, RepNum)
VALUES (@name, @version, @testStatus, GETDATE(), @testid, @repid)'
AS NVARCHAR(MAX))

EXECUTE (@query)

FETCH NEXT FROM db_cursor INTO @dbname

END
CLOSE db_cursor
DEALLOCATE db_cursor;

问题是我有几个更复杂的查询,我想做同样的事情。如果一切都按计划进行,我有一个预期的结果应该返回,如果出现问题,我有一个预期的结果。因此,这是我想要包含的更复杂查询之一的示例: (值得注意的是,我不希望任何人通过“修复”查询来简单地回答这个问题。我有太多东西要放在这里让别人修复,我宁愿自己学习如何去做。)

SELECT Table1.Column1, Table2.Column2, Table3.Column3,
  Table3.Column4, Table3.Column5, Table3.Column6,
  Table3.Column7
FROM Table2 INNER JOIN
  Table3 ON Table3.Column8 = Table2.id INNER JOIN
  Table1 ON Table2.Column9 = Table1.id
WHERE (Table3.Column5 = -1 AND Table3.Column7 > 0) OR
  (Table3.Column5 = -1 AND Table3.Column6 > 0) OR
  (Table3.Column5 > Table3.Column6 AND Table3.Column6 > 0) OR
  (Table3.Column6 > Table3.Column7 AND Table3.Column7 > 0)

当我将此查询添加到我的 if 语句中(并将 if 条件更改为 IS NOT NULL)时,我收到错误消息:

Msg 116, Level 16, State 1, Line 24
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.
Msg 116, Level 16, State 1, Line 24
Only one expression can be specified in the select list when the subquery is not introduced with EXISTS.

对我来说,最终的问题似乎是我对 SQL 的理解不够深入,无法完成这些工作。

编辑:我看到几个 cmets 说因为我没有显示不起作用的代码,所以您无法修复它。我假设您的意思是我提供的示例代码不够具体。

但是,这没有抓住我的问题的重点。我不希望你修复那个特定的代码。我的问题是关于原始 If/Else 代码无法处理某些更复杂的 SQL 查询(比如带回完整表的查询)。

也许为我提供的示例代码提供更多上下文,它是一个查询,它要么不返回任何内容(“测试通过”条件),要么返回一个包含多行和多列的表( “测试失败”条件)。我在询问如何获取适用于简单查询的原始查询并将其调整为无法简单“计数”或其他简单功能的查询。

【问题讨论】:

  • 就像错误所说的那样,在只允许一个字段的上下文中,您有一个返回多个字段的子查询。例如select ... where foo = (select field1, field2 from ....)foo 应该与什么值进行比较? field1?或field2?因此错误 - 您需要消除歧义
  • 我理解您的观点,但如果您不向我们提供 SQL 语句的代码,我们将无法修复 SQL 语句。很高兴您给了我们错误代码...但是请给我们实际的错误 SQL。
  • 另外,您可能只需要了解EXISTS() 的工作原理
  • 警告:不要这样做:'[' + @dbname + ']',这样做:+ QUOTENAME(DB_NAME(DB_ID(@dbname))) + 为什么? (1) QUOTENAME() 使用 [] 字符正确转义垃圾,并且 (2) 包装 DB_NAME()/DB_ID() 确保传递的数据库名称实际上是有效的。
  • @Paparazzi 所以?试试CREATE DATABASE "foo]bar"; - 如果没有QUOTENAME(),它仍然会中断。而且,如果有人从该代码中学习或将其复制到其他地方,通常可以认为使用'[' + @dbname + ']' 是可以的,包括提供@dbname 作为用户输入的情况,并且存在不那么明显的危险与此相关。

标签: sql-server if-statement exists


【解决方案1】:

由于 Marc 似乎对跟进不感兴趣,这里是您问题的答案。

查询的“复杂性”与问题无关。您收到错误的原因有一个,而且您也可以从一个非常简单的查询中得到同样的错误。

此查询有效(如您所述):

IF (
    SELECT COUNT(*)
    FROM [' +@dbname+ '].dbo.Table1
    WHERE [' +@dbname+ '].dbo.Table1.Column1 LIKE ''%execution%''
) IS NOT NULL
...

此查询有效的原因是 IF() 中的子查询返回单个值。伯爵。一排一列。可以测试那个值是否为NULL,所以没有问题。

此查询不起作用:

IF (
    SELECT Column1, Column2
    FROM [' +@dbname+ '].dbo.Table1
    WHERE [' +@dbname+ '].dbo.Table1.Column1 LIKE ''%execution%''
) IS NOT NULL
...

它不起作用的唯一原因是 IF() 中的子查询返回多于一列,因此该查询的结果不能与 NULL 进行比较。也许在您的脑海中,您认为可以通过某种方式完成(任一列为 NULL,两列为 NULL,等等),但我向您保证它不能。 SQL 引擎不知道如何将结果集(多列)与 NULL 进行比较。这不是 SQL Server 的工作方式。它只能将单个标量值与 NULL 进行比较。

这就是你得到错误的原因:

选择列表中只能指定一个表达式,当 EXISTS 没有引入子查询。

您的“复杂”查询在选择列表中有多个列(表达式)。这是唯一的原因。无论您的查询多么复杂,如果它只返回一列,您可以在 IF() 语句中使用它,没有问题。

查看您的编辑,如果您只需要测试您的复杂查询是否返回行,那么您可以像在有效的查询中那样将列列表替换为 COUNT(*)。

【讨论】:

    【解决方案2】:

    我无法谈论您没有向我们展示的代码,但我可以指出一种更好的方法来处理您展示的代码。也许它会解决您将其应用于神秘代码时遇到的问题:

    这个

    IF (
        SELECT COUNT(*)
        FROM [' +@dbname+ '].dbo.Table1
        WHERE [' +@dbname+ '].dbo.Table1.Column1 LIKE ''%execution%'' 
    ) IS NOT NULL
      SET @testStatus = ''Test Passed''
    ELSE
     SET @testStatus = ''Test Failed''
    

    一样
    IF EXISTS (
        SELECT 1
        FROM [' +@dbname+ '].dbo.Table1
        WHERE [' +@dbname+ '].dbo.Table1.Column1 LIKE ''%execution%'')
      SET @testStatus = ''Test Passed''
    ELSE
      SET @testStatus = ''Test Failed''
    

    【讨论】:

    • 嗯,不一样。 count(*) of zero 仍然不为空。但可能是 OP 的意图。
    • @Paparazzi 你可以让 count(*) 返回 0 吗?
    • 那么如果计数为零,您认为会返回什么?
    • @Paparazzi nothing... where 将过滤所有行并且不返回任何结果。
    • 确实如此,如果我要求计算年龄
    猜你喜欢
    • 2021-03-28
    • 1970-01-01
    • 2015-05-04
    • 2012-07-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-11
    相关资源
    最近更新 更多