Mendapatkan SELECT untuk mengembalikan nilai konstan bahkan jika nol baris cocok

15

Pertimbangkan pernyataan pilih ini:

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

Ini mengembalikan kolom query_iddengan nilai 1bersama dengan kolom pemain lain.

Bagaimana cara membuat SQL di atas mengembalikan setidaknya query_iddari 1bahkan jika pilih tidak menemukan baris yang cocok?

BTW, ini PostgreSQL 8.4.

Nathanael Weiss
sumber

Jawaban:

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');

Atau sebagai alternatif ( mungkin lebih cepat karena tidak ada pilihan kedua yang diperlukan):

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

Anda dapat menulis ulang representasi di atas ke representasi yang lebih "kompak":

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

Tapi saya pikir CTE eksplisit ( with...) lebih mudah dibaca (meskipun itu selalu di mata yang melihatnya).

seekor kuda tanpa nama
sumber
1
Dalam mencoba contoh pertama, sepertinya SEMUA kata kunci tidak diperlukan?
Nathanael Weiss
2
@NatWeiss: jika Anda memerlukan pesanan tertentu, Anda harus menyediakan sebuah order by. Yang kedua "menciptakan" tabel virtual dengan tepat satu baris dan satu kolom dan melakukan penggabungan luar (tanpa kondisi gabungan "nyata"), sehingga Anda selalu mendapatkan kembali setidaknya satu baris. Menggunakan select *kode produksi adalah gaya yang buruk. Jangan lakukan itu. Selalu daftarkan kolom yang Anda butuhkan. select *seharusnya hanya digunakan dalam permintaan ad-hoc.
a_horse_with_no_name
2
@NatWeiss: "sintaks pengganti" apa untuk "gabungan lain" yang Anda maksud. Dan mengapa menurut Anda left jointidak dapat dibaca?
a_horse_with_no_name
2
@NatWeiss: bergabung secara implisit di mana klausa adalah gaya pengkodean yang buruk dan harus dihindari. Ini dapat menyebabkan bergabungnya kartesius yang tidak diinginkan tanpa memberi Anda kesalahan. Dan itu jelas memisahkan dua konsep (relasional) bergabung dan menyaring
a_horse_with_no_name
4
re: "semua" pengubah klausa "union" tidak diperlukan: UNION ALLkadang-kadang bisa lebih efisien daripada UNION, karena Anda secara eksplisit memberi tahu perencana kueri bahwa Anda berharap tidak ada baris duplikat yang keluar dari UNIONkueri ed atau jika ada yang ingin mereka menjadi output. Tanpa ALLpengubah itu diasumsikan Anda ingin duplikat menghapus baris (hanya satu dari masing-masing kembali) seperti dengan DISTINCTkata kunci, dan untuk menjamin bahwa itu mungkin perlu resor + menelusuri kembali hasil waktu tambahan. Jadi gunakan ALLdengan UNIONkecuali Anda secara khusus membutuhkan de-duplikasi baris output.
David Spillett
7

Jika Anda hanya mengharapkan satu atau nol baris kembali, maka ini juga akan berfungsi:

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

Ini akan mengembalikan satu baris dengan semua nilai memiliki null kecuali query_id jika tidak ada baris yang ditemukan.

David Aldridge
sumber
2
Trik yang bagus. Satu-satunya kelemahan adalah bahwa nilai-nilai untuk col1 dan col2 mungkin bukan milik baris yang sama, jika ada lebih dari satu yang cocok dengan kondisiusername = 'foobar'
a_horse_with_no_name
1
Bisakah coalesce () juga digunakan dalam mode ini?
Nathanael Weiss
1
Coalesce tidak akan menghasilkan baris di mana tidak ada yang diproyeksikan dari tabel.
David Aldridge
1
@a_horse_with_no_name ya, meskipun nama tabel dan kolom menunjukkan bahwa predikat ada pada kunci kandidat untuk tabel, jadi nol atau satu baris akan diproyeksikan.
David Aldridge
3

Ceroboh di sini terlambat, tapi di sini ada sintaks yang berfungsi (setidaknya di 9.2, belum mencoba versi sebelumnya).

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;

Hanya akan mengembalikan baris "kosong" jika seluruh konten "a" adalah nol.

Nikmati. / Bithead

Kirk Roybal
sumber
-1
select isnull(column,1) from table
pengguna86795
sumber
2
Ini tidak akan menghasilkan baris tambahan ketika kueri mengembalikan 0 baris.
ypercubeᵀᴹ