Mengapa MySQL mengizinkan HAVING untuk menggunakan alias SELECT?

14

Dalam SQL, sejauh yang saya tahu, urutan pemrosesan kueri logis, yang merupakan urutan interpretasi konseptual, dimulai dengan FROM dengan cara berikut:

  1. DARI
  2. DIMANA
  3. KELOMPOK OLEH
  4. MEMILIKI
  5. PILIH
  6. DIPESAN OLEH

Mengikuti daftar ini mudah untuk melihat mengapa Anda tidak dapat memiliki alias SELECT dalam klausa WHERE, karena alias belum dibuat. T-SQL (SQL Server) mengikuti ini dengan ketat dan Anda tidak dapat menggunakan alias SELECT sampai Anda telah melewati SELECT.

Tetapi dalam MySQL dimungkinkan untuk menggunakan alias SELECT dalam klausa HAVING meskipun harus (secara logis) diproses sebelum klausa SELECT. Bagaimana ini bisa terjadi?

Untuk memberi contoh:

SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING Amount>1;

Pernyataan ini tidak valid dalam T-SQL (karena HAVING mengacu pada alias SELECT Amount) ...

Msg 207, Level 16, State 1, Line 5
Invalid column name 'Amount'.

... tetapi berfungsi dengan baik di MySQL.

Berdasarkan ini, saya bertanya-tanya:

  • Apakah MySQL mengambil jalan pintas dalam aturan SQL untuk membantu pengguna? Mungkin menggunakan semacam pra-analisis?
  • Atau apakah MySQL menggunakan urutan interpretasi konseptual yang berbeda dari yang saya ikuti, meskipun semua RDBMS mengikuti?
Ohlin
sumber
1
Dugaan saya adalah, ini poin kedua Anda.
a_horse_with_no_name
3
Yah saya kira itu tidak menyebabkan ambiguitas atau kebingungan sampai mereka mendukung fungsi peringkat. Kemudian SELECT C, ROW_NUMBER() OVER (ORDER BY X) AS RN FROM T GROUP BY C HAVING RN = 1akan bermasalah sebagai ROW_NUMBERberjalan setelah ituHAVING
Martin Smith
Saya tidak yakin apa fungsi peringkat yang didukung oleh MySQL. Jika Anda ingin nomor baris Anda harus menciptakannya dengan cara ini: SELECT @rownum:=@rownum + 1 as row .... Mungkin alasan mengapa mereka mendukung alias SELECT adalah karena mereka dapat, karena fakta bahwa mereka tidak mendukung hal-hal yang membuat tidak mungkin ... siapa yang tahu? :)
Ohlin
Sebagaimana @MartinSmith menjelaskan, selama tidak ada fungsi jendela / peringkat, urutan logis eksekusi HAVINGdan SELECTklausa dapat dipertukarkan. Jadi, tidak ada ambiguitas dalam melakukan ini dan dapat menyederhanakan tampilan kode ketika ada ekspresi mengerikan di SELECT.
ypercubeᵀᴹ
Mudah-mudahan ini agak pada topik untuk mengatakan saya menjawab pertanyaan Di sini yang menikmati hasil lebih cepat (dengan distincts) ... dengan hasil Alias in the Havingyang sama meskipun Explain. Jadi beberapa variasi dengan Pengoptimal sedang terjadi.
Drew

Jawaban:

13

Nah ketika Anda memiliki pertanyaan semacam ini, sumber informasi IMHO terbaik adalah dokumentasi MySQL. Sekarang to the point. Ini adalah perilaku ekstensi MySql GROUP BYyang diaktifkan secara default.

Ekstensi MySQL ke GROUP BY
MySQL memperluas perilaku ini untuk mengizinkan penggunaan alias dalam klausa HAVING untuk kolom gabungan

Jika Anda menginginkan perilaku standar, Anda dapat menonaktifkan ekstensi ini dengan sql_mode ONLY_FULL_GROUP_BY

SET [SESSION | GLOBAL] sql_mode = ONLY_FULL_GROUP_BY;

Jika Anda mencoba menjalankan kueri yang disebutkan di atas dalam ONLY_FULL_GROUP_BYsql_mode Anda akan mendapatkan pesan kesalahan berikut:

Bidang Non-pengelompokan 'Jumlah' digunakan dalam klausa HAVING: SELECT YEAR (orderdate), COUNT (*) sebagai Jumlah FROM Orders GROUP DENGAN TAHUN (orderdate) HAVING Jumlah> 1

Ini demo SQLFiddle

Karena itu terserah Anda bagaimana mengkonfigurasi dan menggunakan instance MySQL Anda.

peterm
sumber
Anda benar tentang dokumentasinya. Saya hanya tidak pernah berpikir itu bisa ditulis dengan sangat jelas seperti yang Anda kutip di atas :) Terima kasih telah menemukannya ...
Ohlin
Jawaban ini tidak menjawab "Apakah MySQL melakukan pra-analisis atau MySQL menggunakan interpretasi konseptual yang berbeda?".
Pacerier
2
@Pacerier MySQL "melakukan pra-analisis," tentu saja, karena pengoptimal kueri mempertimbangkan semua aspek kueri sambil memilih apa yang menurutnya akan menjadi rencana kueri terbaik. Gagasan "interpretasi konseptual yang berbeda" mengkhianati kesalahpahaman tentang fakta bahwa server bebas untuk mengimplementasikan model konseptual dengan cara apa pun yang menghasilkan hasil yang valid. ORDER BY, misalnya, mungkin benar-benar ditangani jauh lebih awal daripada secara teoritis, jika pengoptimal menemukan bahwa baris pada awalnya dapat dibaca dalam urutan dari indeks yang sudah dalam urutan yang diinginkan.
Michael - sqlbot
4

Pertanyaan bagus.

Saya pikir Anda harus menjalankan querys ini

EXPLAIN SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING Amount>1;
SHOW WARNINGS;

dan periksa bagaimana kueri ditulis ulang. saya cukup yakin pengoptimal permintaan mengganti Jumlah dengan COUNT (*)

SELECT YEAR(orderdate), COUNT(*) as Amount
FROM Sales.Orders
GROUP BY YEAR(orderdate) 
HAVING COUNT(*)>1;

Seperti halnya dengan

select 
 *
from 
 test
where 
 id = 5 - 3

setelah query optimizer nya seperti ini.

select 
 test.id as 'id'
from 
 test
where 
 test.id = 2
Raymond Nijland
sumber