SQL Switch / Case dalam klausa 'where'

146

Saya mencoba mencari di sekitar, tetapi saya tidak dapat menemukan apa pun yang dapat membantu saya.

Saya mencoba melakukan ini dalam 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

Saya tahu bahwa saya tidak harus meletakkan '= @locationID' di akhir masing-masing, tetapi saya tidak bisa mendapatkan sintaks yang hampir benar. SQL terus mengeluh tentang '=' saya di baris KETIKA pertama ...

Bagaimana saya bisa melakukan ini?

Miles
sumber

Jawaban:

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
sumber
1
Seperti yang dicatat TomH dalam komentar untuk balasan Anda di bawah ini, Anda salah membentuk SQL. Saya menguji milik saya di SQLServer 2005 dan bekerja dengan baik.
Bob Probst
Mengapa @ dibutuhkan? Apa yang mereka lakukan?
Tadej
2
@ menunjukkan variabel dalam t-sql, tanpa @, @locationID akan ditafsirkan sebagai nama kolom.
Christian Sloper
70

tanpa pernyataan kasus ...

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
sumber
2
Ini akan memberikan hasil yang sedikit berbeda ketika pernyataan Kasus keluar setelah suatu kondisi terpenuhi - tetapi sintaks OR akan mengevaluasi semua kemungkinan
tember
1
@tember Bahkan jika SQL adalah bahasa prosedural, jika OR pertama benar maka sisa ekspresi tidak dievaluasi. Karena SQL adalah bahasa deklaratif, bagaimana Anda tahu DBM akan mengevaluasi semua OR? Pertanyaan yang tulus, saya tidak mengerti.
ArturoTena
Dalam SQL sisa ekspresi tidak dievaluasi dalam sintaks OR. Coba ini (itu tidak akan membiarkan saya memasukkan simbol @ - Anda harus memperbaikinya jika Anda ingin mengujinya): menyatakan var varchar (5) set var = '0' pilih 2 / var mana var <> 0 atau ISNUMERIK (var) = 1. Saya ingin kondisi untuk keluar karena var IS sama dengan 0, tetapi berjalan ke depan untuk memeriksa apakah itu numerik, yang mana, dan karenanya pernyataan mengembalikan kesalahan.
tember
yang satu ini membantu dalam kasus saya di mana saya memiliki operator pembanding yang berbeda untuk setiap nilai tipe.
Alex
lebih baik lagi 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

Ini dia

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
sumber
5
Baiklah, saya akan menuliskannya sebagai SELECT column1, kolom2 DARI viewWhERE WHERE (@locationType = 'location' AND account_location = @locationID) ATAU (@locationType = 'area' DAN xxx_location_area = @locationID) ATAU (@locationType = 'pembagian' AND xxx_location_division = @locationID)
Jan de Vos
2
ini adalah contoh yang bagus, bagaimana dengan rencana eksekusi permintaan dengan Jawaban terbaik (secara pribadi saya lebih suka kode metode ini jelas dan bersih)
PEO
1
benar-benar hebat jika Anda ingin menggunakan klausa dimana berbeda dengan tipe yang berbeda seperti int pada klausa pertama dan nvarchar pada klausa kedua. dengan solusi Bob Probst tidak bekerja. terima kasih
Julian50
6

Saya akan mengatakan ini adalah indikator struktur tabel yang cacat. Mungkin tipe lokasi yang berbeda harus dipisahkan dalam tabel yang berbeda, memungkinkan Anda untuk melakukan lebih banyak permintaan yang lebih kaya dan juga menghindari adanya kolom yang berlebihan.

Jika Anda tidak dapat mengubah struktur, sesuatu seperti di bawah ini mungkin berfungsi:

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
    )

Dan seterusnya ... Kami tidak dapat mengubah struktur kueri dengan cepat, tetapi kami dapat menimpanya dengan membuat predikatnya seimbang.

EDIT: Saran di atas tentu saja jauh lebih baik, abaikan saja saran saya.

Mark S. Rasmussen
sumber
Saya tidak berpikir ini adalah struktur tabel yang cacat. Tabel diatur sedemikian rupa sehingga merupakan referensi sendiri untuk memiliki jumlah tak terbatas dari hubungan orangtua / anak. Percayalah, itu sengaja. Saya tidak berpikir saya ingin mengubah struktur tabel saya hanya menggunakan pernyataan switch. ini tidak begitu penting
Miles
5

Masalah dengan ini adalah bahwa ketika mesin SQL pergi untuk mengevaluasi ekspresi, ia memeriksa bagian FROM untuk menarik tabel yang tepat, dan kemudian bagian WHERE untuk memberikan beberapa kriteria dasar, sehingga tidak dapat dengan benar mengevaluasi kondisi dinamis di mana kolom untuk periksa.

Anda dapat menggunakan klausa WHERE ketika Anda memeriksa kriteria WHERE dalam predikat, seperti

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

jadi dalam kasus khusus Anda, Anda perlu memasukkan kueri ke dalam prosedur tersimpan atau membuat tiga kueri terpisah.

Dillie-O
sumber
5

ATAU operator dapat menjadi alternatif case ketika dalam kondisi dimana

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
sumber
3

Silakan coba kueri ini. Jawab untuk posting di atas:

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
sumber
2
Untuk siapa pun yang membaca ini di masa depan: itu sama dengan jawaban yang diterima dari @ bob-prost di atas: stackoverflow.com/a/206500/264786
ArturoTena
2

Coba ini:

WHERE (
    @smartLocationType IS NULL 
    OR account_location = (
         CASE
            WHEN @smartLocationType IS NOT NULL 
                 THEN @smartLocationType
            ELSE account_location 
         END
    )
)
shah134pk
sumber
1
Diturunkan karena saya tidak mengerti bagaimana ini dapat memilih antara string yang diberikan (yaitu 'lokasi', 'area', 'pembagian')
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
Darshan Balar
sumber
-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
sumber
4
Diturunkan karena penjawab ini tidak terkait dengan pertanyaan. Pertanyaannya adalah bagaimana menggunakan CASE pada klausa WHERE, bukan bagaimana menggunakan CASE pada kolom SELECTed.
ArturoTena
-2

Coba kueri ini. Sangat mudah dimengerti:

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
sumber
1
Bagi siapa pun yang membaca jawaban ini: itu sama dengan jawaban yang diterima dari @ bob-prost di atas: stackoverflow.com/a/206500/264786 Turun karena alasan ini.
ArturoTena