Interruttore / caso SQL nella clausola 'where'


146

Ho provato a cercare in giro, ma non sono riuscito a trovare nulla che mi potesse aiutare.

Sto provando a farlo in SQL:

declare @locationType varchar(50);
declare @locationID int;

SELECT column1, column2
FROM viewWhatever
WHERE
CASE @locationType
    WHEN 'location' THEN account_location = @locationID
    WHEN 'area' THEN xxx_location_area = @locationID
    WHEN 'division' THEN xxx_location_division = @locationID

So che non dovrei mettere '= @locationID' alla fine di ognuno, ma non riesco ad avvicinare la sintassi nemmeno alla correttezza. SQL continua a lamentarsi del mio '=' sulla prima riga QUANDO ...

Come posso fare questo?

Risposte:


191
declare @locationType varchar(50);
declare @locationID int;

SELECT column1, column2
FROM viewWhatever
WHERE
@locationID = 
  CASE @locationType
      WHEN 'location' THEN account_location
      WHEN 'area' THEN xxx_location_area 
      WHEN 'division' THEN xxx_location_division 
  END

1
Come notato da TomH nel commento alla tua risposta di seguito, hai creato l'SQL in modo errato. Ho testato il mio in SQL Server 2005 e ha funzionato bene.
— Bob Probst,

Perché sono necessari i @? Cosa fanno?
— Tadej,

2
@ indica le variabili in t-sql, senza @, @locationID sarebbe interpretato come un nome di colonna.
— Christian Sloper,

70

senza una dichiarazione del caso ...

SELECT column1, column2
FROM viewWhatever
WHERE
    (@locationType = 'location' AND account_location = @locationID)
    OR
    (@locationType = 'area' AND xxx_location_area = @locationID)
    OR
    (@locationType = 'division' AND xxx_location_division = @locationID)

2
Questa è la volontà di dare un risultato leggermente diversa in quanto le uscite istruzione case, dopo una condizione è soddisfatta - ma il o la sintassi valuterà tutte le possibilità
— tembre

1
@ settembre Anche se SQL fosse un linguaggio procedurale, se il primo OR è vero, il resto dell'espressione non viene valutato. Poiché SQL è un linguaggio dichiarativo, come fai a sapere che DBM valuterà tutti gli OR? Domanda sincera, non capisco.
— ArturoTena,

In SQL il resto dell'espressione viene valutato nella sintassi OR. Prova questo (non mi permetterebbe di includere il simbolo @ - dovrai correggerlo se vuoi testarlo): dichiar var varchar (5) set var = '0' seleziona 2 / var dove var <> 0 o ISNUMERICO (var) = 1. Voglio che la condizione esca perché var È uguale a 0, ma va avanti per verificare se è numerico, che è, e quindi la dichiarazione restituisce un errore.
— dicembre

questo mi ha aiutato nel mio caso in cui avevo un operatore di confronto diverso per ciascun valore del tipo.
— Alex,

meglio ancora DECLARE @locationType NVARCHAR(50) = 'youchoose' IF @locationType = 'location' BEGIN SELECT column1, column2 FROM viewWhatever WHERE (account_location = @locationID) END IF @locationType = 'area' BEGIN SELECT column1, column2 FROM viewWhatever WHERE (xxx_location_area = @locationID) END IF @locationType = 'division' BEGIN SELECT column1, column2 FROM viewWhatever WHERE (xxx_location_division = @locationID) END
— Lukek,

35

Ecco qui.

SELECT
   column1, 
   column2
FROM
   viewWhatever
WHERE
CASE 
    WHEN @locationType = 'location' AND account_location = @locationID THEN 1
    WHEN @locationType = 'area' AND xxx_location_area = @locationID THEN 1
    WHEN @locationType = 'division' AND xxx_location_division = @locationID THEN 1
    ELSE 0
END = 1

5
Bene, l'avrei scritto come SELEZIONA colonna1, colonna2 DA viewWhatever WHERE (@locationType = 'location' AND account_location = @locationID) OR (@locationType = 'area' AND xxx_location_area = @locationID) OR (@locationType = 'division' AND xxx_location_division = @locationID)
— Jan de Vos

2
questo è un grande esempio, che ne dici del piano di esecuzione della query con la migliore risposta (personalmente preferisco che questo codice di metodo sia chiaro e pulito)
— PEO

1
davvero fantastico se vuoi usare una clausola where diversa con un tipo diverso come int nella prima clausola e nvarchar nella seconda. con Bob Probst la soluzione non funziona. grazie
— Julian50

6

Direi che questo è un indicatore di una struttura del tavolo difettosa. Forse i diversi tipi di posizione dovrebbero essere separati in diverse tabelle, consentendo di eseguire query molto più ricche ed evitare anche di avere colonne superflue in giro.

Se non riesci a modificare la struttura, potrebbe funzionare qualcosa di simile al seguente:

SELECT
    *
FROM
    Test
WHERE
    Account_Location = (
        CASE LocationType
          WHEN 'location' THEN @locationID
          ELSE Account_Location
        END
    )
    AND
    Account_Location_Area = (
        CASE LocationType
          WHEN 'area' THEN @locationID
          ELSE Account_Location_Area
        END
    )

E così via ... Non possiamo cambiare la struttura della query al volo, ma possiamo sovrascriverla rendendo uguali i predicati.

EDIT: I suggerimenti di cui sopra sono ovviamente molto meglio, basta ignorare il mio.


Non penso che questa sia una struttura del tavolo imperfetta. La tabella è stata impostata in questo modo in modo che fosse auto-riferimento avere una quantità infinita di relazioni genitore / figlio. Credetemi, era apposta. Non credo di voler cambiare la struttura della mia tabella per usare solo un'istruzione switch. non è poi così importante
— Miglia,

5

Il problema è che quando il motore SQL va a valutare l'espressione, controlla la parte FROM per estrarre le tabelle appropriate e quindi la parte WHERE per fornire alcuni criteri di base, quindi non può valutare correttamente una condizione dinamica su quale colonna controllare contro.

È possibile utilizzare una clausola WHERE quando si verificano i criteri WHERE nel predicato, ad esempio

WHERE account_location = CASE @locationType
                              WHEN 'business' THEN 45
                              WHEN 'area' THEN 52
                         END

quindi nel tuo caso particolare, dovrai inserire la query in una procedura memorizzata o creare tre query separate.


5

L'operatore OR può essere alternativo al caso in cui si trova

ALTER PROCEDURE [dbo].[RPT_340bClinicDrugInventorySummary]
    -- Add the parameters for the stored procedure here
     @ClinicId BIGINT = 0,
     @selecttype int,
     @selectedValue varchar (50)
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
SELECT
    drugstock_drugname.n_cur_bal,drugname.cdrugname,clinic.cclinicname

FROM drugstock_drugname
INNER JOIN drugname ON drugstock_drugname.drugnameid_FK = drugname.drugnameid_PK
INNER JOIN drugstock_drugndc ON drugname.drugnameid_PK = drugstock_drugndc.drugnameid_FK
INNER JOIN drugndc ON drugstock_drugndc.drugndcid_FK = drugndc.drugid_PK
LEFT JOIN clinic ON drugstock_drugname.clinicid_FK = clinic.clinicid_PK

WHERE   (@ClinicId = 0 AND 1 = 1)
    OR  (@ClinicId != 0 AND drugstock_drugname.clinicid_FK = @ClinicId)

    -- Alternative Case When You can use OR
    AND ((@selecttype = 1 AND 1 = 1)
    OR  (@selecttype = 2 AND drugname.drugnameid_PK = @selectedValue)
    OR  (@selecttype = 3 AND drugndc.drugid_PK = @selectedValue)
    OR  (@selecttype = 4 AND drugname.cdrugclass = 'C2')
    OR  (@selecttype = 5 AND LEFT(drugname.cdrugclass, 1) = 'C'))

ORDER BY clinic.cclinicname, drugname.cdrugname
END

3

Si prega di provare questa domanda. Risposta al precedente post:

select @msgID, account_id
    from viewMailAccountsHeirachy
    where 
    CASE @smartLocationType
        WHEN 'store' THEN account_location
        WHEN 'area' THEN xxx_location_area 
        WHEN 'division' THEN xxx_location_division 
        WHEN 'company' THEN xxx_location_company 
    END  = @smartLocation

2
Per chi legge questo in futuro: è la stessa come la risposta accettata da @ bob-prost sopra: stackoverflow.com/a/206500/264786
— ArturoTena

2

Prova questo:

WHERE (
    @smartLocationType IS NULL 
    OR account_location = (
         CASE
            WHEN @smartLocationType IS NOT NULL 
                 THEN @smartLocationType
            ELSE account_location 
         END
    )
)

1
Sottovalutato perché non capisco come questo possa scegliere tra le stringhe date (cioè 'location', 'area', 'division')
— ArturoTena

0
CREATE PROCEDURE [dbo].[Temp_Proc_Select_City]
    @StateId INT
AS  
        BEGIN       
            SELECT * FROM tbl_City 
                WHERE 
                @StateID = CASE WHEN ISNULL(@StateId,0) = 0 THEN 0 ELSE StateId END ORDER BY CityName
        END

-1
Case Statement in SQL Server Example

Syntax

CASE [ expression ]

   WHEN condition_1 THEN result_1
   WHEN condition_2 THEN result_2
   ...
   WHEN condition_n THEN result_n

   ELSE result

END

Example

SELECT contact_id,
CASE website_id
  WHEN 1 THEN 'TechOnTheNet.com'
  WHEN 2 THEN 'CheckYourMath.com'
  ELSE 'BigActivities.com'
END
FROM contacts;

OR

SELECT contact_id,
CASE
  WHEN website_id = 1 THEN 'TechOnTheNet.com'
  WHEN website_id = 2 THEN 'CheckYourMath.com'
  ELSE 'BigActivities.com'
END
FROM contacts;

4
Sottovalutato perché questo risponditore non è correlato alla domanda. La domanda era come usare CASE sulla clausola WHERE, non come usare CASE su una colonna SELECT.
— ArturoTena,

-2

Prova questa domanda. È molto facile da capire:

CREATE TABLE PersonsDetail(FirstName nvarchar(20), LastName nvarchar(20), GenderID int);
GO

INSERT INTO PersonsDetail VALUES(N'Gourav', N'Bhatia', 2),
              (N'Ramesh', N'Kumar', 1),
              (N'Ram', N'Lal', 2),
              (N'Sunil', N'Kumar', 3),
              (N'Sunny', N'Sehgal', 1),
              (N'Malkeet', N'Shaoul', 3),
              (N'Jassy', N'Sohal', 2);
GO

SELECT FirstName, LastName, Gender =
    CASE GenderID
    WHEN 1 THEN 'Male'
    WHEN 2 THEN 'Female'
    ELSE 'Unknown'
    END
FROM PersonsDetail

1
Per chiunque legga questa risposta: è la stessa come la risposta accettata da @ bob-prost sopra: stackoverflow.com/a/206500/264786 downvoted per questo motivo.
— ArturoTena,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.