簡體   English   中英

案例在WHERE子句中

[英]CASE inside WHERE Clause

我有一個查詢,其中需要從表中獲取結果,具體取決於用戶在vb.net頁上指定的參數。

聲明變量將是理想的解決方案,但是由於它不是內聯表值函數,因此我無法做到這一點,我在其他問題上讀到,在多聲明表函數中這是可能的,但不能使用此類函數推薦的。

以下查詢將是理想的解決方案或我想要實現的目標。

 USE [AUDIT]
 GO

ALTER FUNCTION [dbo].[GetCreated_LPA_Audits]
(   
@fechaInicio nvarchar (max),
@fechaFin nvarchar (max)
@Period int,
@Fecha int

)
RETURNS TABLE 
AS
RETURN 
(

SELECT T1.Plant,T1.Area,T1.Location,T1.IDAudit,T1.Auditor,T1.AuditDate,T1.DueDate,T1.CreationDate
FROM Header AS T1 inner join Audits AS T2 ON T1.IDChecklist = T2.IDChecklist
inner join AuditGroups AS T3 ON T2.IDGroup = T3.IDGroup 

  WHERE T3.IDGroup = '2' and CreationDate is not null AND
   CASE WHEN @Periodo = '0' THEN CreationDate>= @fechaInicio
        WHEN @Periodo = '1' THEN DATEPART(MONTH,CreationDate)>= @Fecha
        WHEN @Periodo = '2' THEN 
                               CASE WHEN @Fecha = 13 THEN 
                                                     DATEPART(MONTH,CreationDate)>= 1
                               END
        WHEN @Periodo = '2' THEN 
                               CASE WHEN @Fecha = 14 THEN 
                                                     DATEPART(MONTH,CreationDate)>= 7
                               END
        WHEN @Periodo = '3' THEN 
                               CASE WHEN @Fecha = 15 THEN 
                                                     DATEPART(Year,CreationDate)>= 2015
                               END
   END
   AND

CASE WHEN @Periodo = '0' THEN  @fechaInicio<=CreationDate
     WHEN @Periodo = '1' THEN  @Fecha<=DATEPART(MONTH,CreationDate)
     WHEN @Periodo = '2' THEN 
                               CASE WHEN @Fecha = 13 THEN 
                                                     6<=DATEPART(MONTH,CreationDate)
                               END
        WHEN @Periodo = '2' THEN 
                               CASE WHEN @Fecha = 14 THEN 
                                                     12<=DATEPART(MONTH,CreationDate)
                               END
        WHEN @Periodo = '3' THEN 
                               CASE WHEN @Fecha = 15 THEN 
                                                     DATEPART(Year,CreationDate)>= 2015
                               END
   END

AND在上一個查詢中,@ fechaInicio,@ fechaFin,@ Periodo和@Fecha是用戶提供的參數。

基本上,如果@Periodo = 0我需要獲取CreationDate>= @fechaInicio and CreationDate<=@fechaFin

如果@Periodo = 1我需要獲取@Periodo = 1 DATEPART(MONTH,CreationDate)>= @Fecha and DATEPART(MONTH,CreationDate)<= @Fecha

等等。 希望我能說清楚一點,在此先感謝!

EDIT利用@MatBailie偽代碼,有些輕視的改變和解答他的問題

IF @periodo = '0' THEN
    WHERE CreationDate  >= @fechaInicio  -- Copied from 1st CASE
      AND @fechaFin <= CreationDate   -- Copied from 2nd CASE

   -- gets results from @fechaInicio to @fechaFin
   -- i.e. results from 04/05/2016  to 04/16/2016

IF @periodo = '1' THEN
    WHERE DATEPART(MONTH,CreationDate) >= @Fecha   -- Copied from 1st CASE
      AND @Fecha <= DATEPART(MONTH,CreationDate)   -- Copied from 2nd CASE

    -- In these case both conditions are the same 'cause 
    -- @Fecha is the number of a month (1 - 12)
    -- i.e. @Fecha = 3 will get all the results of March
    -- regardless of what it is on @fechaInicio and @fechaFin

IF @periodo = '2' THEN
    IF @fetcha = 13 THEN
        WHERE DATEPART(MONTH,CreationDate)>= 1  -- Copied from 1st CASE
          AND 6<=DATEPART(MONTH,CreationDate)   -- Copied from 2nd CASE
    IF @fetcha = 14 THEN
        WHERE DATEPART(MONTH,CreationDate)>= 7  -- Copied from 1st CASE
          AND 12<=DATEPART(MONTH,CreationDate)  -- Copied from 2nd CASE

    -- You never use @fetchaInicio?
    -- You want everything in the first 6 months or last 6 months
    -- For every year
    -- Regardless of what is in @fetchaInicio?

    -- Exactly!!--

IF @periodo = '3' THEN
    IF @fetcha = 15 THEN
        WHERE DATEPART(Year,CreationDate)>= 2015  -- Copied from 1st CASE
          AND 2015 <= DATEPART(Year,CreationDate) -- Copied from 2nd CASE

    -- 

那么@periodo = '2' AND @fetcha NOT IN (13,14)呢? 那么@periodo = '3' AND @fetcha NOT IN (15)呢?

這種情況將不存在,它僅限於客戶端。 如果他們選擇@Periodo = '2'@Fecha值將為1-12,而沒有其他值。

@Periodo = '3'相同,然后@Fecha值將為15或16,均指2015或2016。

您最好重新組織WHERE子句,以使過濾后的字段位於左側,而不位於任何函數內。

例如...

WHERE
      CreationDate >= @VPeriodo
  AND CreationDate <  CASE
                        WHEN @Periodo = '0' THEN DATEADD(DAY,   1, @VPeriodo)
                        WHEN @Periodo = '1' THEN DATEADD(MONTH, 1, @VPeriodo)
                        WHEN @Periodo = '2' THEN DATEADD(MONTH, 1, @VPeriodo)
                        WHEN @Periodo = '3' THEN DATEADD(YEAR,  1, @VPeriodo)
                      END

在此示例的右側是所有標量常數。 這意味着您可以在CreationDate字段上進行范圍掃描。

同樣,@VPeriodo應該是DATEDATETIME而不是VARCHAR(MAX)



編輯: 包括使用VARCHAR跳過的箍


使用VARCHAR時,所有日期的格式都必須為YYYYMMDD 這樣一來,stuntng的自然順序與日期的自然順序相同。
'20161101' > '20161002'

當使用其他格式,例如YYYYDDMM ,它將失敗...
'20160111' < '20160210'問題,這種格式的10月2日到11月1日之后

WHERE
      CreationDate >= @VPeriodo
  AND CreationDate <  CONVERT(
                        NVARCHAR(8),
                        CASE
                          WHEN @Periodo = '0' THEN DATEADD(DAY,   1, CAST(@VPeriodo AS DATE))
                          WHEN @Periodo = '1' THEN DATEADD(MONTH, 1, CAST(@VPeriodo AS DATE))
                          WHEN @Periodo = '2' THEN DATEADD(MONTH, 1, CAST(@VPeriodo AS DATE))
                          WHEN @Periodo = '3' THEN DATEADD(YEAR,  1, CAST(@VPeriodo AS DATE))
                        END,
                        112    -- Format code for ISO dates, YYYYMMDD
                      )



編輯: OP發表評論並更改問題后,向OP提問

在這里,我要做的就是重新排列您的代碼,以使所編寫的內容成為偽代碼...

IF @periodo = '0' THEN
    WHERE CreationDate  >= @fetchaInicio  -- Copied from 1st CASE
      AND @fetchaInicio <= CreationDate   -- Copied from 2nd CASE

    -- These two conditions are direct from your code
    -- But they're the same as each other
    -- What do you REALLY want to happen when @Periodo = '0'?

IF @periodo = '1' THEN
    WHERE DATEPART(MONTH,CreationDate) >= @Fecha   -- Copied from 1st CASE
      AND @Fecha <= DATEPART(MONTH,CreationDate)   -- Copied from 2nd CASE

    -- These two conditions are direct from your code
    -- But they're the same as each other
    -- What do you REALLY want to happen when @Periodo = '1'?

IF @periodo = '2' THEN
    IF @fetcha = 13 THEN
        WHERE DATEPART(MONTH,CreationDate)>= 1  -- Copied from 1st CASE
          AND 6<=DATEPART(MONTH,CreationDate)   -- Copied from 2nd CASE
    IF @fetcha = 14 THEN
        WHERE DATEPART(MONTH,CreationDate)>= 7  -- Copied from 1st CASE
          AND 12<=DATEPART(MONTH,CreationDate)  -- Copied from 2nd CASE

    -- You never use @fetchaInicio?
    -- You want everything in the first 6 months or last 6 months
    -- For every year
    -- Regardless of what is in @fetchaInicio?

IF @periodo = '3' THEN
    IF @fetcha = 15 THEN
        WHERE DATEPART(Year,CreationDate)>= 2015  -- Copied from 1st CASE
          AND DATEPART(Year,CreationDate)>= 2015  -- Copied from 2nd CASE

    -- Both conditions are the same again, why?
    -- You want everything from 2015 onwards, forever?
    -- You never use @fetchaInicio?
    -- It's always 2015?

那么@periodo = '2' AND @fetcha NOT IN (13,14)呢? 那么@periodo = '3' AND @fetcha NOT IN (15)呢?


請您在上面給出我的偽代碼,並給出一些您實際想在每種情況下實際執行的操作的真實示例嗎?

每當您要在where子句中將參數用作條件時,都可以采用以下格式:

WHERE 
((@Periodo = '0' AND CreationDate
OR (@Periodo = '1' AND @VPeriodo = CAST(DATEPART(MONTH,CreationDate) as Nvarchar(10))
OR (@Periodo = '2' AND @VPeriodo = CAST(DATEPART(MONTH,CreationDate) as Nvarchar(10))
OR (@Periodo = '3' AND @VPeriodo = CAST(DATEPART(Year,CreationDate) as Nvarchar(10)))

確保參數評估是第一個,因為它將很快,如果它為假,將阻止查詢繼續處理條件中處理強度更高的部分。

首先,您是對的,內聯TFV確實更快。 如果不是太復雜。 我最好在SQL Server端為每個@Periodo參數值設置多個iTFV,並在客戶端的代碼中選擇合適的一個。

另外,您也可以在一個iTVF中進行操作

WHERE 
  @Periodo = '0' AND  CreationDate>= @fechaInicio and CreationDate<=@fechaFin
  OR @Periodo = '1' and DATEPART(MONTH,CreationDate)>= @Fecha and DATEPART(MONTH,CreationDate)<= @Fecha
  ...

但是,眾所周知,MS SQL會為OR運算符建立偶爾的錯誤計划,這可能會使您堅持iTVF的努力毫無用處。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM