【问题标题】:Dynamic SQL: CASE expression in HAVING clause for SSRS dataset query动态 SQL:用于 SSRS 数据集查询的 HAVING 子句中的 CASE 表达式
【发布时间】:2019-09-12 13:24:29
【问题描述】:

我的一个表包含 6 位标志:

tblDocumentFact.useCase1
tblDocumentFact.useCase2
tblDocumentFact.useCase3
tblDocumentFact.useCase4
tblDocumentFact.useCase5
tblDocumentFact.useCase6 

位标志用于限制通过HAVING子句返回的数据,例如:

HAVING tblDocumentFact.useCase4 = 1     /* '1' means 'True' */

这适用于静态查询。该查询针对 SQL Server Reporting Services 报表的数据集。我不想有 6 个报告,每个位标志一个,我希望有 1 个带有 @UserChoice 输入参数的报告。我正在尝试编写一个动态查询来根据 @UserChoice 参数构造 HAVING 子句。我认为当用户单击 1-of-6 选项按钮时,@UserChoice 可以设置为整数值(1、2、3、4、5 或 6)。我尝试通过如下所示的 CASE 表达式来执行此操作,但它不起作用——查询不返回任何行。这里的正确方法是什么?

HAVING  (
        (CASE WHEN @UserChoice =1 THEN 'dbo.tblDocumentFact.useCase1' END) = '1'
  OR    (CASE WHEN @UserChoice =2 THEN 'dbo.tblDocumentFact.useCase2' END) = '1' 
  OR    (CASE WHEN @UserChoice =3 THEN 'dbo.tblDocumentFact.useCase3' END) = '1' 
  OR    (CASE WHEN @UserChoice =4 THEN 'dbo.tblDocumentFact.useCase4' END) = '1' 
  OR    (CASE WHEN @UserChoice =5 THEN 'dbo.tblDocumentFact.useCase5' END) = '1' 
  OR    (CASE WHEN @UserChoice =6 THEN 'dbo.tblDocumentFact.useCase6' END) = '1' 
        )

【问题讨论】:

    标签: sql dynamic reporting-services case having


    【解决方案1】:

    你需要稍微改一下你的逻辑:

    HAVING
        (@UserChoice = 1 AND 'dbo.tblDocumentFact.useCase1' = '1') OR
        (@UserChoice = 2 AND 'dbo.tblDocumentFact.useCase2' = '2') OR
        (@UserChoice = 3 AND 'dbo.tblDocumentFact.useCase3' = '3') OR
        (@UserChoice = 4 AND 'dbo.tblDocumentFact.useCase4' = '4') OR
        (@UserChoice = 5 AND 'dbo.tblDocumentFact.useCase5' = '5') OR
        (@UserChoice = 6 AND 'dbo.tblDocumentFact.useCase6' = '6');
    

    CASE 表达式不能以您使用它的方式使用,因为 THENELSE 后面的内容必须是文字值,而不是逻辑条件。

    【讨论】:

    • 另一个不使用 ' 包围标识符的好理由,因为原始查询会发出那些文字字符串?另外,如果数据类型是位,我会提倡替换 '1' -> 1
    • @CaiusJard 我不知道 SSRS ...也许它需要它们或至少接受它们?是的,我总体上同意你的看法。
    • Tim/Caius:非常感谢你们。当我将 Tim 的改写逻辑与 Caius 的建议结合起来时,它会起作用,因此: HAVING (@UserChoice = 1 AND dbo.tblDocumentFact.useCase1 = 1) OR ...我还没有在 SSRS 中尝试过;我仍在尝试 SSMS,但我对这一进展感到满意。
    【解决方案2】:

    为了扩展蒂姆帖子下的评论,我认为它不起作用的原因是因为您的案例发出的字符串包含列名而不是列的值

    HAVING
      CASE WHEN @UserChoice = 1 THEN dbo.tblDocumentFact.useCase1 END = 1
      OR CASE WHEN @UserChoice = 2 THEN dbo.tblDocumentFact.useCase2 END = 1
     ...
    

    它甚至可以解决这个问题:

    拥有 案例@用户选择 当 1 时 dbo.tblDocumentFact.useCase1 当 2 那么 dbo.tblDocumentFact.useCase2 ... 结束 = 1

    问题(我相信;至少在 sql server 中,不完全确定 SSRS)是当你说:

    CASE WHEN @UserChoice = 1 THEN 'dbo.tblDocumentFact.useCase1' END = '1'
    

    您的情况何时发出文字字符串 dbo.tblDocumentFact.useCase1 而不是该行上该列的值。当然,这个文字字符串永远不会等于 1 的文字字符串

    总的来说,我更喜欢 Tim 的解决方案;我认为查询优化器更有可能在这种形式的位列上使用索引,但请注意使用 OR 会导致 sql server 忽略索引;我老地方的 DBA 经常重写如下查询:

    SELECT * FROM Person WHERE FirstName = 'john' OR LastName = 'Smith'
    

    进入这个:

    SELECT * FROM Person WHERE FirstName = 'john' 
    UNION
    SELECT * FROM Person WHERE LastName = 'Smith'
    

    因为当我们使用 OR 时,服务器不会合并 FirstName 上的索引和 LastName 上的另一个索引,但它会使用 UNION 表单中的两个索引并行执行

    考虑作为替代方案,将这些位标志组合成一个整数,或者作为二进制 2 的补码(如果您希望能够通过搜索 3 或选择 2 和 4 和 6 来说出用户选择 1 和 2通过搜索 42 [2^(2 -1) + 2^(4-1) + 2^(6-1)]) 或直接 int,您可以与 @userChoice 进行比较,并对其进行索引

    【讨论】:

    • Caius:非常感谢您的详细解释。我需要提高对 SQL Server 对索引的利用率、查询执行计划、查询优化等的理解。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2014-10-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多