执行依赖于参数/变量的子查询

时间:2013-09-18 11:31:51

标签: sql sql-server stored-procedures subquery

我的问题

我正在尝试在MERGE查询中执行查询,以便将结果从一个表拉到另一个表中。这一切都很好,除了一个小小的hickup ...

为从表中获取数据而执行的查询取决于名为@User_ID的变量。

如果@User_IDNULL,则执行子查询A,否则执行子查询B.

我遇到的问题是我不知道如何在子查询中放置IF ...我该怎么做?

我尝试了什么......

这是我到目前为止所做的:

请注意,这可能有效,但由于我正在运行SQL Server 2008而不是2012(我认为),我认为IIF功能不可用,所以我需要另一种方法来实现此

-- Get the relevant logins for the past 12 weeks and insert
-- them into the temporary table where the week number is the same
MERGE INTO #Number_Of_Logins AS NOL
USING (
    SELECT IIF (ISNULL(@User_ID,0), (
            SELECT 
                Year(UA.Audit_When) AS Year_Num,
                DatePart(wk, UA.Audit_When) AS Week_Number,
                Count(*) AS Num_Logins
            FROM User_Auditing UA
            WHERE Audit_Type = 'SYSLI'
                AND User_ID = @User_ID
                AND DATEDIFF(wk, Audit_When, GETDATE()) <= 11
            GROUP BY Year(Audit_When), DatePart(wk, Audit_When)
        ),
        (
            SELECT 
                Year(UA.Audit_When) AS Year_Num,
                DatePart(wk, UA.Audit_When) AS Week_Number,
                Count(*) AS Num_Logins
            FROM User_Auditing UA
                JOIN User_Profiles UP ON UA.User_ID = UP.User_ID
            WHERE UA.Audit_Type = 'SYSLI'
                AND UP.Company_ID = @Company_ID
                AND DATEDIFF(wk, UA.Audit_When, GETDATE()) <= 11
            GROUP BY Year(Audit_When), DatePart(wk, UA.Audit_When)
        )
    )
) AS TEST
ON NOL.Week_Number = TEST.Week_Number
WHEN MATCHED THEN
UPDATE
    SET Number_Of_Logins = Num_Logins;

更新

我也尝试过使用CASE,我收到以下错误:

  

在上下文中指定的非布尔类型的表达式   条件是预期的,接近'那么'。

2 个答案:

答案 0 :(得分:1)

在不测试您的查询的情况下,存在一个明显的问题,我可以快速提出一种解决方案。

问题: CASE表达式和IIF函数调用使用值,而不是结果集。由于这个原因,您提出的上述方法在SQL Server 2012中也不起作用。

一种可能的解决方案:使用UNION ALL组合您的两个可能的查询,使用不同的WHERE子句来过滤一个或另一个结果集:

-- Get the relevant logins for the past 12 weeks and insert
-- them into the temporary table where the week number is the same
MERGE INTO #Number_Of_Logins AS NOL
USING (
        SELECT 
            Year(UA.Audit_When) AS Year_Num,
            DatePart(wk, UA.Audit_When) AS Week_Number,
            Count(*) AS Num_Logins
        FROM User_Auditing UA
        WHERE Audit_Type = 'SYSLI'
            AND User_ID = @User_ID
            AND DATEDIFF(wk, Audit_When, GETDATE()) <= 11
            AND @User_ID Is Not Null
        GROUP BY Year(Audit_When), DatePart(wk, Audit_When)

        UNION ALL

        SELECT 
            Year(UA.Audit_When) AS Year_Num,
            DatePart(wk, UA.Audit_When) AS Week_Number,
            Count(*) AS Num_Logins
        FROM User_Auditing UA
            JOIN User_Profiles UP ON UA.User_ID = UP.User_ID
        WHERE UA.Audit_Type = 'SYSLI'
            AND UP.Company_ID = @Company_ID
            AND DATEDIFF(wk, UA.Audit_When, GETDATE()) <= 11
            AND @User_ID Is Null
        GROUP BY Year(Audit_When), DatePart(wk, UA.Audit_When)
    )
) AS TEST
ON NOL.Week_Number = TEST.Week_Number
WHEN MATCHED THEN
UPDATE
    SET Number_Of_Logins = Num_Logins;

另一个选项:有两个完全独立的MERGE语句,IF语句选择执行哪一个(更多重复的代码,但可以具有查询缓存/性能优势):

-- Get the relevant logins for the past 12 weeks and insert
-- them into the temporary table where the week number is the same
IF @User_ID IS NOT NULL
    MERGE INTO #Number_Of_Logins AS NOL
    USING (
        SELECT Year(UA.Audit_When) AS Year_Num
            ,DatePart(wk, UA.Audit_When) AS Week_Number
            ,Count(*) AS Num_Logins
        FROM User_Auditing UA
        WHERE Audit_Type = 'SYSLI'
            AND User_ID = @User_ID
            AND DATEDIFF(wk, Audit_When, GETDATE()) <= 11
        GROUP BY Year(Audit_When)
            ,DatePart(wk, Audit_When)
        ) AS TEST
        ON NOL.Week_Number = TEST.Week_Number
    WHEN MATCHED
        THEN
            UPDATE
            SET Number_Of_Logins = Num_Logins;
ELSE
    MERGE INTO #Number_Of_Logins AS NOL
    USING (
        SELECT Year(UA.Audit_When) AS Year_Num
            ,DatePart(wk, UA.Audit_When) AS Week_Number
            ,Count(*) AS Num_Logins
        FROM User_Auditing UA
        INNER JOIN User_Profiles UP ON UA.User_ID = UP.User_ID
        WHERE UA.Audit_Type = 'SYSLI'
            AND UP.Company_ID = @Company_ID
            AND DATEDIFF(wk, UA.Audit_When, GETDATE()) <= 11
        GROUP BY Year(Audit_When)
            ,DatePart(wk, UA.Audit_When)
        ) AS TEST
        ON NOL.Week_Number = TEST.Week_Number
    WHEN MATCHED
        THEN
            UPDATE
            SET Number_Of_Logins = Num_Logins;

我可能会选择第二种选择,以获得(潜在)性能优势。

答案 1 :(得分:1)

这是您的解决方案,无需在如此高的水平上控制流量。

MERGE INTO #Number_Of_Logins AS NOL
USING (
    SELECT 
                Year(UA.Audit_When) AS Year_Num,
                DatePart(wk, UA.Audit_When) AS Week_Number,
                Count(*) AS Num_Logins
            FROM User_Auditing UA
                LEFT OUTER JOIN User_Profiles UP ON UA.User_ID = UP.User_ID
            WHERE Audit_Type = 'SYSLI'
                AND ( User_ID = @User_ID
                    OR ( @User_ID IS NULL
                       AND UP.Company_ID = @Company_ID 
                    )
                )
                AND DATEDIFF(wk, Audit_When, GETDATE()) <= 11
            GROUP BY Year(Audit_When), DatePart(wk, Audit_When)
) AS TEST
ON NOL.Week_Number = TEST.Week_Number
WHEN MATCHED THEN
UPDATE
    SET Number_Of_Logins = Num_Logins;

在这个例子中,where子句的第二个条件是: 提供了@User_ID 或者用户ID为空,公司ID匹配。

如果满足其中任何一个条件以及其他条件,则包含此记录。这应该封装您需要的所有信息,而不需要复杂的嵌套选择。