SQL - Case在Where子句中减少IF语句

时间:2016-01-28 18:35:03

标签: sql-server tsql

尝试执行时出错。我不是DBA,但是我需要根据传入的变量来过滤where子句。我使用IF语句的大量语句工作,但看起来非常糟糕,所以我想出了这个想法但是却出错了

查询:

    DECLARE
    @VariableName AS NVARCHAR(100),
    @VariableValue AS NVARCHAR(MAX),
    @PileFrom AS INT,
    @PileTo AS INT,
    @BeginDate AS DATETIME,
    @EndDate AS DATETIME,
    @Truck AS BIT,
    @Train AS BIT
--AS
BEGIN
    IF @BeginDate = '' BEGIN
        SET @Begindate = '1/1/1900'
    END

    IF @EndDate = '' BEGIN
        SET @EndDate = '1/1/9999'
    END 

    IF @Truck = 1 BEGIN
        SELECT 
            Car_ID,
            PO_Number,
            Vendor,
            Shipper,
            Commodity,
            Date_Weighed_In,
            Date_Weighed_Out,
            Pile_No AS 'PILe From',
            Pile_No1 AS 'Pile To',
            Operator_In,
            Operator_Out,
            Comments_In,
            Comments_Out,
            Weight_In,
            Weight_Out,
            Transaction_Num,
            Record_time_Stamp
        FROM
            dbo.Transactions_Truck
        WHERE
            (Date_Weighed_IN BETWEEN @BeginDate AND @EndDate) AND
            CASE 
                WHEN @VariableName = 'rbCardID'         THEN Car_ID = @VariableValue
                WHEN @VariableName = 'rbPONumber'       THEN PO_Number = @VariableValue
                WHEN @VariableName = 'rbVendor'         THEN Vendor = @VariableValue
                WHEN @VariableName = 'rbCommodity'      THEN Commodity = @VariableValue
                WHEN @VariableName = 'rbPileNumber'     THEN Pile_No = @PileFrom AND Pile_No1 = @PileTo
                WHEN @VariableName = 'rbTransactionNum' THEN Transaction_Num = @VariableValue
                WHEN @VariableName = 'rbOperator'       THEN Operator_In = @VariableValue
                WHEN @VariableName = 'rbComments'       THEN Comments_In = @VariableValue           
        END ELSE IF @Train = 1 BEGIN
            SELECT 
                Car_ID,
                PO_Number,
                Vendor,
                Shipper,
                Commodity,
                Date_Weighed_In,
                Date_Weighed_Out,
                Pile_No AS 'PILe From',
                Pile_No1 AS 'Pile To',
                Operator_In,
                Operator_Out,
                Comments_In,
                Comments_Out,
                Weight_In,
                Weight_Out,
                Transaction_Num,
                Record_time_Stamp
            FROM
                dbo.Transactions_train
            WHERE
                (Date_Weighed_IN BETWEEN @BeginDate AND @EndDate) AND
                CASE 
                    WHEN @VariableName = 'rbCardID'         THEN Car_Id = @VariableValue
                    WHEN @VariableName = 'rbPONumber'       THEN PO_Number = @VariableValue
                    WHEN @VariableName = 'rbVendor'         THEN Vendor = @VariableValue
                    WHEN @VariableName = 'rbCommodity'      THEN Commodity = @VariableValue
                    WHEN @VariableName = 'rbPileNumber'     THEN Pile_No = @PileFrom AND Pile_No1 = @PileTo
                    WHEN @VariableName = 'rbTransactionNum' THEN Transaction_Num = @VariableValue
                    WHEN @VariableName = 'rbOperator'       THEN Operator_In = @VariableValue
                    WHEN @VariableName = 'rbComments'       THEN Comments_In = @VariableValue
                    WHEN @VariableName = 'rbRailRoad'       THEN Rail_Road = @VariableValue
        END
END

2 个答案:

答案 0 :(得分:2)

你不能做那样的where语句..你应该只使用AND和OR

WHERE   (Date_Weighed_IN BETWEEN @BeginDate AND @EndDate)
        AND (
                (@VariableName = 'rbCardID'         AND Car_Id          = @VariableValue)
            OR  (@VariableName = 'rbPONumber'       AND PO_Number       = @VariableValue)
            OR  (@VariableName = 'rbVendor'         AND Vendor          = @VariableValue)
            OR  (@VariableName = 'rbCommodity'      AND Commodity       = @VariableValue)
            OR  (@VariableName = 'rbPileNumber'     AND Pile_No         = @PileFrom         
                                                    AND Pile_No1        = @PileTo       )
            OR  (@VariableName = 'rbTransactionNum' AND Transaction_Num = @VariableValue)
            OR  (@VariableName = 'rbOperator'       AND Operator_In     = @VariableValue)
            OR  (@VariableName = 'rbComments'       AND Comments_In     = @VariableValue)
            OR  (@VariableName = 'rbRailRoad'       AND Rail_Road       = @VariableValue)
        )

答案 1 :(得分:1)

JamieD77的答案应该做的工作,但是很多OR可能会让你遇到性能问题,所以我也会考虑使用dynamic SQL来尝试获得更小的SQL语句执行(这没有经过测试,我已经重新格式化以使其更具可读性):

DECLARE @SQL NVARCHAR(4000) = '
   SELECT Car_ID, PO_Number, Vendor, Shipper, Commodity, Date_Weighed_In, Date_Weighed_Out, Pile_No AS [PILe From],
            Pile_No1 AS [Pile To], Operator_In, Operator_Out, Comments_In, Comments_Out, Weight_In, Weight_Out,
            Transaction_Num, Record_time_Stamp
        FROM dbo.Transactions_Truck
        WHERE (Date_Weighed_IN BETWEEN @BeginDate AND @EndDate)'

-- merge conditions that are common to @Train = 0 and @Train = 1
IF (@VariableName = 'rbCardID') @SQL = @SQL + CHAR(13) + CHAR(10) + N' AND Car_ID = @VariableValue'
IF (@VariableName = 'rbPONumber') @SQL = @SQL + CHAR(13) + CHAR(10) + N' AND PO_Number = @VariableValue'
-- other conditions come here
IF (@VariableName = 'rbComments') @SQL = @SQL + CHAR(13) + CHAR(10) + N' AND Comments_In = @VariableValue'

IF (@Train = 1 AND @VariableName = 'rbRailRoad') @SQL = @SQL + CHAR(13) + CHAR(10) + N' AND Rail_Road = @VariableValue'

-- for debugging purposes
PRINT @SQL

EXEC sp_executesql @SQL, N'@BeginDate DATETIME, @EndDate DATETIME, @VariableValue NVARCHAR(MAX)', 
   @BeginDate = @BeginDate, @EndDate = @EndDate, @VariableValue = @VariableValue

这样你的代码就更DRY了,我认为你应该更快(我不知道引擎是否知道优化所有这些OR,这可能比使用动态可能丢失的性能更糟糕查询)。

更好的方法是以打字的方式进行比较。与@VariableValue相比,我看不到所有列的数据类型,但我认为Ids和日期不是字符串。如果类型与NVARCHAR不同,则将为每种类型的不匹配生成隐式转换,性能将降低。

但是,这需要使用Entity Framework完全重写您的逻辑(如果这是您的程序正在进行的所有内容并且这是很好的练习,那么这应该不是很难)或类似的东西。