Mengapa kueri diuraikan sedemikian rupa sehingga melarang penggunaan alias kolom di sebagian besar klausa?


16

Saat mencoba menulis kueri, saya menemukan (cara yang sulit) bahwa SQL Server mem-parsing WHERE dalam permintaan jauh sebelum mem-parsing SELECT ketika menjalankan kueri.

Dokumen MSDN mengatakan bahwa urutan parsing logis umum adalah sedemikian rupa sehingga SELECT diurai hampir terakhir (sehingga mengakibatkan kesalahan "tidak ada objek [alias]" ketika mencoba menggunakan alias kolom dalam klausa lain). Bahkan ada saran untuk memungkinkan alias untuk digunakan di mana saja, yang ditembak jatuh oleh tim Microsoft, mengutip masalah kepatuhan standar ANSI (yang menunjukkan bahwa perilaku ini merupakan bagian dari standar ANSI).

Sebagai seorang programmer (bukan DBA), saya menemukan perilaku ini agak membingungkan, karena menurut saya itu sebagian besar mengalahkan tujuan memiliki alias kolom (atau, paling tidak, alias kolom dapat dibuat secara signifikan lebih kuat jika mereka diuraikan sebelumnya dalam eksekusi permintaan), karena satu-satunya tempat Anda benar-benar dapat menggunakan alias adalah dalam ORDER BY. Sebagai seorang programmer, sepertinya tidak ada peluang besar untuk membuat kueri lebih kuat, nyaman, dan KERING.

Sepertinya ini adalah masalah yang mencolok sehingga masuk akal, lalu, bahwa ada alasan lain untuk memutuskan bahwa alias kolom tidak boleh diizinkan selain SELECT dan ORDER BY, tapi apa alasannya?

Jawaban:


19

Ringkasan

Tidak ada alasan logis mengapa hal itu tidak dapat dilakukan, tetapi manfaatnya kecil dan ada beberapa perangkap yang mungkin tidak segera terlihat.

Hasil penelitian

Saya melakukan riset dan menemukan beberapa informasi yang baik. Berikut ini adalah kutipan langsung dari sumber utama yang dapat diandalkan (yang ingin tetap anonim) pada 2012-08-09 17:49 GMT:

Ketika SQL pertama kali ditemukan, ia tidak memiliki alias dalam klausa SELECT. Ini adalah kelemahan serius yang diperbaiki ketika bahasa tersebut dibakukan oleh ANSI pada sekitar tahun 1986.

Bahasa itu dimaksudkan untuk "non-prosedural" - dengan kata lain, untuk menggambarkan data yang Anda inginkan tanpa menentukan cara menemukannya. Jadi, sejauh yang saya tahu, tidak ada alasan mengapa implementasi SQL tidak dapat menguraikan seluruh permintaan sebelum memprosesnya, dan mengizinkan alias untuk didefinisikan di mana saja dan digunakan di mana-mana. Misalnya, saya tidak melihat alasan mengapa kueri berikut tidak valid:

select name, salary + bonus as pay
from employee
where pay > 100000

Meskipun saya pikir ini adalah permintaan yang masuk akal, beberapa sistem berbasis SQL dapat memperkenalkan pembatasan penggunaan alias untuk beberapa alasan terkait implementasi. Saya tidak terkejut mendengar bahwa SQL Server melakukan ini.

Saya tertarik dalam penelitian lebih lanjut tentang standar SQL-86 dan mengapa DBMS modern tidak mendukung alias digunakan kembali, tetapi belum punya waktu untuk melakukannya. Sebagai permulaan, saya tidak tahu di mana mendapatkan dokumentasi atau bagaimana mencari tahu siapa yang sebenarnya menjadi panitia. Adakah yang bisa membantu? Saya juga ingin tahu lebih banyak tentang produk Sybase asli yang berasal dari SQL Server.

Dari penelitian ini dan beberapa pemikiran lebih lanjut, saya menjadi curiga bahwa menggunakan alias di klausa lain, walaupun sangat mungkin, tidak pernah menjadi prioritas yang tinggi untuk produsen DBMS dibandingkan dengan fitur bahasa lainnya. Karena itu tidak terlalu banyak kendala, yang dengan mudah dikerjakan oleh penulis kueri, menempatkan upaya ke dalamnya atas kemajuan lain tidak optimal. Selain itu, itu akan menjadi hak milik karena jelas bukan bagian dari standar SQL (meskipun saya menunggu untuk mengetahui lebih lanjut tentang itu) dan dengan demikian akan menjadi perbaikan kecil, melanggar kompatibilitas SQL antara DBMSes. Sebagai perbandingan, CROSS APPLY(yang benar-benar tidak lebih dari tabel turunan yang memungkinkan referensi luar) adalah perubahan besar, bahwa sementara kepemilikan menawarkan kekuatan ekspresif yang luar biasa tidak mudah dilakukan dengan cara lain.

Masalah Dengan Menggunakan Alias ​​Di Mana Saja

Jika Anda mengizinkan item SELECT untuk dimasukkan ke dalam klausa WHERE, Anda tidak hanya bisa meledakkan kompleksitas kueri (dan dengan demikian kompleksitas menemukan rencana eksekusi yang baik) dimungkinkan untuk menghasilkan hal-hal yang sama sekali tidak masuk akal. Mencoba:

SELECT X + 5 Y FROM MyTable WHERE Y = X

Bagaimana jika MyTable sudah memiliki kolom Y, yang mana yang dimaksud dengan klausa WHERE? Solusinya adalah dengan menggunakan CTE atau tabel turunan, yang dalam kebanyakan kasus tidak memerlukan biaya tambahan tetapi mencapai hasil akhir yang sama. CTE dan tabel turunan setidaknya menegakkan resolusi ambiguitas dengan mengizinkan alias hanya digunakan satu kali.

Juga, tidak menggunakan alias dalam klausa FROM masuk akal. Anda tidak dapat melakukan ini:

SELECT
   T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
FROM
   Table1 T
   INNER JOIN Table2 T2
      ON T2.ID = CalcID
   INNER JOIN Table3 T3
      ON T2.ID = T3.ID

Itu referensi melingkar (dalam arti bahwa T2 diam-diam merujuk pada nilai dari T3, sebelum tabel itu disajikan dalam daftar BERGABUNG), dan sangat sulit untuk dilihat. Bagaimana dengan yang ini:

INSERT dbo.FinalTransaction
SELECT
   newid() FinalTransactionGUID,
   'GUID is: ' + Convert(varchar(50), FinalTransactionGUID) TextGUID,
   T.*
FROM
   dbo.MyTable T

Seberapa besar Anda ingin bertaruh bahwa fungsi newid () akan dimasukkan ke dalam rencana eksekusi dua kali, benar-benar secara tak terduga membuat dua kolom menunjukkan nilai yang berbeda? Bagaimana bila kueri di atas digunakan level N jauh di CTE atau tabel turunan. Saya jamin masalahnya lebih buruk dari yang bisa Anda bayangkan. Ada sudah masalah inkonsistensi serius ketika hal-hal yang dievaluasi hanya sekali atau pada titik apa dalam rencana permintaan, dan Microsoft telah mengatakan tidak akan memperbaikibeberapa di antaranya karena mereka mengekspresikan aljabar kueri dengan benar - jika seseorang mendapatkan hasil yang tidak terduga, pisahkan kueri menjadi beberapa bagian. Membiarkan referensi berantai, mendeteksi referensi melingkar melalui rantai yang berpotensi sangat lama – ini adalah masalah yang cukup rumit. Kenalkan paralelisme dan Anda akan mendapatkan mimpi buruk.

Catatan: Menggunakan alias di WHERE atau GROUP BY tidak akan membuat perbedaan pada masalah dengan fungsi seperti newid () atau rand ().

Cara SQL Server untuk membuat ekspresi yang dapat digunakan kembali

CROSS APPLY / OUTER APPLY adalah salah satu cara di SQL Server untuk membuat ekspresi yang dapat digunakan di tempat lain dalam kueri (tidak lebih awal dari klausa FROM):

SELECT
   X.CalcID
FROM
   Table1 T
   INNER JOIN Table3 T3
      ON T.ID = T3.ID
   CROSS APPLY (
      SELECT
         T3.ID + (SELECT Min(Interval) FROM Intervals WHERE IntName = 'T') CalcID
   ) X
   INNER JOIN Table2 T2
      ON T2.ID = X.CalcID

Ini melakukan dua hal:

  1. Membuat semua ekspresi di CROSS APPLY mendapatkan "namespace" (alias tabel, di sini, X) dan menjadi unik di dalam namespace itu.
  2. Menjadikannya jelas di mana-mana tidak hanya bahwa CalcID berasal dari X, tetapi juga menjelaskan mengapa Anda tidak dapat menggunakan apa pun dari X saat bergabung dengan tabel T1 dan T3, karena X belum diperkenalkan.

Saya sebenarnya cukup suka LINTAS BERLAKU. Itu telah menjadi teman saya yang setia, dan saya menggunakannya setiap saat. Perlu sebagian UNPIVOT (yang akan membutuhkan PIVOT / UNPIVOT atau UNPIVOT / PIVOT menggunakan sintaksis asli)? Dikerjakan dengan SALIB BERLAKU. Perlu nilai terhitung yang akan digunakan kembali berkali-kali? Selesai Perlu menerapkan perintah eksekusi secara kaku untuk panggilan melalui server yang ditautkan? Dilakukan dengan peningkatan kecepatan yang menjerit. Hanya perlu satu jenis baris dibagi menjadi 2 baris atau dengan kondisi tambahan? Selesai

Jadi, paling tidak, dalam DBMS SQL Server 2005 dan yang lebih tinggi, Anda tidak memiliki alasan lebih lanjut untuk komplain: CROSS BERLAKU adalah cara Anda KERING dengan cara yang Anda inginkan.


14

Saya tidak bisa memberi tahu Anda alasan yang tepat, tetapi saya akan memberi tahu Anda bahwa ada solusi untuk mengulangi ekspresi, misalnya menggunakan CTE, subkueri, tabel turunan, dll. Untuk menghindari pengulangan.

Jika Anda menampilkan kueri dengan ekspresi berulang, kami mungkin dapat menunjukkan kepada Anda cara menulisnya kembali sehingga ekspresi hanya terdaftar satu kali. Namun ini hanya mengurangi kompleksitas dalam menulis / membaca kueri, tidak mungkin banyak berubah tentang efisiensi. SQL Server umumnya cukup baik tentang mengenali bahwa ekspresi diulangi, dan itu tidak akan berfungsi dua kali. Ada pengecualian yang berlawanan, tetapi Anda hanya perlu khawatir tentang efisiensi ketika Anda benar-benar mengamati ini terjadi. Saya menduga sebagian besar ekspresi berulang yang Anda tulis benar-benar runtuh menjadi hanya satu operasi dalam rencana.

Itu semua mengatakan, saya juga akan mengulangi bagian dari jawaban saya dari pertanyaan ini:

/dba/19762/why-is-the-select-clause-listed-first


Berikut adalah penjelasan Joe Celko tentang bagaimana permintaan diproses sesuai dengan standar (saya mencuri ini dari artikel aspfaq.com saya sendiri , yang mencuri kutipan mungkin dari posting newsgroup oleh Celko):

Berikut adalah cara kerja SELECT dalam SQL ... setidaknya dalam teori. Produk nyata akan mengoptimalkan berbagai hal ketika mereka bisa.

Mulailah pada klausa FROM dan bangun tabel kerja dari semua gabungan, serikat pekerja, persimpangan, dan apa pun konstruktor tabel lainnya yang ada. Opsi AS memungkinkan Anda memberi nama ke tabel kerja ini yang kemudian harus Anda gunakan untuk sisa kueri yang berisi.

Buka klausa WHERE dan hapus baris yang tidak lulus kriteria; yaitu, yang tidak menguji ke TRUE (tolak UNKNOWN dan FALSE). Klausa WHERE diterapkan pada klausa FROM yang berfungsi.

Pergi ke klausa GROUP BY opsional, buat grup dan kurangi masing-masing grup menjadi satu baris, ganti tabel kerja asli dengan tabel grup yang baru. Baris dari tabel yang dikelompokkan harus karakteristik kelompok: (1) kolom pengelompokan (2) statistik tentang grup (yaitu fungsi agregat) (3) fungsi atau (4) ekspresi yang terdiri dari ketiga item.

Pergi ke klausa HAVING opsional dan terapkan pada tabel kerja yang dikelompokkan; jika tidak ada klausa GROUP BY, perlakukan seluruh tabel sebagai satu kelompok.

Buka klausa SELECT dan buat ekspresi dalam daftar. Ini berarti bahwa subqueries skalar, pemanggilan fungsi, dan ekspresi dalam SELECT dilakukan setelah semua klausa lainnya selesai. Operator AS juga dapat memberikan nama untuk ekspresi dalam daftar SELECT. Nama-nama baru ini muncul sekaligus, tetapi setelah klausa WHERE dieksekusi; Anda tidak dapat menggunakannya dalam daftar SELECT atau petunjuk WHERE karena alasan itu.

Ekspresi kueri bersarang mengikuti aturan pelingkupan biasa yang Anda harapkan dari bahasa terstruktur blok seperti C, Pascal, Algol, dll. Yaitu, kueri paling dalam dapat mereferensikan kolom dan tabel dalam kueri yang berisinya.

Ini berarti bahwa SELECT tidak dapat memiliki lebih banyak kolom daripada GROUP BY; tetapi tentu saja dapat memiliki lebih sedikit kolom.

Sekarang, Celko adalah salah satu kontributor utama untuk versi standar sebelumnya. Saya tidak tahu apakah Anda akan mendapatkan jawaban pasti untuk WHY?pertanyaan itu, kecuali untuk spekulasi. Dugaan saya adalah bahwa mendaftar operasi yang sebenarnya terlebih dahulu membuatnya sangat mudah bagi parser untuk mengetahui dengan tepat apa jenis operasi yang akan terjadi. Bayangkan gabungan 20-tabel yang bisa berakhir menjadi a SELECTatau UPDATEatau DELETE, dan ingat bahwa kode untuk mesin ini awalnya ditulis kembali pada hari-hari ketika penguraian string cukup mahal.

Perhatikan bahwa jika standar SQL didikte FROMuntuk didahulukan, vendor mungkin telah secara independen memutuskan untuk mengurai tata bahasa dalam urutan yang berbeda, sehingga masih mungkin tidak masuk akal untuk mengharapkan urutan klausa seperti yang ditulis untuk benar-benar mematuhi urutan pemrosesan 100% dari waktu.

Hal yang sama berlaku untuk hal-hal seperti CASE. Kami telah melihat skenario di sini di situs ini , misalnya, di mana mitos yang sebelumnya diyakini yang CASEselalu diproses secara berurutan dan korsleting, adalah salah. Dan ini meluas ke kepercayaan umum lainnya juga, seperti SQL Server mengevaluasi bergabung dalam urutan mereka ditulis, klausa hubung singkat WHEREdari kiri ke kanan , atau memproses CTE sekali atau dalam urutan tertentu bahkan jika mereka direferensikan beberapa kali. Produk bebas untuk mengoptimalkan sesuai keinginan mereka meskipun itu tidak mencerminkan dengan tepat bagaimana Anda telah menyatakan permintaan harus bekerja secara deklaratif.


2
Perhatikan juga bahwa kemampuan untuk menggunakan atau tidak menggunakan alias di bagian kueri yang berbeda diberlakukan oleh pengurai, bukan oleh pengoptimal atau mesin eksekusi. Bagaimana mesin mengeksekusi kueri sebenarnya tidak mencerminkan pembatasan yang memengaruhi sintaksis.
Aaron Bertrand

2

Di Entity SQL , Anda BISA menggunakan alias dari ekspresi di tempat lain di kueri dalam beberapa situasi:

select k1, count(t.a), sum(t.a)
from T as t
group by t.b + t.c as k1

Perhatikan bahwa di sini Anda HARUS mendefinisikan ekspresi dalam GROUP BYklausa untuk menggunakannya dalam SELECTklausa.

Jelas dimungkinkan untuk mengizinkan beberapa jenis alias-as-reusable-expression dalam query SQL.

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.