การรับ SELECT เพื่อส่งคืนค่าคงที่แม้ว่าแถวศูนย์ตรงกัน


15

พิจารณาข้อความสั่งที่เลือกนี้:

SELECT *, 
       1 AS query_id 
FROM players 
WHERE username='foobar';

มันจะส่งคืนคอลัมน์ที่query_idมีค่า1พร้อมกับคอลัมน์อื่น ๆ ของผู้เล่น

วิธีการหนึ่งที่จะทำให้ผลตอบแทน SQL ข้างต้นอย่างน้อยquery_idของ1แม้ว่าเลือกพบว่าไม่มีแถวจับคู่?

BTW คือ PostgreSQL 8.4

คำตอบ:


22
SELECT col1, 
       col2, 
       col3, 
       1 AS query_id 
FROM players 
WHERE username='foobar'
union all 
select null,
       null,
       null,
       1
where not exists (select 1 from players where username = 'foobar');

หรือเป็นทางเลือก ( อาจเร็วกว่าโดยไม่จำเป็นต้องเลือกครั้งที่สอง):

with qid (query_id) as (
   values (1)
) 
select p.*, 
       qid.query_id
from qid 
  left join players as p on (p.useranme = 'foobar');

คุณสามารถเขียนซ้ำด้านบนเพื่อเป็นตัวแทน "กระชับ" มากขึ้น:

select p.*, 
       qid.query_id
from (values (1)) as qid (query_id)
  left join players as p on (p.useranme = 'foobar');

แต่ฉันคิดว่า CTE ชัดเจน ( with...) นั้นอ่านง่ายกว่า (แม้ว่าจะอยู่ในสายตาของคนดูเสมอ)


1
ในการลองใช้ตัวอย่างแรกดูเหมือนว่าไม่จำเป็นต้องใช้คำหลักทั้งหมดหรือไม่
Nathanael Weiss

2
@NatWeiss: ถ้าคุณต้องการที่เฉพาะเจาะจงเพื่อที่คุณจะมีorder byการจัดหา อันที่สอง "สร้าง" ตารางเสมือนที่มีหนึ่งแถวและหนึ่งคอลัมน์และทำการรวมภายนอก (โดยไม่มีเงื่อนไขการเข้าร่วม "ของจริง") ดังนั้นคุณจะต้องกลับมาอย่างน้อยหนึ่งแถว การใช้select *รหัสการผลิตเป็นรูปแบบที่ไม่ดี อย่าทำมัน แสดงรายการคอลัมน์ที่คุณต้องการเสมอ select *ควรเท่านั้น ที่จะใช้ใน Ad-hoc แบบสอบถาม
a_horse_with_no_name

2
@NatWeiss: คุณหมายถึงอะไร "ไวยากรณ์สำรอง" สำหรับ "การรวมอื่น ๆ " และทำไมคุณถึงคิดว่าleft joinอ่านไม่ออก?
a_horse_with_no_name

2
@NatWeiss: การเข้าร่วมโดยนัยในส่วนคำสั่งเป็นรูปแบบการเข้ารหัสที่ไม่ดีและควรหลีกเลี่ยง มันสามารถนำไปสู่การเข้าร่วมคาร์ทีเซียนที่ไม่ต้องการโดยไม่ให้เกิดข้อผิดพลาด และมันแยกแนวคิดสองอย่าง (เชิงสัมพันธ์) ของการเข้าร่วมและการกรอง
a_horse_with_no_name

4
Re: ตัวดัดแปลง "all" ของประโยค "union" ไม่จำเป็น: UNION ALLบางครั้งอาจมีประสิทธิภาพมากกว่าUNIONเมื่อคุณบอกผู้วางแผนคิวรีอย่างชัดเจนว่าคุณคาดหวังว่าจะไม่มีแถวที่ซ้ำกันออกมาจากUNIONคิวรี ed หรือ มีคุณต้องการให้พวกเขาออก หากไม่มีALLตัวดัดแปลงจะถือว่าคุณต้องการลบแถวที่ซ้ำกัน (แต่ละอันเท่านั้นที่ส่งคืน) ซึ่งคล้ายกับDISTINCTคำหลักและเพื่อรับประกันว่าอาจต้องใช้ + สแกนซ้ำผลลัพธ์อีกครั้ง ดังนั้นใช้ALLกับUNIONถ้าคุณไม่ต้องการการทำสำเนาแถวซ้ำโดยเฉพาะ
David Spillett

7

หากคุณคาดหวังว่าจะได้แถวหนึ่งหรือศูนย์กลับมาแค่นี้ก็จะได้ผลเช่นกัน:

SELECT
  max(col1) col1,
  max(col2) col2, 
  1 AS query_id 
FROM
  players 
WHERE
  username='foobar';

นี่จะส่งคืนหนึ่งแถวที่มีค่าทั้งหมดที่มีค่าเป็น null ยกเว้น query_id หากไม่พบแถว


2
เคล็ดลับดี ข้อเสียเปรียบเพียงอย่างเดียวคือค่าสำหรับ col1 และ col2 อาจไม่ได้อยู่ในแถวเดียวกันหากมีมากกว่าหนึ่งเงื่อนไขที่ตรงกับusername = 'foobar'
a_horse_with_no_name

1
สามารถรวมกัน () สามารถใช้ในแบบนี้ได้หรือไม่?
Nathanael Weiss

1
รวมกันจะไม่สร้างแถวที่ไม่มีการฉายจากตาราง
David Aldridge

1
@a_horse_with_no_name ใช่แม้ว่าชื่อตารางและคอลัมน์จะแนะนำว่าเพรดิเคตนั้นอยู่บนคีย์ตัวเลือกสำหรับตารางดังนั้นศูนย์หรือหนึ่งแถวจะถูกฉาย
David Aldridge

3

การตีลังกาในช่วงสายที่นี่ แต่นี่เป็นไวยากรณ์ที่ใช้งานได้ (อย่างน้อยใน 9.2 ไม่ได้ลองรุ่นก่อนหน้านี้)

SELECT (COALESCE(a.*,b.*::players)).*
FROM ( SELECT col1,  col2,  col3, 1 AS query_id 
       FROM players WHERE username='foobar' ) a
RIGHT JOIN (select null col1, null col2, null col3, 1 col4) b
ON a.query_id = b.col4;

จะส่งคืนแถว "ว่าง" เท่านั้นหากเนื้อหาทั้งหมดของ "a" เป็นโมฆะ

สนุก. / bithead


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