นามแฝงอ้างอิง (คำนวณใน SELECT) ในส่วนคำสั่ง WHERE


130
SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
FROM Invoices
WHERE BalanceDue > 0 --error

ค่าที่คำนวณได้ 'BalanceDue' ที่ตั้งเป็นตัวแปรในรายการคอลัมน์ที่เลือกไม่สามารถใช้ใน WHERE clause ได้

มีวิธีที่จะทำได้หรือไม่? ในคำถามที่เกี่ยวข้องนี้ ( การใช้ตัวแปรใน MySQL Select Statment ใน Where Clause ) ดูเหมือนว่าคำตอบจะเป็นจริงไม่ใช่คุณเพียงแค่เขียนการคำนวณ ( และทำการคำนวณนั้นในแบบสอบถาม) สองครั้งไม่มี ซึ่งเป็นที่น่าพอใจ

คำตอบ:


237

คุณไม่สามารถอ้างอิงนามแฝงได้ยกเว้นใน ORDER BY เนื่องจาก SELECT เป็นประโยคสุดท้ายที่สองที่ถูกประเมิน การแก้ไขสองวิธี:

SELECT BalanceDue FROM (
  SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
  FROM Invoices
) AS x
WHERE BalanceDue > 0;

หรือเพียงแค่ทำซ้ำการแสดงออก:

SELECT (InvoiceTotal - PaymentTotal - CreditTotal) AS BalanceDue
FROM Invoices
WHERE  (InvoiceTotal - PaymentTotal - CreditTotal)  > 0;

ฉันชอบอันหลัง หากการแสดงออกมีความซับซ้อนมาก (หรือมีค่าใช้จ่ายในการคำนวณสูง) คุณควรพิจารณาคอลัมน์ที่คำนวณ (และอาจคงอยู่) แทนโดยเฉพาะอย่างยิ่งหากมีคำถามจำนวนมากอ้างถึงนิพจน์เดียวกันนี้

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

ต่อไปนี้เป็นตัวอย่างแบบสอบถาม 5 รายการที่ให้ผลการดำเนินการตามแผนเดียวกันทั้งหมด

SELECT LEN(name) + column_id AS x
FROM sys.all_columns
WHERE LEN(name) + column_id > 30;

SELECT x FROM (
SELECT LEN(name) + column_id AS x
FROM sys.all_columns
) AS x
WHERE x > 30;

SELECT LEN(name) + column_id AS x
FROM sys.all_columns
WHERE column_id + LEN(name) > 30;

SELECT name, column_id, x FROM (
SELECT name, column_id, LEN(name) + column_id AS x
FROM sys.all_columns
) AS x
WHERE x > 30;

SELECT name, column_id, x FROM (
SELECT name, column_id, LEN(name) + column_id AS x
FROM sys.all_columns
) AS x
WHERE LEN(name) + column_id > 30;

แผนผลลัพธ์สำหรับข้อความค้นหาทั้งห้ารายการ:

ป้อนคำอธิบายรูปภาพที่นี่


11
ว้าว. SQL Server นั้นฉลาดพอที่จะทำการคำนวณเพียงครั้งเดียวเท่านั้น
Alternatefaraz

5
ว้าวนี่เป็นคำตอบที่มีคุณภาพสูงมาก!
Siddhartha

ฉันต้องการเงื่อนไขเพิ่มเติมบางอย่างในคำสั่งรวมและนี่เป็นวิธีเดียวที่ฉันจะทำให้มันทำงานได้ ขอบคุณ!
Eric

1
@EricBurdo หากคุณกำลังใช้MERGEโปรดให้แน่ใจว่าคุณได้ดำเนินการทั้งหมดนี้เข้าบัญชี: ด้วยความระมัดระวังMERGE
Aaron Bertrand

11

คุณสามารถทำได้โดยใช้ cross apply

SELECT c.BalanceDue AS BalanceDue
FROM Invoices
cross apply (select (InvoiceTotal - PaymentTotal - CreditTotal) as BalanceDue) as c
WHERE  c.BalanceDue  > 0;

4

เป็นไปได้จริง ๆ ที่จะกำหนดตัวแปรที่สามารถใช้ได้ทั้งใน SELECT, WHERE และ clauses อื่น ๆ

การเข้าร่วมไขว้ไม่จำเป็นต้องมีการผูกที่เหมาะสมกับคอลัมน์ตารางที่อ้างถึงอย่างไรก็ตาม OUTER APPLY ทำ - และถือว่าเป็นโมฆะโปร่งใสมากขึ้น

SELECT
    vars.BalanceDue
FROM
    Entity e
OUTER APPLY (
    SELECT
        -- variables   
        BalanceDue = e.EntityTypeId,
        Variable2 = ...some..long..complex..expression..etc...
    ) vars
WHERE
    vars.BalanceDue > 0

รุ่งโรจน์เพื่อไซ Mehroz Alam

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.