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.