Apa hasil yang benar untuk permintaan ini?

20

Saya menemukan puzzle ini di komentar di sini

CREATE TABLE r (b INT);

SELECT 1 FROM r HAVING 1=1;

SQL Server dan PostgreSQL mengembalikan 1 baris.

MySQL dan Oracle mengembalikan nol baris.

Yang mana yang benar? Atau keduanya sama-sama valid?

Martin Smith
sumber
Teka-teki yang bagus. Saya pikir yang benar adalah mengembalikan 1 baris. SQL-Server bertentangan sendiri karena SELECT COUNT(*) FROM r;mengembalikan 1 baris (dengan 0), sementara SELECT COUNT(*) FROM r GROUP BY ();tidak mengembalikan baris.
ypercubeᵀᴹ
1
Ingin lebih? SELECT 1 WHERE 1=0 HAVING 1=1;. SQL Server dan PostgreSQL masih mengembalikan satu baris. Oracle menginginkan FROM DUAL dan tidak mengembalikan baris. MySQL tidak mengkompilasi dengan FROM DUAL atau tanpa itu .
Andriy M
1
@AndriyM Untuk beberapa alasan yang tidak diketahui "dual" dan "HAVING" tidak berfungsi dengan baik di MySQL. (Temuan bagus). Tetapi yang setara berfungsi: SELECT 1 AS t FROM (SELECT 1) tmp WHERE 1=0 HAVING 1=1; 1-baris-tidak-ganda dan mengembalikan 0 baris.
ypercubeᵀᴹ
1
@SQLKiwi - Bagaimana dengan bagian ini dari spec. "Jika TE tidak segera mengandung <group by clause>, maka “GROUP BY ()”implisit." Tidakkah seharusnya kedua kueri mengembalikan hasil yang sama?
Martin Smith
1
Tapi tidak setuju pada ini (Oracle mengeksekusi pertanyaan dengan HAVINGberbeda): SQl-biola 2: MEMILIKI hal-hal yang berbeda
ypercubeᵀᴹ

Jawaban:

17

Sesuai standar:

SELECT 1 FROM r HAVING 1=1

cara

SELECT 1 FROM r GROUP BY () HAVING 1=1

Kutipan ISO / IEC 9075-2: 2011 7.10 Sintaks Aturan 1 (Bagian dari definisi klausa HAVING):

Biarkan HCsaja <having clause>. Biarkan TEmenjadi <table expression>yang berisi HC. Jika TEtidak segera mengandung a <group by clause>, maka " GROUP BY ()" adalah implisit. Membiarkan Tmenjadi deskriptor dari tabel yang didefinisikan oleh <group by clause> GBCsegera terkandung TEdan biarkan Rmenjadi hasil GBC.

Ok jadi itu cukup jelas.


Pernyataan: 1=1adalah kondisi pencarian yang sebenarnya. Saya tidak akan memberikan kutipan untuk ini.


Sekarang

SELECT 1 FROM r GROUP BY () HAVING 1=1

setara dengan

SELECT 1 FROM r GROUP BY ()

Kutipan ISO / IEC 9075-2: 2011 7.10 Aturan Umum 1:

The <search condition>dievaluasi untuk setiap kelompok R. Hasil dari <having clause>adalah tabel dikelompokkan dari kelompok-kelompok R yang hasilnya <search condition>benar.

Logika: Karena kondisi pencarian selalu benar, hasilnya adalah R, yang merupakan hasil grup dengan ekspresi.


Berikut ini adalah kutipan dari Peraturan Umum 7.9 (definisi GROUP BY CLAUSE)

1) Jika tidak <where clause>ditentukan, maka biarkan Tmenjadi hasil dari pendahulunya <from clause>; jika tidak, biarkan Tmenjadi hasil dari sebelumnya <where clause>.

2) Kasus:

a) Jika tidak ada kolom pengelompokan, maka hasil dari <group by clause>adalah tabel dikelompokkan terdiri dari Tsebagai satu-satunya grup.

Dengan demikian kita dapat menyimpulkan itu

FROM r GROUP BY ()

menghasilkan tabel yang dikelompokkan, terdiri dari satu grup, dengan nol baris (karena R kosong).


Kutipan dari Aturan Umum 7.12, yang mendefinisikan Spesifikasi Kueri (alias pernyataan SELECT):

1) Kasus:

a) Jika Tbukan tabel yang dikelompokkan, maka [...]

b) Jika Ttabel dikelompokkan, maka

Kasus:

i) Jika Tmemiliki 0 (nol) grup, maka biarkan TEMP menjadi tabel kosong.

ii) Jika Tmemiliki satu atau lebih kelompok, maka setiap <value expression>diterapkan untuk setiap kelompok Tmenghasilkan sebuah meja TEMPdari Mbaris, di mana Madalah jumlah kelompok di T. The ikolom -th TEMP mengandung nilai-nilai yang diperoleh evaluasi i-th <value expression>. [...]

2) Kasus:

a) Jika <set quantifier> DISTINCTtidak ditentukan, maka hasil dari <query specification>is TEMP.

Oleh karena itu karena tabel memiliki satu grup, itu harus memiliki satu baris hasil.

Jadi

SELECT 1 FROM r HAVING 1=1

harus mengembalikan set hasil 1 baris.

QED

Kevin Cathcart
sumber
+1 Terima kasih telah mengatasi semua masalah itu! Seperti @ypercube mengatakan SQL Server tampaknya bertentangan sendiri di sini sebagai SELECT 1 FROM r GROUP BY (); mengembalikan nol baris tetapi bagian yang Anda kutip tampaknya cukup jelas pada titik ini.
Martin Smith
Bolehkah saya bertanya di mana Anda menemukan standar? Jika Anda mengatakan 'di rak buku saya' saya akan kecewa :)
dezso
Secara teknis saya menggunakan Final Draft International Standard, daripada standar itu sendiri. Per aturan ISO / IEC hanya perubahan editorial (non-teknis) diizinkan antara FDIS dan standar akhir. Standar dimuntahkan menjadi beberapa bagian. Bagian 1 , Bagian 2 , Bagian 4 ...
Kevin Cathcart
Bagian 11 , dan Bagian 14 . Bagian 3,9,10, dan 13 Tidak diperbarui pada 2011, dan dengan demikian versi mereka sebelumnya berlaku. Tidak ada bagian 12. Demikian pula tidak ada bagian 5-8. Lihat halaman Wikipedia untuk Sql: 2011 atau Bagian 1 sendiri untuk penjelasan tentang apa yang berisi setiap bagian.
Kevin Cathcart
7

Ketika ada HAVINGklausa, tanpa WHEREklausa:

SELECT 1 FROM r HAVING 1=1;

... maka GROUP BY ()tersirat. Jadi, kueri harus sama dengan:

SELECT 1 FROM r GROUP BY () HAVING 1=1;

... yang seharusnya mengelompokkan semua baris tabel ke dalam satu grup (bahkan jika tabel tidak memiliki baris sama sekali - itu masih satu grup dari 0 baris) dan mengembalikan 1 baris. The HAVINGdengan Truekondisi seharusnya tidak berpengaruh sama sekali setelah itu.


Dari sudut pandang yang berbeda, berapa banyak baris yang harus kueri seperti ini kembali?

SELECT COUNT(*), MAX(b) FROM r;

Satu, nol atau "nol atau satu, tergantung pada apakah tabelnya kosong atau tidak"?

Saya pikir satu baris, tidak peduli berapa banyak baris yang rdimiliki.

ypercubeᵀᴹ
sumber
Masalah utamanya adalah apakah memang benar bahwa "meskipun tabel tidak memiliki baris sama sekali, itu masih satu grup dengan 0 baris". Dan standarnya ternyata eksplisit tentang ini: "Jika tidak ada kolom pengelompokan, maka ... adalah tabel yang dikelompokkan yang terdiri dari T sebagai satu-satunya grup". (dan itu berlaku bahkan jika T kosong - jadi memang ada grup.) Selanjutnya, klausa memiliki menentukan bahwa kondisi diterapkan ke masing-masing grup (dalam contoh demikian sekali). Mereka mungkin mendefinisikannya dengan cara ini untuk membuat SUM dan COUNT kembali satu baris bahkan untuk T kosong.
Erwin Smout
+1 (sebelumnya!) Meskipun logika Anda sama dengan Kevin, saya telah menerima jawabannya karena kutipan dari spec. Terima kasih!
Martin Smith
@MartinSmith. Thnx. Yang saya dapatkan dari menjadi malas :)
ypercubeᵀᴹ
@ ypercube: +1 dari saya juga. Saya memutuskan untuk mengambil waktu ekstra untuk menarik dari spec untuk membuktikan bahwa tidak ada kata musang tersembunyi di suatu tempat yang akan membuat jawaban Anda salah. Tapi begitu saya melakukan itu, saya mungkin mempostingnya sebagai jawaban lengkap. Jadi saya lakukan.
Kevin Cathcart
3
@ ErwinSmout: Tentu saja tidak. Namun ini termasuk dalam penggunaan yang wajar berdasarkan hukum hak cipta AS. Porsi yang relatif kecil, dikutip dalam konteks analisis (yaitu kritik) dari pekerjaan, untuk tujuan pendidikan, dengan dampak yang dapat diabaikan pada kemampuan pekerjaan untuk dijual.
Kevin Cathcart
3

Dari apa yang saya lihat, sepertinya SQLServer dan PostgerSQL tidak repot melihat ke dalam tabel sama sekali:

CREATE TABLE r (b INT);
insert into r(b) values (1);
insert into r(b) values (2);
SELECT 1 FROM r HAVING 1=1;

juga mengembalikan hanya satu baris. Meskipun kata SQLServer docs

Ketika GROUP BY tidak digunakan, HAVING berperilaku seperti klausa WHERE.

itu tidak benar dalam kasus ini - WHERE 1=1alih - alih HAVINGmengembalikan jumlah baris yang tepat. Saya akan mengatakan itu bug pengoptimal (atau setidaknya bug dokumentasi) ... Rencana SQLServer menunjukkan 'Pemindaian konstan' dalam kasus HAVINGdan 'pemindaian tabel' untuk WHERE...

Perilaku Oracle dan Mysql tampaknya lebih logis dan benar bagi saya ...

a1ex07
sumber
1
Anda benar bahwa SQL Server tidak melihat tabel. Rencana eksekusi hanya memiliki pemindaian konstan dan bahkan tidak merujuk tabel. Jika itu hanya SQL Server saya hanya akan meletakkannya ke bug tetapi karena bukan hanya SQL Server Saya bertanya-tanya apakah ada beberapa ambiguitas asli di sini.
Martin Smith
PostgreSQL menunjukkan hasil yang sama dengan SQLServer, dan sejauh yang saya tahu dari output explain"Hasil (baris = 1) ..." untuk memiliki dan "Pemindaian Seq" untuk "DIMANA" juga tidak melihat ke dalam tabel. .. Saya kira itu entah bagaimana terkait dengan fakta bahwa "FROM" tidak wajib dalam TSQL dan PostgreSQL. Saya tahu Mysql juga tidak memerlukannya, tetapi karena mereka mendukung dual, mereka mungkin menguraikan kueri yang sedikit berbeda. Saya setuju, ini terdengar seperti spekulasi, tapi saya harap ini masuk akal.
a1ex07