SQL Switch / Case ในอนุประโยค 'where'


147

ฉันพยายามค้นหารอบ ๆ แต่ไม่พบสิ่งที่จะช่วยฉันได้

ฉันกำลังพยายามทำสิ่งนี้ใน 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

ฉันรู้ว่าฉันไม่ควรใส่ '= @locationID' ต่อท้ายแต่ละอัน แต่ฉันไม่สามารถรับไวยากรณ์ได้ใกล้เคียงกับความถูกต้อง SQL ยังคงบ่นเกี่ยวกับ '=' ของฉันในบรรทัดแรกเมื่อ ...

ฉันจะทำเช่นนี้ได้อย่างไร?

คำตอบ:


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
ดังที่ TomH ระบุไว้ในความคิดเห็นต่อคำตอบของคุณด้านล่างคุณสร้าง SQL ไม่ถูกต้อง ฉันทดสอบของฉันใน SQLServer 2005 และใช้งานได้ดี
Bob Probst

ทำไมต้องมี @ พวกเขาทำอะไร?
ธ เดช

2
@ ระบุตัวแปรใน t-sql โดยไม่มี @, @locationID จะถูกตีความเป็นชื่อคอลัมน์
Christian Sloper

71

ไม่มีคำสั่งกรณี ...

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
สิ่งนี้จะให้ผลลัพธ์ที่แตกต่างกันเล็กน้อยเนื่องจากคำสั่ง Case ออกหลังจากเงื่อนไขเป็นไปตามเงื่อนไข - แต่ไวยากรณ์ OR จะประเมินความเป็นไปได้ทั้งหมด
tember

1
@tember แม้ว่า SQL จะเป็นภาษาโพรซีเดอร์ แต่หาก OR แรกเป็นจริงนิพจน์ที่เหลือจะไม่ได้รับการประเมิน เนื่องจาก SQL เป็นภาษาประกาศคุณจะทราบได้อย่างไรว่า DBM จะประเมิน OR ทั้งหมด คำถามจริงใจฉันไม่เข้าใจ
ArturoTena

ใน SQL นิพจน์ที่เหลือจะได้รับการประเมินในไวยากรณ์ OR ลองสิ่งนี้ (มันจะไม่ให้ฉันใส่สัญลักษณ์ @ - คุณจะต้องแก้ไขถ้าคุณต้องการทดสอบ): ประกาศ var varchar (5) set var = '0' เลือก 2 / var โดยที่ var <> 0 หรือ ISNUMERIC (var) = 1. ฉันต้องการให้เงื่อนไขออกเนื่องจาก var IS เท่ากับ 0 แต่จะดำเนินต่อไปเพื่อตรวจสอบว่าเป็นตัวเลขหรือไม่ซึ่งเป็นดังนั้นคำสั่งจะส่งกลับข้อผิดพลาด
tember

สิ่งนี้ช่วยในกรณีของฉันที่ฉันมีตัวดำเนินการเปรียบเทียบที่แตกต่างกันสำหรับแต่ละค่าของประเภท
Alex

ยังดีกว่า 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
ลุ

36

จัดให้เลย

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
ฉันจะเขียนมันเป็น SELECT column1, column2 จากมุมมองไม่ว่าที่ไหน (@locationType = 'location' AND account_location = @locationID) หรือ (@locationType = 'area' และ xxx_location_area = @locationID) หรือ (@locationType = 'division' และ xxx_location_division = @locationID)
ม.ค. de Vos

2
นี่เป็นตัวอย่างความสงบที่ดีวิธีการเกี่ยวกับแผนการดำเนินการสืบค้นที่มีคำตอบที่ดีที่สุด (โดยส่วนตัวแล้วฉันชอบรหัสวิธีนี้ที่ชัดเจนและสะอาด)
PEO

1
ดีมากถ้าคุณต้องการใช้ที่แตกต่างกันโดยที่ clause กับประเภทที่แตกต่างกันเช่น int ในประโยคที่ 1 และ nvarchar ใน 2nd ด้วยโซลูชัน Bob Probst ไม่ทำงาน ขอบคุณ
Julian50

6

ฉันจะบอกว่านี่เป็นตัวบ่งชี้โครงสร้างตารางที่มีข้อบกพร่อง บางทีประเภทสถานที่ตั้งที่แตกต่างกันควรแยกออกจากกันในตารางที่แตกต่างกันทำให้คุณทำการสืบค้นได้มากขึ้นและหลีกเลี่ยงการมีคอลัมน์ที่ไม่จำเป็นอยู่รอบ ๆ

หากคุณไม่สามารถเปลี่ยนโครงสร้างได้สิ่งที่ต้องการด้านล่างอาจได้ผล:

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
    )

และอื่น ๆ ... เราไม่สามารถเปลี่ยนโครงสร้างของแบบสอบถามได้ทันที แต่เราสามารถแทนที่ได้โดยทำให้เพรดิเคตเท่ากับตัวมันเอง

แก้ไข: คำแนะนำข้างต้นดีกว่ามากเพียงแค่เพิกเฉยของฉัน


ฉันไม่คิดว่านี่เป็นโครงสร้างตารางที่มีข้อบกพร่อง ตารางถูกตั้งค่าด้วยวิธีนี้เพื่อให้มีการอ้างอิงตัวเองเพื่อให้มีความสัมพันธ์พ่อแม่และลูกไม่สิ้นสุด เชื่อฉันว่ามันเป็นไปตามวัตถุประสงค์ ฉันไม่คิดว่าฉันต้องการเปลี่ยนโครงสร้างตารางของฉันให้ใช้คำสั่งสวิตช์เท่านั้น มันสำคัญมาก
ไมล์

5

ปัญหาเกี่ยวกับสิ่งนี้คือเมื่อกลไก SQL ไปประเมินนิพจน์จะตรวจสอบส่วน FROM เพื่อดึงตารางที่เหมาะสมจากนั้นส่วน WHERE เพื่อให้เกณฑ์พื้นฐานบางอย่างดังนั้นจึงไม่สามารถประเมินเงื่อนไขแบบไดนามิกได้อย่างถูกต้องว่าจะต้องคอลัมน์ใด ตรวจสอบกับ

คุณสามารถใช้คำสั่ง WHERE เมื่อคุณกำลังตรวจสอบเกณฑ์ WHERE ในเพรดิเคตเช่น

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

ดังนั้นในกรณีเฉพาะของคุณคุณจะต้องใส่แบบสอบถามลงในขั้นตอนการจัดเก็บหรือสร้างแบบสอบถามแยกกันสามรายการ


5

หรือตัวดำเนินการสามารถเป็นทางเลือกของกรณีเมื่ออยู่ในสภาพ

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

โปรดลองใช้คำค้นหานี้ คำตอบสำหรับโพสต์ด้านบน:

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
สำหรับใครก็ตามที่อ่านสิ่งนี้ในอนาคต: เหมือนกับคำตอบที่ยอมรับจาก @ bob-prost ด้านบน: stackoverflow.com/a/206500/264786
ArturoTena

2

ลองสิ่งนี้:

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

1
โหวตลงเพราะฉันไม่เข้าใจว่าสิ่งนี้จะเลือกระหว่างสตริงที่กำหนดได้อย่างไร (เช่น 'location', 'area', 'division')
ArturoTena


0

ลองใช้แบบสอบถามนี้มันง่ายและมีประโยชน์มาก: พร้อมที่จะดำเนินการ!

USE tempdb
GO

IF NOT OBJECT_ID('Tempdb..Contacts') IS NULL
    DROP TABLE Contacts

CREATE TABLE Contacts(ID INT, FirstName VARCHAR(100), LastName VARCHAR(100))
INSERT INTO Contacts (ID, FirstName, LastName)
SELECT 1, 'Omid', 'Karami'
UNION ALL
SELECT 2, 'Alen', 'Fars'
UNION ALL
SELECT 3, 'Sharon', 'b'
UNION ALL
SELECT 4, 'Poja', 'Kar'
UNION ALL
SELECT 5, 'Ryan', 'Lasr'
GO
 
DECLARE @FirstName VARCHAR(100)
SET @FirstName = 'Omid'
 
DECLARE @LastName VARCHAR(100)
SET @LastName = '' 
 
SELECT FirstName, LastName
FROM Contacts
WHERE  
    FirstName = CASE
    WHEN LEN(@FirstName) > 0 THEN  @FirstName 
    ELSE FirstName 
    END
AND
    LastName = CASE
    WHEN LEN(@LastName) > 0 THEN  @LastName 
    ELSE LastName
    END
GO

-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
โหวตลงเนื่องจากคำตอบนี้ไม่เกี่ยวข้องกับคำถาม คำถามคือวิธีใช้ CASE กับส่วนคำสั่ง WHERE ไม่ใช่วิธีใช้ CASE ในคอลัมน์ที่เลือก
ArturoTena

-2

ลองใช้แบบสอบถามนี้ เข้าใจง่ายมาก:

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
สำหรับใครก็ตามที่อ่านคำตอบนี้: เหมือนกับคำตอบที่ยอมรับจาก @ bob-prost ด้านบน: stackoverflow.com/a/206500/264786 ลดลงด้วยเหตุผลนี้
ArturoTena
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.