Apakah BENAR-BENAR mungkin bahwa pesanan tidak akan dijamin untuk tabel turunan berlebihan ini?

12

Saya menemukan pertanyaan ini di percakapan Twitter dengan Lukas Eder .

Meskipun perilaku yang benar adalah dengan menerapkan klausa ORDER BY pada kueri terluar, karena, di sini, kami tidak menggunakan DISTINCT, GROUP BY, JOIN, atau klausa WHERE lainnya dalam kueri terluar, mengapa RDBMS tidak akan meneruskan saja data yang masuk karena diurutkan berdasarkan permintaan dalam?

SELECT * 
FROM (
    SELECT * FROM table ORDER BY time DESC
) AS t

Saat menjalankan contoh ini di PostgreSQL, setidaknya, Anda mendapatkan Paket Eksekusi yang sama untuk kueri dalam dan contoh tabel turunan ini, serta set hasil yang sama.

Jadi, saya akan berasumsi bahwa Perencana hanya akan membuang permintaan terluar karena itu berlebihan atau hanya melewati hasil dari tabel dalam.

Adakah yang mengira ini bukan masalahnya?

Vlad Mihalcea
sumber
4
Perhatikan bahwa kueri Anda akan gagal di SQL Server karena pesanan oleh tidak diizinkan di dalam tabel turunan.
a_horse_with_no_name
Mengapa kamu begitu ragu? Mengapa Anda menganggap sesuatu? Ketika Anda menulis sebuah program yang memberi Anda pilihan, apakah Anda mengharapkan pengguna mengharapkan hal-hal tentang pilihan Anda? Baca tentang optimisasi / implementasi kueri & fisik.
philipxy
2
"Saya akan berasumsi bahwa Perencana hanya akan membuang permintaan terluar karena itu berlebihan atau hanya melewati hasil dari tabel dalam." Anda dapat dengan mudah berasumsi bahwa Perencana akan membuang klausa pemesanan pada permintaan dalam karena itu tidak ada artinya dalam konteks.
Wildcard
MariaDB, sekitar 2012, membahas masalah ini. Kurangnya innerORDER BYlead untuk pengoptimalan yang berbeda untuk maks groupwise .
Rick James
1
Sebenarnya, Anda tepat untuk Postgres.
Erwin Brandstetter

Jawaban:

20

Sebagian besar basis data cukup jelas tentang fakta bahwa suatu ORDER BYsubquery adalah:

  • Tidak diizinkan: Misalnya SQL Server, Sybase SQL Anywhere (kecuali dilengkapi dengan TOPatau OFFSET .. FETCH)
  • Tidak berarti: Misalnya PostgreSQL, DB2 (sekali lagi, kecuali dilengkapi dengan OFFSET .. FETCHatau LIMIT)

Berikut adalah contoh dari manual LU2 DB2 (tambang penekanan)

Klausa ORDER BY dalam subselect tidak memengaruhi urutan baris yang dikembalikan oleh kueri. Klausa ORDER BY hanya memengaruhi urutan baris yang dikembalikan jika ditentukan dalam pemilihan penuh terluar.

Kata-katanya cukup eksplisit, seperti halnya PostgreSQL :

Jika penyortiran tidak dipilih, baris akan dikembalikan dalam urutan yang tidak ditentukan. Urutan aktual dalam kasus itu akan tergantung pada pemindaian dan bergabung dengan jenis paket dan urutan pada disk, tetapi tidak boleh diandalkan . Pemesanan keluaran tertentu hanya dapat dijamin jika langkah sortir dipilih secara eksplisit.

Dari spesifikasi ini, dapat dipastikan bahwa setiap pemesanan yang dihasilkan dari ORDER BYklausa dalam tabel turunan hanyalah kebetulan dan mungkin secara kebetulan cocok dengan pemesanan yang Anda harapkan (yang dilakukannya di sebagian besar database dalam contoh sepele Anda), tetapi tidak bijaksana untuk mengandalkan ini.

Catatan tentang DB2:

Secara khusus, DB2 memiliki fitur yang kurang dikenalORDER BY ORDER OF <table-designator> , yang dapat digunakan sebagai berikut:

SELECT C1 FROM
   (SELECT C1 FROM T1
      UNION
    SELECT C1 FROM T2
    ORDER BY C1 ) AS UTABLE
ORDER BY ORDER OF UTABLE

Dalam kasus khusus ini, urutan tabel turunan dapat secara eksplisit digunakan kembali di paling luar SELECT

Catatan tentang Oracle:

Selama bertahun-tahun telah menjadi praktik di Oracle untuk mengimplementasikan OFFSETpagination menggunakan ROWNUM, yang dapat dihitung secara wajar hanya setelah memesan tabel turunan:

SELECT *
FROM (
  SELECT rownum AS rn, t.* -- ROWNUM here depends on the derived table's ordering
  FROM (
    SELECT * FROM table ORDER BY time DESC
  ) t
) t
WHERE rn BETWEEN 10 AND 20

Dapat diperkirakan bahwa setidaknya dengan adanya ROWNUMpermintaan, versi Oracle di masa depan tidak akan merusak perilaku ini agar tidak merusak hampir semua warisan Oracle SQL di luar sana, yang belum bermigrasi ke tempat yang jauh lebih diinginkan dan lebih disukai. OFFSET .. FETCHSintaks standar SQL yang dapat dibaca :

SELECT * FROM table ORDER BY time DESC OFFSET 10 ROWS FETCH NEXT 10 ROWS ONLY
Lukas Eder
sumber
Meaningless: E.g. PostgreSQLharus benar-benar: 'diandalkan', karena tidak berarti sesuatu. Baris diurutkan dalam kueri dalam, dan urutan itu disimpan dalam tingkat kueri luar kecuali diinstruksikan sebaliknya atau pemesanan ulang adalah tepat untuk operasi tambahan. Bahkan jika itu hanya detail implementasi, itu tidak berarti. Ini dapat digunakan untuk input yang diurutkan ke fungsi agregat. Manual bahkan mengisyaratkan sebanyak: Alternatively, supplying the input values from a sorted subquery will usually work.
Erwin Brandstetter
Kutipan yang Anda tambahkan untuk Postgres sebenarnya berlaku untuk kasus yang berbeda: kueri tanpa ORDER BYsama sekali.
Erwin Brandstetter
@ ErwinBrandstetter: Jangan ragu untuk menambahkan jawaban dengan detail itu. Saya pribadi tidak setuju bahwa detail implementasi sangat berarti. Baru hari ini, saya telah belajar bahwa di masa lalu, orang-orang mengandalkan Oracle selalu melakukan grup yang diurutkan berdasarkan operasi di Oracle 8i (saya percaya), ketika tiba-tiba, versi yang lebih baru memperkenalkan kelompok hash, yang mematahkan asumsi bahwa beberapa pemesanan bisa diandalkan. Dengan kata lain: Saya suka menuliskannya dengan kata-kata yang berani. Tidak ada artinya , daripada oh jika Anda tahu detail rumit dari versi xyz, Anda sebenarnya bisa ...
Lukas Eder
Saya sudah menambahkan jawaban. Apakah kami memilih untuk mengabaikan perilaku non-standar atau saran bagus lainnya yang kami miliki di samping pertanyaan: Apakah pesanan dijamin untuk permintaan yang diberikan? Ini untuk Postgres. Ini tidak (atau bahkan tidak berlaku) untuk RDBMS lainnya. Dan itu berlaku untuk semua versi Postgres yang ada, bukan hanya untuk versi xyz. Bahkan didokumentasikan (dengan reservasi). Kutipan Anda menyesatkan. Jika kita ingin mengabaikan perilaku non-standar kita bisa mulai dengan Oracle membuat kita percaya NULL dan string kosong itu sama. Juga ortogonal terhadap pertanyaan itu.
Erwin Brandstetter
@ ErwinBrandstetter: Menarik, terima kasih atas pembaruannya. Apakah jaminan ini yang Anda maksudkan didokumentasikan?
Lukas Eder
12

Iya. Tanpa ORDER BYklausa, urutan output tidak ditentukan dan perencana kueri berada dalam jangkauannya untuk mengasumsikan bahwa Anda mengetahui dan memahami hal ini.

Mungkin memutuskan bahwa karena kueri luar tidak menentukan pesanan, ia dapat menjatuhkan pemesanan dalam kueri batin untuk menghindari operasi pengurutan, terutama jika tidak ada indeks berkerumun atau tidak ada indeks sama sekali untuk mendukung pemesanan. Jika tidak sekarang dapat dilakukan di versi mendatang.

Jangan pernah mengandalkan perilaku yang tidak terdefinisi. Jika Anda membutuhkan pemesanan khusus, berikan ORDER BYklausa di tempat yang sesuai.

David Spillett
sumber
Saat mengujinya pada PostgreSQL, pengurutan dilakukan setelah pemindaian sekuensial karena saya tidak memiliki indeks apa pun pada kolom yang digunakan oleh ORDER BY. RDBMS mana yang menurut Anda akan melewati permintaan dalam ORDER OLEH?
Vlad Mihalcea
5
Saya tidak bisa mengatakan saya tahu apa yang akan , hanya saja mereka ada dan semuanya bebas melakukannya jika mereka mau - itu akan menjadi optimasi yang dapat diterima sesuai dengan standar umum dan spesifikasi produk. SQL Server akan menolak permintaan secara langsung (kecuali jika Anda memasukkan TOP 100%sehingga permintaan saat ini tidak portabel, jika itu menjadi prioritas untuk proyek Anda. Karena Postgres mematuhi pemesanan dalam permintaan dalam sekarang tidak menyiratkan itu akan selalu akan dilakukan di masa depan (atau bahwa versi yang lebih lama melakukannya, pada kenyataannya) sehingga Anda harus menghindari mengandalkan perilaku untuk berjaga-jaga.
David Spillett
1
@VladMihalcea DBMS yang "mengoptimalkan pergi" yang redundan ORDER BYadalah MariaDB: Mengapa ORDER BY dalam FROM Subquery Diabaikan?
ypercubeᵀᴹ
6

Ini adalah masalah dengan perilaku yang tidak terdefinisi - bekerja untuk Anda, bekerja untuk saya, memformat ulang HDD dalam prod;)

Kita dapat mengambil satu langkah mundur dan mengatakan bahwa dalam satu hal Anda benar - tidak ada alasan duniawi mengapa setiap RDBMS yang waras akan mengatur ulang baris-baris pada pemilihan dalam. Tapi itu tidak dijamin - artinya di masa depan mungkin ada alasan, dan vendor bebas untuk melakukannya. Berarti bahwa setiap kode yang bergantung pada perilaku ini adalah pada belas kasihan dari perubahan yang dapat dibuat oleh vendor yang mereka tidak wajib mempublikasikan, karena itu bukan merupakan perubahan yang melanggar dari POV API.

PaulJWilliams
sumber
2
Salah satu alasan mengapa ia dapat mengoptimalkan pesanan dengan cepat adalah kecepatan. Mengembalikan baris dalam urutan berbeda mungkin lebih efisien.
TomTom
2
Secara khusus, server dapat memanfaatkan paralelisme untuk membaca tabel. Jika itu terjadi, dan tidak perlu menegakkan perintah, Anda akan mendapatkan kembali baris namun utas membacanya. (SQL Server sebenarnya melakukan ini, sehingga SELECTtanpa ORDER BYbenar-benar tidak deterministik, dan bukan hanya dalam teori atau karena data berubah.)
Jeroen Mostert
@ JoeroenMostert: Perilaku yang tidak terdefinisi hanya menjadi lebih buruk. Apa yang terjadi jika itu rusak dan delta digunakan untuk mengindeks ke dalam array?
Joshua
2

Apakah BENAR-BENAR mungkin bahwa pesanan tidak akan dijamin untuk tabel turunan berlebihan ini?

Jawaban untuk semua versi Postgres yang saat ini ada (yang Anda uji) adalah: Tidak - untuk kueri khusus ini. Urutan pesanan dijamin.

Orang-orang SQL server akan merasa tidak nyaman dengan ini karena Microsoft bahkan tidak mengizinkan ORDER BYdalam subqueries. Urutan sortir dijamin untuk permintaan sederhana ini di Postgres. ORDER BYditerapkan dalam subquery, dan kueri luar tidak melakukan apa pun yang dapat mengubah urutan.

Manual ini bahkan mengisyaratkan sebanyak dalam bab Fungsi Agregat :

Atau, memasok nilai input dari subquery yang diurutkan biasanya akan berfungsi.

Perhatikan ini hanya benar sementara tingkat kueri luar tidak menambahkan operasi yang dapat mengubah urutan. Jadi itu hanya "dijamin" untuk kasus sederhana, dan itu tidak didukung oleh standar SQL. Postgres bebas untuk memesan ulang jika layak untuk operasi tambahan. Jika ragu tambahkan yang lain ORDER BYke luar SELECT. (Dalam hal ini bagian dalamnya ORDER BYakan menjadi noise yang berlebihan untuk permintaan sederhana ini.)

Erwin Brandstetter
sumber
Apakah benar ketika "table"itu bukan tabel dasar yang sederhana tetapi tampilan kompleks atau tabel dipartisi? Apakah benar ketika rencana juga dieksekusi secara paralel? Apakah benar di Postgres 10 juga? (Saya hanya bertanya, saya tidak yakin untuk jawaban dari semua pertanyaan ini.)
ypercubeᵀᴹ
@ ypercubeᵀᴹ: Saya belum menguji Postgres 10 untuk semua ini, tapi saya cukup yakin itu benar. Pesanan diterapkan dan tidak diubah dalam permintaan luar untuk kasus sederhana.
Erwin Brandstetter