条件连接 - 动态SQL

时间:2009-01-22 14:45:08

标签: sql sql-server join dynamic-sql

这里的DBA正试图将我直截了当的存储过程变成动态的sql怪物。不可否认,我的存储过程可能没有他们想要的那么快,但我不禁相信有足够的方法来完成基本上是条件连接的事情。

以下是我存储过程的一个示例:

SELECT 
*
FROM
table
WHERE
(
    @Filter IS NULL OR table.FilterField IN 
    (SELECT Value FROM dbo.udfGetTableFromStringList(@Filter, ','))
)

UDF将逗号分隔的过滤器列表(例如,银行名称)转换为表格。

显然,在where子句中使用过滤条件并不理想。欢迎任何有关基于存储的proc参数有条件地加入的更好方法的建议。除此之外,有没有人有任何建议支持或反对动态SQL方法?

由于

6 个答案:

答案 0 :(得分:4)

您可以对从UDF返回的表进行INNER JOIN,而不是在IN子句中使用它

您的UDF可能类似于

CREATE FUNCTION [dbo].[csl_to_table] (@list varchar(8000) )
RETURNS @list_table TABLE ([id] INT)
AS
BEGIN
    DECLARE     @index INT,
            @start_index INT,
            @id INT

    SELECT @index = 1 
    SELECT @start_index = 1
    WHILE @index <= DATALENGTH(@list)
    BEGIN

        IF SUBSTRING(@list,@index,1) = ','
        BEGIN

            SELECT @id = CAST(SUBSTRING(@list, @start_index, @index - @start_index ) AS INT)
            INSERT @list_table ([id]) VALUES (@id)
            SELECT @start_index = @index + 1
        END
        SELECT @index  = @index + 1
    END
    SELECT @id = CAST(SUBSTRING(@list, @start_index, @index - @start_index ) AS INT)
    INSERT @list_table ([id]) VALUES (@id)
    RETURN
END

然后在返回表中的id上INNER JOIN。此UDF假定您在逗号分隔列表中传递INT

修改

为了处理为@filter传入的null或没有值,我能看到的最直接的方法是根据@filter值在sproc中执行不同的查询。我不确定这会如何影响缓存的执行计划(如果有人可以确认会更新),或者最终结果是否会比原始的sproc更快,我认为答案就在于测试。

答案 1 :(得分:2)

看起来代码的重写正在另一个答案中解决,但是在存储过程中反对动态SQL的一个很好的论据是它打破了所有权链。

也就是说,当您正常调用存储过程时,它在使用execute命令执行动态SQL时,在存储过程所有者EXCEPT的权限下执行,对于动态SQL的上下文,它将恢复为调用者的权限,根据您的安全模型,这可能是不受欢迎的。

最后,您最好还是妥协并重写它以解决DBA的问题,同时避免使用动态SQL。

答案 2 :(得分:2)

我不确定我是否理解您对动态SQL的厌恶。也许是因为你的UDF已经很好地抽象出了问题的一些混乱,你觉得动态SQL会把它带回来。好吧,考虑到大多数(如果不是全部)DAL或ORM工具将广泛依赖于动态SQL,我认为您的问题可以重述为“我如何能够很好地抽象出动态SQL的混乱”。

就我而言,动态SQL为我提供了我想要的查询,以及随后我正在寻找的性能和行为。

答案 3 :(得分:2)

我认为你的方法没有任何问题。根据@Filter是否为null,重写它以使用动态SQL来执行两个不同的查询对我来说真的很傻。

我能看到你所拥有的唯一潜在缺点是它可能会在确定一个好的执行计划时造成一些困难。但如果表现足够好,就没有理由改变它。

答案 4 :(得分:1)

无论你做什么(这里的答案都有好处),一定要比较每个选项的性能和执行计划。

有时候,如果手动优化会影响代码的可维护性,并且在代码执行方式上没有任何差别,那么手动优化就毫无意义。

我首先想看看IN更改为简单LEFT JOINNULL检查(这不会消除你的udf,但它应该只被调用一次) :

SELECT *
FROM table
LEFT JOIN dbo.udfGetTableFromStringList(@Filter, ',') AS filter
    ON table.FilterField = filter.Value
WHERE @Filter IS NULL
    OR filter.Value IS NOT NULL

答案 5 :(得分:1)

您似乎正在尝试编写单个查询来处理两种情况:
1. @filter =“x,y,z”
2. @filter IS NULL

为了优化方案2,我会在UDF上INNER JOIN,而不是使用IN子句......

SELECT * FROM table
INNER JOIN dbo.udfGetTableFromStringList(@Filter, ',') AS filter
  ON table.FilterField = filter.Value

为了优化方案2,我不会尝试调整现有的查询,而是故意将这些情况分开,无论是IF语句还是UNION,并使用WHERE子句模拟IF ......

TSQL IF

IF (@filter IS NULL)
  SELECT * FROM table
ELSE
  SELECT * FROM table
  INNER JOIN dbo.udfGetTableFromStringList(@Filter, ',') AS filter
    ON table.FilterField = filter.Value


UNION模拟IF

SELECT * FROM table
  INNER JOIN dbo.udfGetTableFromStringList(@Filter, ',') AS filter
    ON table.FilterField = filter.Value

UNION ALL

SELECT * FROM table WHERE @filter IS NULL

这种设计的优点是每种情况都很简单,确定哪种简单就是自我简单。然而,将这两者组合成单个查询会导致诸如LEFT JOIN之类的妥协,因此会给每个查询带来显着的性能损失。