【问题标题】:Bypass last INNER JOIN in query绕过查询中的最后一个 INNER JOIN
【发布时间】:2011-04-19 08:09:25
【问题描述】:

我有以下存储过程,它接受用户 ID、开始日期、结束日期和逗号分隔列表中的代码列表,并返回这两个日期之间与其中一个匹配的所有活动记录列表中的代码。

ALTER PROCEDURE [dbo].[ActivitiesSummary] 

@UserID varchar(30),
@StartDate datetime,
@EndDate datetime,
@Codes varchar(100)

AS

BEGIN

 SET NOCOUNT ON;

 SELECT act.SectionID, act.UnitID, act.ActivityCode
 FROM dbo.Activities act INNER JOIN ConvertCodeListToTbl(@Codes) i ON act.ActivityCode = i.code
 WHERE act.ActivityDate>=@Startdate AND act.ActivityDate<@EndDate
 GROUP BY act.SectionID, act.UnitID, act.ActivityCode
 ORDER BY act.SectionID, act.UnitID, act.ActivityCode
END

ConvertCodeListToTbl(@Codes) 是一个函数,它采用逗号分隔的代码列表(例如,'A0001, B0001, C0001')并返回每行一个代码的表:

A0001
B0001
C0001

此方法非常有效,除非未选择任何代码。发生这种情况时,我没有收到任何记录,因为 @Codes='' 并且最后一个 INNER JOIN 没有返回任何记录。

我想要发生的事情:如果 @Codes='',忽略最后一个 INNER JOIN,或者找到一种方法来返回所有记录而不管代码。

我有什么选择?

【问题讨论】:

  • 我之前设法避免使用动态 SQL,因为它存在所有相关的安全问题,所以如果可能的话,我想继续这样做。

标签: sql sql-server-2005 stored-procedures


【解决方案1】:

听起来您需要将 INNER JOIN 行更改为:

FROM dbo.Activities act INNER JOIN ConvertCodeListToTbl(@Codes) i 
ON (act.ActivityCode = i.code OR @Codes = '')

【讨论】:

  • 我设计了一个带有位标志的 if 语句,但这样更好。
【解决方案2】:

我不知道性能将如何比较,但这应该是一个相同的查询,可以满足您的需求:

SELECT act.SectionID, act.UnitID, act.ActivityCode 
FROM dbo.Activities act
WHERE act.ActivityDate>=@Startdate AND act.ActivityDate<@EndDate
    AND (@Codes = '' OR
         EXISTS
           (SELECT *
            FROM ConvertCodeListToTbl(@Codes)
            WHERE act.ActivityCode = code))
GROUP BY act.SectionID, act.UnitID, act.ActivityCode 
ORDER BY act.SectionID, act.UnitID, act.ActivityCode

另一种选择是让ConvertCodeListToTbl 在其输入为空时返回SELECT ActivityCode FROM dbo.Activities

【讨论】:

    【解决方案3】:

    在 SQL Server 中,您可以为存储过程中的参数设置默认值。

    将顶部的行更改为:

    @Codes varchar(100) = '*'

    然后你的另一个proc ConvertCodeListToTbl 返回所有值,如果它被传递'*' 作为它的参数。

    【讨论】:

    • 很遗憾,ConvertCodeListToTbl 只能看到通过@Codes 传递的代码,因此无法返回整个列表。
    • @ofm - 是否不可能在 proc 中添加一行 IF @Codes = '*' THEN SELECT * FROM CODES...ELSE....
    • 有可能,虽然我选择的答案更简洁。
    【解决方案4】:

    您可以在 join 语句中添加另一个条件。这可能不是有效的 sql server 语法,但在做类似的事情

    ... i ON (act.ActivityCode = i.code) OR IF(@codes = '', 1, 0)
    

    如果@codes 为空,基本上会在任何地方加入。

    【讨论】:

    • 在 T-SQL 中我能想到的最接近的东西是 CASE..WHEN 语句,据我所知,它需要另一个 SELECT 语句并且变得相当混乱。
    【解决方案5】:

    我不确定在 SQL 中执行此操作的方法不会破坏查询计划(例如,您可以使用动态 sql 执行此操作,但随后会生成两个非常不同的查询)。

    您的调用应用程序在调用存储过程之前必须知道@codes 是空的,那么为什么不让它调用另一个不执行连接的存储过程呢?

    【讨论】:

    • 如果我找不到更好的解决方案,那么我可能不得不这样做,但这不是理想的解决方案,因为这个特定的内部连接是其他几个查询的一部分,这意味着我有几个几乎重复的存储过程。
    猜你喜欢
    • 2015-05-29
    • 1970-01-01
    • 2020-09-19
    • 1970-01-01
    • 1970-01-01
    • 2014-02-24
    • 2014-03-13
    • 2013-12-22
    • 1970-01-01
    相关资源
    最近更新 更多