SQL Switch / Case in 'where'-Klausel

146

Ich habe versucht, mich umzuschauen, aber ich konnte nichts finden, was mir helfen würde.

Ich versuche dies in SQL zu tun:

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

Ich weiß, dass ich nicht '= @locationID' am Ende jedes einzelnen setzen muss, aber ich kann die Syntax nicht annähernd korrekt wiedergeben. SQL beschwert sich immer wieder über mein '=' in der ersten WHEN-Zeile ...

Wie kann ich das machen?

Meilen
quelle

Antworten:

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
Bob Probst
quelle
1
Wie TomH im Kommentar zu Ihrer Antwort unten vermerkt hat, haben Sie die SQL falsch gebildet. Ich habe meine in SQLServer 2005 getestet und es hat gut funktioniert.
Bob Probst
Warum wird das @ benötigt? Was machen Sie?
Tadej
2
@ gibt Variablen in t-sql an, ohne das @ würde @locationID als Spaltenname interpretiert.
Christian Sloper
70

ohne fallaussage ...

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)
Lukek
quelle
2
Dies führt zu einem etwas anderen Ergebnis, wenn die Case-Anweisung beendet wird, nachdem eine Bedingung erfüllt ist - aber die OR-Syntax bewertet alle Möglichkeiten
12.
1
@tember Selbst wenn SQL eine prozedurale Sprache wäre, wird der Rest des Ausdrucks nicht ausgewertet, wenn das erste ODER wahr ist. Woher wissen Sie, dass der DBM alle ORs auswertet, da SQL eine deklarative Sprache ist? Aufrichtige Frage, ich verstehe nicht.
ArturoTena
In SQL wird der Rest des Ausdrucks in der ODER-Syntax ausgewertet. Versuchen Sie dies (ich darf das @ -Symbol nicht einfügen - Sie müssen es korrigieren, wenn Sie es testen möchten): deklarieren Sie var varchar (5) set var = '0' select 2 / var where var <> 0 or ISNUMERIC (var) = 1. Ich möchte, dass die Bedingung beendet wird, weil var gleich 0 ist, aber es wird geprüft, ob es numerisch ist, was es ist, und daher gibt die Anweisung einen Fehler zurück.
22.
Dies half in meinem Fall, in dem ich für jeden Wert des Typs einen anderen Vergleichsoperator hatte.
Alex
noch besser 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

Bitte schön.

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
Pittsburgh DBA
quelle
5
Nun, ich hätte das als SELECT Spalte1, Spalte2 FROM viewWhatever WHERE (@locationType = 'location' AND account_location = @locationID) ODER (@locationType = 'area' UND xxx_location_area = @locationID) ODER (@locationType = 'Division' geschrieben. AND xxx_location_division = @locationID)
Jan de Vos
2
Dies ist ein gutes Beispiel dafür, wie wäre es mit dem Abfrageausführungsplan mit der besten Antwort (ich persönlich bevorzuge, dass dieser Methodencode klar und sauber ist)
PEO
1
Wirklich großartig, wenn Sie eine andere where-Klausel mit einem anderen Typ verwenden möchten, z. B. int für die erste Klausel und nvarchar für die zweite. mit Bob Probst Lösung funktioniert nicht. danke
Julian50
6

Ich würde sagen, dies ist ein Indikator für eine fehlerhafte Tabellenstruktur. Möglicherweise sollten die verschiedenen Standorttypen in verschiedenen Tabellen getrennt werden, damit Sie viel umfangreichere Abfragen durchführen und überflüssige Spalten vermeiden können.

Wenn Sie die Struktur nicht ändern können, funktioniert möglicherweise Folgendes:

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
    )

Und so weiter ... Wir können die Struktur der Abfrage nicht im laufenden Betrieb ändern, aber wir können sie überschreiben, indem wir die Prädikate gleich machen.

EDIT: Die obigen Vorschläge sind natürlich viel besser, ignorieren Sie einfach meine.

Mark S. Rasmussen
quelle
Ich denke nicht, dass dies eine fehlerhafte Tabellenstruktur ist. Die Tabelle wurde so eingerichtet, dass es sich um Selbstreferenzen handelte, um eine unendliche Anzahl von Eltern-Kind-Beziehungen zu haben. Glauben Sie mir, es war absichtlich. Ich glaube nicht, dass ich meine Tabellenstruktur ändern möchte, um nur eine switch-Anweisung zu verwenden. Es ist nicht so wichtig
Meilen
5

Das Problem dabei ist, dass die SQL-Engine beim Auswerten des Ausdrucks den FROM-Teil überprüft, um die richtigen Tabellen abzurufen, und dann den WHERE-Teil, um einige Basiskriterien bereitzustellen, sodass eine dynamische Bedingung für die Spalte nicht ordnungsgemäß ausgewertet werden kann prüfen gegen.

Sie können eine WHERE-Klausel verwenden, wenn Sie die WHERE-Kriterien im Prädikat überprüfen, z

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

In Ihrem speziellen Fall müssen Sie die Abfrage in eine gespeicherte Prozedur stellen oder drei separate Abfragen erstellen.

Dillie-O
quelle
5

Der OP-Operator kann eine Alternative sein, wenn er sich in einem Zustand befindet

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
Atik Sarker
quelle
3

Bitte versuchen Sie diese Abfrage. Antwort auf den obigen Beitrag:

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
Durre Najaf
quelle
2
Für alle, die dies in Zukunft lesen: Es ist dasselbe wie die akzeptierte Antwort von @ bob-prost oben: stackoverflow.com/a/206500/264786
ArturoTena
2

Versuche dies:

WHERE (
    @smartLocationType IS NULL 
    OR account_location = (
         CASE
            WHEN @smartLocationType IS NOT NULL 
                 THEN @smartLocationType
            ELSE account_location 
         END
    )
)
shah134pk
quelle
1
Abgestimmt, weil ich nicht verstehe, wie dies zwischen den angegebenen Zeichenfolgen (dh "Standort", "Gebiet", "Teilung")
wählen kann
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
Darshan Balar
quelle
-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;
Kavitha Reddy
quelle
4
Abgestimmt, weil diese Antwort nicht mit der Frage zusammenhängt. Die Frage war, wie CASE in der WHERE-Klausel verwendet wird und nicht, wie CASE in einer SELECTed-Spalte verwendet wird.
ArturoTena
-2

Versuchen Sie diese Abfrage. Es ist sehr leicht zu verstehen:

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
Mike Clark
quelle
1
Für jeden, der diese Antwort liest: Sie entspricht der oben von @ bob-prost akzeptierten Antwort: stackoverflow.com/a/206500/264786 Aus diesem Grund abgelehnt.
ArturoTena