【问题标题】:What is the proper approach to defining a dynamic IN clause, based off of parameter flags?根据参数标志定义动态 IN 子句的正确方法是什么?
【发布时间】:2015-01-23 13:46:13
【问题描述】:

如果这是重复的,我深表歉意,但我已经搜索了这个网站,但我无法找到一个简单的答案来解决必须是常见问题的问题。

我有一个存储过程,它接受多个参数,但其中三个是标志。对于每个标记为“Y”的标志,我需要在IN 子句中插入一个值。换句话说,请考虑以下代码。

CREATE PROC testProc
  @FlagA char(1),
  @FlagB char(1),
  @FlagC char(1)
AS

--... Do stuff...

IF (@FlagA = 'Y')
  --Add 'AVal' to IN list
IF (@FlagB = 'Y')
  --Add 'BVal' to IN list
IF (@FlagC = 'Y')
  --Add 'CVal' to IN list

--... Do more stuff...

SELECT * FROM myTable
WHERE testField -- Insert correct IN clause.

这是我的代码试图完成的原因的一个极其简单的版本。我可以想出多种解决这种困境的方法,但没有一种方法感觉正确

我能想出的最佳解决方案是定义一个包含一个字段的临时表。对于设置的每个标志,我可以将相应的值添加到临时表中,然后使用...IN (SELECT * FROM #temp) 语句。

这是正确的方法还是对这个问题有更正确的解决方案?如果我的解决方案是最佳答案,我很好,但是,每当我看到临时表的简单用法时,我常常怀疑是否有更好的方法。

任何清晰度将不胜感激。谢谢。

【问题讨论】:

  • 这是一个很酷的方法。将值添加到临时表,然后从临时表中选择执行 IN。好的!我和你在一起,我遇到过几次,动态 sql(很明显)或另一个变量,它以正确的“IN”形式('Y','N')保存标志的连接值
  • 虽然您以不同的方式到达您的列表,但解决方案的其余部分与此处相同(希望我每次发布此链接作为答案时都能获得报酬):vyaskn.tripod.com/passing_arrays_to_stored_procedures.htm
  • 感谢大家的回复。三脚架链接(大声笑..三脚架,哈哈!)可以用于将来的参考,但一般来说,我尽量避免 SQL 中的两件事——带有 EXEC() 语句和临时表的动态 SQL。是的,每个都有它的位置,但正如我所提到的,当一个问题看起来很常见和简单时,我无法相信没有一个不涉及这两种方法中的任何一种的内置“正确”解决方案。跨度>
  • @TabAlleman 请停止发布该链接作为答案;-)。它已经过时了。它显示的大多数拆分 CSV 列表的方式都非常慢。它确实提到了一个数字表和 INNER JOIN 选项,但在其他方面更有可能让某人使用一种低效的方法而不是一种好的方法。为了记录,拆分 CSV 列表几乎总是通过 CLR 函数最快完成。

标签: sql-server tsql in-clause


【解决方案1】:

您必须检查这方面的性能,但您可以使用条件 WHERE 子句作为替代方案。这可能不是一个很好的解决方案,具体取决于您的实际程序有多复杂,但它为您提供了其他需要考虑的因素:

DECLARE @FlagA CHAR(1) = 'Y' ,
        @FlagB CHAR(1) = 'N' ,
        @FlagC CHAR(1) = 'Y'

SELECT  *
FROM    MyTable
WHERE   ( @FlagA = 'Y' AND testField = AVal )
     OR ( @FlagB = 'Y' AND testField = BVal )
     OR ( @FlagC = 'Y' AND testField = CVal )

这样,每行的WHERE 子句只有在满足第一个条件时才会被评估。

【讨论】:

  • 我考虑过某种形式的条件语句。但是,就我而言,您可以将 testField IN ... 语句替换为 tesField = 'valN'
  • @RLH 会有相同的查询计划
  • @Blam,正确。我不赞成使用IN 子句的想法,就其本身而言,我只有一个硬值列表,只有在提供标志时才需要检查。
【解决方案2】:

请,请,请不要执行动态 SQL 或多个 OR 条件(每个条件都有特定的“标志”参数)。 SQL Server 会喜欢一种非常简单且基于集合的方法:

  1. 创建临时表(或表变量)
  2. 将带有“Y”值的标志值插入其中
  3. EXISTS 子句中使用该临时表(或表变量)

例子:

DECLARE
  @FlagA CHAR(1),
  @FlagB CHAR(1),
  @FlagC CHAR(1);

SELECT
  @FlagA = 'Y',
  @FlagB = 'n',
  @FlagC = 'y';

DECLARE @Options TABLE (Value VARCHAR(50));

IF (@FlagA = 'Y')
BEGIN
  INSERT INTO @Options (Value) VALUES ('AVal');
END;

IF (@FlagB = 'Y')
BEGIN
  INSERT INTO @Options (Value) VALUES ('BVal');
END;

IF (@FlagC = 'Y')
BEGIN
  INSERT INTO @Options (Value) VALUES ('CVal');
END;

SELECT *
FROM   MyTable mt
WHERE  EXISTS (SELECT *
               FROM   @Options op
               WHERE  op.Value = mt.[testField]
              );

当然,将性能与您建议的 IN 列表进行比较可能没有什么坏处:

SELECT *
FROM   MyTable mt
WHERE  mt.[testField] IN (SELECT op.Value FROM @Options op);

而且,由于要搜索的值只能出现一次,从技术上讲,这是一个 INNER JOIN,对吗?通常我不喜欢将INNER JOIN 纯粹用作过滤器,但不会损害测试:)。如果 [testField] 被索引,这个版本可能会更好。

SELECT *
FROM   MyTable mt
INNER JOIN @Options op
        ON op.Value = mt.[testField];

在任何一种情况下,都首选使用这种方法(其中任何一种方法),因为它可以让 SQL Server 完成它所调整的工作:使用集合。 EXISTS 很有帮助,因为一旦找到第一行的查询,它将停止搜索。我怀疑EXISTSIN 列表更好,但只有3 个值应该被测试。但是,这种方法也更容易适应将来添加更多“标志”值,因为这不会改变查询。

如果您想对正在被 OUTER JOIN 的表执行 INNER JOIN 选项:

SELECT *
FROM   PrimaryTable pt
LEFT JOIN (
             MyTable mt
  INNER JOIN @Options op
          ON op.Value = mt.[testField]
          ) ON mt.ID = pt.ID;

【讨论】:

  • 这是我在问题中倾向于(并声称)的方法。如果可以,请详细说明为什么这是最佳选择。越技术越好。 ;) 谢谢!
  • INNER JOIN 语句在大多数情况下都有效,但我认为它不适用于我。我的SELECT 语句实际上非常复杂,IN 子句位于父表顶部的OUTER JOIN 内。据我所知,如果您在OUTER JOIN 中有一张桌子,您不能在不破坏父OUTER JOIN 的情况下将其INNER JOIN 放到另一张桌子上。所以 Table->ToOuterTable->ToInnerTable 不起作用。
  • @RLH 是的,您可以将一个表内部联接到一个正在外部联接的表。您只需要使用括号对它们进行分组,以便优化器知道分组。或者根据 ON 子句的位置可能不需要括号,但至少括号使它更具可读性。但我以前做过,效果很好。
  • 嗯,很有趣。我不知道怎么做,但是当我尝试过时,它通常会杀死我的查询。
  • 我的立场是正确的(而且人是这样更有效率!)使用连接将查询从 5-7 秒缩短到 2 秒!谢谢,谢谢!
【解决方案3】:

不确定是否更好,但另一种方法

  where @flag1 + testField = 'YAval'
     or @flag2 + testField = 'YBval'
     or @flag2 + testField = 'YCval'

where 'Y' + testField = in ( @flag1 + 'Aval', @flag2 + 'Bval', @flag3 + 'Cval')

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-08-30
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-01-06
    • 1970-01-01
    • 2017-02-26
    相关资源
    最近更新 更多