Mysql
 sql >> Teknologi Basis Data >  >> RDS >> Mysql

Berbeda vs Grup Oleh

Biasanya disarankan untuk menggunakan DISTINCT bukannya GROUP BY , karena itulah yang sebenarnya Anda inginkan, dan biarkan pengoptimal memilih rencana eksekusi "terbaik". Namun - tidak ada pengoptimal yang sempurna. Menggunakan DISTINCT pengoptimal dapat memiliki lebih banyak opsi untuk rencana eksekusi. Namun itu juga berarti bahwa ia memiliki lebih banyak opsi untuk memilih paket yang buruk .

Anda menulis bahwa DISTINCT kueri "lambat", tetapi Anda tidak memberi tahu angka apa pun. Dalam pengujian saya (dengan 10 kali lebih banyak baris di MariaDB 10.0.19 dan 10.3.13 ) DISTINCT kueri seperti (hanya) 25% lebih lambat (562ms/453ms). EXPLAIN hasilnya tidak membantu sama sekali. Itu bahkan "berbohong". Dengan LIMIT 100, 30 itu perlu membaca setidaknya 130 baris (itulah yang saya EXPLAIN sebenarnya menampilkan GROUP BY ), tetapi ini menunjukkan kepada Anda 65.

Saya tidak dapat menjelaskan perbedaan 25% dalam waktu eksekusi, tetapi tampaknya mesin melakukan pemindaian tabel/indeks lengkap dalam hal apa pun, dan mengurutkan hasilnya sebelum dapat melewati 100 dan memilih 30 baris.

Rencana terbaik mungkin adalah:

  • Baca baris dari idx_reg_date indeks (tabel A ) satu per satu dalam urutan menurun
  • Lihat apakah ada kecocokan di idx_order_id indeks (tabel B )
  • Lewati 100 baris yang cocok
  • Kirim 30 baris yang cocok
  • Keluar

Jika ada seperti 10% dari baris di A yang tidak memiliki kecocokan di B , rencana ini akan membaca kira-kira 143 baris dari A .

Hal terbaik yang bisa saya lakukan untuk memaksakan rencana ini adalah:

SELECT A.id
FROM `order` A
WHERE EXISTS (SELECT * FROM order_detail_products B WHERE A.id = B.order_id)
ORDER BY A.reg_date DESC
LIMIT 30
OFFSET 100

Kueri ini mengembalikan hasil yang sama dalam 156 mdtk (3 kali lebih cepat dari GROUP BY ). Tapi itu masih terlalu lambat. Dan mungkin masih membaca semua baris dalam tabel A .

Kami dapat membuktikan bahwa rencana yang lebih baik dapat eksis dengan trik subquery "kecil":

SELECT A.id
FROM (
    SELECT id, reg_date
    FROM `order`
    ORDER BY reg_date DESC
    LIMIT 1000
) A
WHERE EXISTS (SELECT * FROM order_detail_products B WHERE A.id = B.order_id)
ORDER BY A.reg_date DESC
LIMIT 30
OFFSET 100

Kueri ini dijalankan dalam "tidak ada waktu" (~ 0 ms) dan mengembalikan hasil yang sama pada data pengujian saya. Dan meskipun tidak 100% dapat diandalkan, ini menunjukkan bahwa pengoptimal tidak bekerja dengan baik.

Jadi apa kesimpulan saya:

  • Pengoptimal tidak selalu melakukan pekerjaan terbaik dan terkadang membutuhkan bantuan
  • Bahkan ketika kita tahu "rencana terbaik", kita tidak selalu bisa menjalankannya
  • DISTINCT tidak selalu lebih cepat dari GROUP BY
  • Bila tidak ada indeks yang dapat digunakan untuk semua klausa - semuanya menjadi sangat rumit

Skema pengujian dan data dummy:

drop table if exists `order`;
CREATE TABLE `order` (
  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,
  `reg_date` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  PRIMARY KEY (`id`),
  KEY `idx_reg_date` (`reg_date`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

insert into `order`(reg_date)
    select from_unixtime(floor(rand(1) * 1000000000)) as reg_date
    from information_schema.COLUMNS a
       , information_schema.COLUMNS b
    limit 218860;

drop table if exists `order_detail_products`;
CREATE TABLE `order_detail_products` (
  `id` int(10) unsigned NOT NULL AUTO_INCREMENT,
  `order_id` bigint(20) unsigned NOT NULL,
  `order_detail_id` int(11) NOT NULL,
  `prod_id` int(11) NOT NULL,
  PRIMARY KEY (`id`),
  KEY `idx_order_detail_id` (`order_detail_id`,`prod_id`),
  KEY `idx_order_id` (`order_id`,`order_detail_id`,`prod_id`)
) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;

insert into order_detail_products(id, order_id, order_detail_id, prod_id)
    select null as id
    , floor(rand(2)*218860)+1 as order_id
    , 0 as order_detail_id
    , 0 as prod_id
    from information_schema.COLUMNS a
       , information_schema.COLUMNS b
    limit 437320;

Permintaan:

SELECT DISTINCT A.id
FROM `order` A
JOIN order_detail_products B ON A.id = B.order_id
ORDER BY A.reg_date DESC
LIMIT 30 OFFSET 100;
-- 562 ms

SELECT A.id
FROM `order` A
JOIN order_detail_products B ON A.id = B.order_id
GROUP BY A.id
ORDER BY A.reg_date DESC
LIMIT 30 OFFSET 100;
-- 453 ms

SELECT A.id
FROM `order` A
WHERE EXISTS (SELECT * FROM order_detail_products B WHERE A.id = B.order_id)
ORDER BY A.reg_date DESC
LIMIT 30 OFFSET 100;
-- 156 ms

SELECT A.id
FROM (
    SELECT id, reg_date
    FROM `order`
    ORDER BY reg_date DESC
    LIMIT 1000
) A
WHERE EXISTS (SELECT * FROM order_detail_products B WHERE A.id = B.order_id)
ORDER BY A.reg_date DESC
LIMIT 30 OFFSET 100;
-- ~ 0 ms



  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Haruskah saya menggunakan satu pernyataan SQL Select besar atau beberapa pernyataan kecil?

  2. java.sql.SQLException:Tidak ada database yang dipilih - mengapa?

  3. Perbarui Data di Database MySQL

  4. Bagaimana membatasi permintaan SHOW TABLES

  5. MySQL Pilih 7 hari terakhir