Apa urutan catatan default untuk pernyataan SELECT di MySQL?

66

Misalkan Anda memiliki tabel dan data berikut:

create table t (
    k int,
    v int,
    index k(k)
    ) engine=memory;

insert into t (k, v)
values (10, 1),
       (10, 2),
       (10, 3);

Saat mengeluarkan select * from t where k = 10tanpa order byklausa, bagaimana cara MySQL mengurutkan catatan secara default?

bunga aster
sumber

Jawaban:

75

Memposting ulang jawaban saya untuk pertanyaan serupa tentang SQL Server:

Di dunia SQL, pesanan bukan properti yang melekat dari sekumpulan data. Dengan demikian, Anda tidak mendapatkan jaminan dari RDBMS Anda bahwa data Anda akan kembali dalam urutan tertentu - atau bahkan dalam urutan yang konsisten - kecuali jika Anda meminta data Anda dengan klausa ORDER BY.

Jadi, untuk menjawab pertanyaanmu:

  • Namun MySQL memilah catatan yang diinginkan tanpa jaminan konsistensi.
  • Jika Anda ingin mengandalkan pesanan ini untuk apa pun, Anda harus menentukan pesanan yang Anda inginkan menggunakan ORDER BY. Melakukan hal lain berarti menyiapkan diri untuk kejutan yang tidak diinginkan.

Ini adalah properti dari semua SQL, bukan hanya MySQL. Teks yang relevan dalam spesifikasi SQL-92 adalah:

Jika <order by clause> tidak ditentukan, maka urutan baris Q bergantung pada implementasi.

Ada bit teks yang serupa dalam spesifikasi untuk kursor.

Nick Chammas
sumber
26

Urutan baris dengan tidak adanya ORDER BYklausa mungkin:

  • berbeda antara dua mesin penyimpanan;
  • jika Anda menggunakan mesin penyimpanan yang sama, ini mungkin berbeda antara dua versi dari mesin penyimpanan yang sama; Contoh di sini , gulir ke bawah ke "Urutan Baris".
  • jika versi mesin penyimpanan sama, tetapi versi MySQL berbeda, mungkin berbeda karena perubahan pengoptimal permintaan antara versi tersebut;
  • jika semuanya sama, bisa berbeda karena fase bulan dan itu OK.
Laurynas Biveinis
sumber
10

Penyisipan tidak teratur, kacau, pada saat kedatangan. Indeks yang dibuat memang memiliki urutan di mana elemen dimasukkan di lokasi yang tepat di daftar tertaut yang merupakan indeks. Pikirkan daftar triply linked untuk indeks, di mana Anda memiliki tautan bergerak maju dari satu elemen indeks ke yang berikutnya, tautan melihat ke belakang untuk tujuan traversal dan integritas, dan kemudian satu set petunjuk ke catatan aktual dalam tabel yang cocokkan dengan elemen yang diindeks yang dimaksud.

Data aktual, kacau dalam penyimpanan. Indeks yang terkait dengan data, dipesan dalam penyimpanan dan konstruksi. Tarik data yang sebenarnya, dipesan atau tidak, tergantung pada kueri yang terlibat.

James Pulley
sumber
4

Ketika datang ke mesin penyimpanan MEMORY , saya akan mengharapkan urutan dengan urutan penyisipan karena tata letak indeks default HASHbukan BTREEdan aspek tata letak indeks tidak digunakan. Karena Anda mengindeks k, dan k adalah nilai yang sama, semua kunci masuk ke ember hash yang sama . Karena tidak ada alasan untuk mengasumsikan kerumitan tambahan untuk mengisi hash bucket, urutan penyisipan paling masuk akal.

Saya mengambil tabel sampel dan data yang sama dan menjalankan 30 INSERTdetik dan saya mendapatkan ini:

mysql> use test
Database changed
mysql> drop table if exists t;
Query OK, 0 rows affected (0.00 sec)

mysql> create table t(k int, v int,index k(k)) engine=memory;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3);
Query OK, 30 rows affected (0.00 sec)
Records: 30  Duplicates: 0  Warnings: 0

mysql> select * from t;
+------+------+
| k    | v    |
+------+------+
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
+------+------+
30 rows in set (0.00 sec)

mysql>

Saya memutuskan untuk menguji menambahkan dua nilai berbeda untuk k: 10 dan 11 saya mendapatkan ini:

mysql> use test
Database changed
mysql> drop table if exists t;
Query OK, 0 rows affected (0.02 sec)

mysql> create table t(k int, v int,index k(k)) engine=memory;
Query OK, 0 rows affected (0.01 sec)

mysql> insert into t values
    -> (11, 1), (11, 2), (11, 3), (10, 1), (10, 2), (10, 3),
    -> (11, 1), (11, 2), (11, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3);
Query OK, 30 rows affected (0.00 sec)
Records: 30  Duplicates: 0  Warnings: 0

mysql> select * from t;
+------+------+
| k    | v    |
+------+------+
|   11 |    1 |
|   11 |    2 |
|   11 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   11 |    1 |
|   11 |    2 |
|   11 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
+------+------+
30 rows in set (0.00 sec)

mysql>

Sepertinya urutan penyisipan. k = 11 adalah hash kunci pertama kemudian 10. Bagaimana dengan menyisipkan 10 pertama alih-alih 11? Inilah yang saya dapat:

mysql> use test
Database changed
mysql> drop table if exists t;
Query OK, 0 rows affected (0.02 sec)

mysql> create table t(k int, v int,index k(k)) engine=memory;
Query OK, 0 rows affected (0.00 sec)

mysql> insert into t values
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3),
    -> (11, 1), (11, 2), (11, 3), (10, 1), (10, 2), (10, 3),
    -> (11, 1), (11, 2), (11, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3),
    -> (10, 1), (10, 2), (10, 3), (10, 1), (10, 2), (10, 3);
Query OK, 30 rows affected (0.00 sec)
Records: 30  Duplicates: 0  Warnings: 0

mysql> select * from t;
+------+------+
| k    | v    |
+------+------+
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   11 |    1 |
|   11 |    2 |
|   11 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   11 |    1 |
|   11 |    2 |
|   11 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
|   10 |    1 |
|   10 |    2 |
|   10 |    3 |
+------+------+
30 rows in set (0.00 sec)

mysql>

Dengan suara bulat !!! ORDER OF INSERTION adalah jawabannya.

Sidenotes menggunakan indeks untuk mesin penyimpanan MEMORY

Pencarian rentang untuk MEMORY akan memiliki kinerja yang mengerikan.

Saat membuat indeks, Anda dapat menentukan USING BTREEklausa bersama dengan definisi indeks. Ini akan meningkatkan hal-hal untuk kueri rentang.

Mencari baris tertentu akan menghasilkan hasil yang sama dalam kinerja baik dengan HASHatau BTREE.

UPDATE 2011-09-22 11:18 EDT

Saya belajar sesuatu yang menarik hari ini. Saya membaca tautan yang disediakan oleh @Laurynas Biveinis dari Percona: Tautan Percona mengatakan sesuatu tentang tabel MEMORY untuk MySQL 5.5.15 :

Pemesanan Baris

Dengan tidak adanya ORDER BY, catatan dapat dikembalikan dalam urutan yang berbeda dari implementasi MEMORY sebelumnya. Ini bukan bug. Aplikasi apa pun yang mengandalkan pesanan tertentu tanpa klausa ORDER BY dapat memberikan hasil yang tidak terduga. Pesanan khusus tanpa ORDER BY adalah efek samping dari mesin penyimpanan dan penerapan optimizer kueri yang mungkin dan akan berubah antara rilis MySQL kecil.

Ini adalah tautan yang baik untuk saya lihat hari ini. Jawaban yang saya berikan menunjukkan bahwa tabel yang saya muat diambil agar saya harapkan HARI INI di MySQL 5.5.12. Seperti yang baru saja ditunjukkan oleh Percona dan @Laurynas Biveinis , tidak ada jaminan dalam rilis kecil lainnya.

Jadi, daripada mencoba membela jawaban saya, saya lebih suka mempromosikan jawaban dari @Laurynas Biveinis karena ini adalah info terbaru. Kudos dan topi untuk @Laurynas Biveinis . Saya juga ingin mengucapkan terima kasih kepada @eevar dengan sopan menunjukkan tidak mempromosikan jawaban versi khusus untuk pertanyaan. Mereka berdua mendapatkan upvote saya hari ini.

RolandoMySQLDBA
sumber