Sql将WHEN和IF语句与多个参数组合在一起

时间:2011-12-08 16:24:02

标签: sql sql-server if-statement

  

如何将Q1和Q2组合成Q5(但工作!)? PLSE?我卡住了... SQL Server 2008 R2

请看看我到目前为止所尝试的内容:

- Q1执行时没有错误:       DECLARE @RfDate日期         DECLARE @Description nvarchar(250)         DECLARE @BAccount int         SET @RfDate ='{Rf_Date}'         SET @Description = {dbo.BankstatementLine_Description | type = string}         SET @BAccount = {dbo.BankAccount_Id}

    IF @RfDate <> ''
       BEGIN
          SELECT *
          FROM
              dbo.BankStatementLine
          WHERE 
              Date >=@RfDate
          AND
              FkBankAccount = @BAccount
          AND
              IsDebit = 'true'
          AND
              Id NOT IN (select  FkBankStatementLine from DocumentBankStatementLine)
       END                                                     
    ELSE
       BEGIN
          SELECT *
          FROM 
              dbo.BankStatementLine
          WHERE 
              Date <> @RfDate
          AND
              FkBankAccount =@BAccount
          AND
              IsDebit = 'true'
          AND
              Id NOT IN (select  FkBankStatementLine from DocumentBankStatementLine)
       END

- Q2执行时没有错误:      DECLARE @RAmount浮动         SET @RAmount = {Rf_Amount}

    IF @RAmount <>''
       BEGIN
            SELECT Amount
            FROM      dbo.BankStatementLine
            WHERE Amount=@RAmount
       END
    ELSE 
       BEGIN
            SELECT Amount
            FROM      dbo.BankStatementLine

            WHERE Amount<>@RAmount
            END

在X-Zero 的帮助下(命令描述我的角色比我的程序技能更好),我想出了以下查询,它实际上做了它需要做的事情,除了最后一个两个AND OR规则。 LIKE没有任何影响,如果我在@RfAccept字段中输入'PEKING',则应显示描述为“0111111111 GPPeking”的条目,但不是.....

DECLARE @RfDate date
DECLARE @BAccount int
DECLARE @RfAmount decimal
DECLARE @RfAcAmount float
DECLARE @RfKenmerk nvarchar(250)
DECLARE @RfAccept nvarchar(250)
SET @RfDate = '{Rf_Date}'
SET @BAccount = {dbo.BankAccount_Id}
SET @RfAmount = {Rf_Amount}
SET @RfAcAmount = {Rf_AccAmount}
SET @RfKenmerk = {Rf_Betalingskenmerk|type=string}
SET @RfAccept = {Rf_Acceptgiro|type=string}

        SELECT *
        FROM
            dbo.BankStatementLine
        WHERE -- All statements can have a value or ''. All statements are not mandatory.
                isDebit = 1
        AND Id NOT IN (select  FkBankStatementLine from DocumentBankStatementLine)

        AND fkBankAccount = {dbo.bankAccount_Id}

        AND ((Date = @RfDate AND @RfDate <> '')
        OR (Date <> @RfDate AND @RfDate = ''))

        AND ((Amount = @RfAmount AND @RfAmount <> '')
        OR (Amount <> @RfAmount AND @RfAmount = ''))

        AND ((Amount = @RfAcAmount AND @RfAcAmount <> '')
        OR (Amount <> @RfAcAmount AND @RfAcAmount = ''))

        AND((Description LIKE '%@RfAccept%' AND @RfAccept<>'')--When Rf_Acceptgiro has a value, the value must be part of the field Description.
        OR (Description <> @RfAccept AND @RfAccept ='')) --OR Return all Description rules

        AND((Description LIKE '%@RfKenmerk%' AND @RfKenmerk<>'')--When Rf_Kenmerk has a value, the value must be part of the field Description. 
        OR (Description <> @RfKenmerk AND @RfKenmerk =''))--OR Return all Description rules

2 个答案:

答案 0 :(得分:1)

...您似乎对(不完全)理解SQL有一些问题,而且它只能在一般用途中完成。这些“程序”通常(虽然并非总是)不必要。你没有列出你的RDBMS,所以我写的就好像是DB2(不包括语句参数),尽管大多数主要的RDBMS都能够按原样运行语句

-Q1可以简单地重写为:

SELECT a.* -- note, make sure to list all columns in actual query, '*' is bad form  
FROM dbo.BankStatementLine as a  
EXCEPTION JOIN DocumentBankStatementLine as b  
ON b.fkBankStatementLine = a.id  -- this is your 'NOT IN' statement  
WHERE a.isDebit = 'true' -- this is really bad; usea boolean type if available
                         -- otherwise, use `int`/`char` 1/0, with check constraints  
AND a.fkBankAccount = {dbo.bankAccount_id}
AND (({Rf_Date} IS NOT NULL AND a.Date >= {Rf_Date}) -- please name 'a.date' better...
     OR ({Rf_Date} IS NULL AND a.Date IS NULL)) -- and use `date` types, and -null-

-Q2遵循相同的一般原则(虽然看起来毫无意义;你已经知道了数量):

SELECT amount -- it's -really- bad to use `float` types for money 
              -- use `decimal` or `int` (cents) types instead
FROM dbo.BankStatementLine
WHERE (({Rf_Amount} IS NOT NULL AND amount = {Rf_Amount})
        OR ({Rf_Amount} IS NULL AND amount IS NULL))

-Q3似乎是Q1和Q2的组合(有点):

SELECT a.*
FROM dbo.BankStatementLine as a  -- unless there's a -need- to query a specific  
                        -- schema, leave it off; you'll get a bit of flexibility
EXCEPTION JOIN DocumentBankStatementLine as b
ON b.fkBankStatementLine = a.id
WHERE a.isDebit = 'true'
AND a.fkBankAccount = {dbo.bankAccount_Id}  -- this seems like a table name?
AND (({Rf_Date} IS NOT NULL AND a.Date >= {Rf_Date})
     OR ({Rf_Date} IS NULL AND a.Date IS NULL))  
AND (({Rf_Amount} IS NOT NULL AND amount = {Rf_Amount})  -- didn't know what you
     OR ({Rf_AccAmount} IS NOT NULL AND amount = {Rf_AccAmount}))  -- actually wanted
          -- But `null`s don't compare to anything, so...

-Q4:我不确定你是否可以实际嵌套if语句(特别是Amount-(IF @RAmount <> '')行,你肯定不应该。它也不清楚(因为Q3)订购是否重要,所以我认为是。请尝试这样做(请注意,这个和原始版本只有在返回一行时才有效):

SELECT *
FROM dbo.BankStatementLine
WHERE amount = COALESCE({RF_Amount}, {RfAccAmount}, (SELECT amount
                                                     FROM dbo.BankStatementLine
                                                     WHERE amount IS NOT NULL))

-Q5似乎是Q3和Q1的一些奇怪的集合。虽然适用相同的可重写规则...如果你想要将Q2和Q1结合起来,只需将WHERE子句中缺少的谓词加在一起(为了我们的目的,EXCEPTION JOIN被认为是{{ 1}}子句谓词):

WHERE

嵌套SELECT a.* FROM dbo.BankStatementLine as a EXCEPTION JOIN DocumentBankStatementLine as b ON b.fkBankStatementLine = a.id WHERE a.isDebit = 'true' AND a.fkBankAccount = {dbo.bankAccount_Id} AND (({Rf_Date} IS NOT NULL AND a.Date >= {Rf_Date}) OR ({Rf_Date} IS NULL AND a.Date IS NULL)) AND (({Rf_Amount} IS NOT NULL AND amount = {Rf_Amount}) OR ({Rf_Amount} IS NULL AND amount IS NULL)) 语句/子句总是略显危险,即使在普通命令式(Java,C#,C ++等)语言中也是如此。在SQL和类似的情况下,它可能只是完全错误。你好像在想一个强制性的程序员 - 学会用(你的ifWHERE条款)思考,你会好多了。< / p>

<小时/> 编辑:

它没有返回您期望的结果,因为比较区分大小写。对于大多数其他编程语言来说,这也是正确的 作为旁注,执行JOIN(带有前导LIKE '%<insertValueHere>')可以防止使用指标(因为优化器没有好方法知道列中的 where 价值出现)。将函数包装在函数中也不会有太大帮助,但我相信有一些方法可以创建“物化”/计算的指标,这样该值就已经可用了。

无论如何,这是适当的调整。请注意,如果列始终具有值(而不是null),则如果未提供输入参数,则无需进行比较。

%

当您输入的参数上写着“我不在乎它是什么,给我一切”时,不需要与该字段进行比较(并且可能会变得更好通过删除比较表现)。如果您仍然需要检查以确保该字段不为空或空白或其他内容,那么您必须在某些时候检查它 - 在SQL中,或在获取结果集之后。
此外,如果您使用DECLARE @RfDate date DECLARE @BAccount int DECLARE @RfAmount decimal DECLARE @RfAcAmount float DECLARE @RfKenmerk nvarchar(250) DECLARE @RfAccept nvarchar(250) SET @RfDate = '{Rf_Date}' SET @BAccount = {dbo.BankAccount_Id} SET @RfAmount = {Rf_Amount} SET @RfAcAmount = {Rf_AccAmount} SET @RfKenmerk = {Rf_Betalingskenmerk|type=string} SET @RfAccept = {Rf_Acceptgiro|type=string} SELECT * FROM dbo.BankStatementLine WHERE isDebit = 1 -- Thank you, this is much better. If numeric, add check constraint. AND Id NOT IN (select FkBankStatementLine from DocumentBankStatementLine) -- I am unsure of the performance of this relative to other options AND fkBankAccount = {dbo.bankAccount_Id} -- Assuming this is an actual date field, non-nullable, you don't need the second comparison AND ((Date = @RfDate AND @RfDate <> '') OR (@RfDate = '')) -- Don't store amounts as strings, just don't... -- Comparison removed for reason above. AND ((Amount = @RfAmount AND @RfAmount <> '') OR (@RfAmount = '')) -- See notes above AND ((Amount = @RfAcAmount AND @RfAcAmount <> '') OR (@RfAcAmount = '')) -- If you want all rules/whatever, then... -- If it's case insensitive, it's best to -store- it that way. Otherwise, wrap in function AND((UPPER(Description) LIKE '%' || UPPER(@RfAccept) || '%' AND @RfAccept<>'') OR (@RfAccept ='')) -- Same thing here too. AND((Description LIKE '%@RfKenmerk%' AND @RfKenmerk<>'') OR (@RfKenmerk ='')) 在字段内搜索,因为它包含多个值(作为分隔符/格式化列表或数据结构),您(很可能)会错误地设置表格;重新格式化以将列放入其自己的表中(嗯,_usually)。如果它是事物的一部分(比如在书名中搜索一个单词),你只需要考虑性能(好吧,有可能方法,但会导致设计有些古怪)

答案 1 :(得分:0)

看起来您正在使用SQL Server(通常有助于指定):

  Amount = CASE WHEN @RAmount <> '' THEN @RAmount
                WHEN @RAcAmount <> '' THEN @RacAmount
                ELSE Amount END

是我认为你正在寻找的。如果第一个参数不为空,则使用它。如果第一个参数为空,而第二个参数不为,则使用第二个参数。如果两者都为空,则返回所有值。