Cara mengoptimalkan SELECT yang sangat lambat dengan LEFT JOINs di atas tabel besar

14

Saya googling, belajar sendiri & mencari solusi selama berjam-jam tetapi tidak berhasil. Saya menemukan beberapa pertanyaan serupa di sini tetapi tidak dalam kasus ini.

Meja saya:

  • orang (~ baris 10M)
  • atribut (lokasi, umur, ...)
  • tautan (M: M) antara orang dan atribut (~ baris 40M)

Dump penuh ~ 280MB

Situasi: Saya mencoba untuk memilih id semua orang ( person_id) dari beberapa lokasi ( location.attribute_value BETWEEN 3000 AND 7000), menjadi beberapa jenis kelamin ( gender.attribute_value = 1), lahir dalam beberapa tahun ( bornyear.attribute_value BETWEEN 1980 AND 2000) dan memiliki warna mata ( eyecolor.attribute_value IN (2,3)).

Ini adalah penyihir kueri saya memakan waktu 3 ~ 4 mnt. dan saya ingin mengoptimalkan:

SELECT person_id
FROM person
    LEFT JOIN attribute location ON location.attribute_type_id = 1 AND location.person_id = person.person_id
    LEFT JOIN attribute gender ON gender.attribute_type_id = 2 AND gender.person_id = person.person_id
    LEFT JOIN attribute bornyear ON bornyear.attribute_type_id = 3 AND bornyear.person_id = person.person_id
    LEFT JOIN attribute eyecolor ON eyecolor.attribute_type_id = 4 AND eyecolor.person_id = person.person_id
WHERE 1
    AND location.attribute_value BETWEEN 3000 AND 7000
    AND gender.attribute_value = 1
    AND bornyear.attribute_value BETWEEN 1980 AND 2000
    AND eyecolor.attribute_value IN (2,3)
LIMIT 100000;

Hasil:

+-----------+
| person_id |
+-----------+
|       233 |
|       605 |
|       ... |
|   8702599 |
|   8703617 |
+-----------+
100000 rows in set (3 min 42.77 sec)

Jelaskan diperpanjang:

+----+-------------+----------+--------+---------------------------------------------+-----------------+---------+--------------------------+---------+----------+--------------------------+
| id | select_type | table    | type   | possible_keys                               | key             | key_len | ref                      | rows    | filtered | Extra                    |
+----+-------------+----------+--------+---------------------------------------------+-----------------+---------+--------------------------+---------+----------+--------------------------+
|  1 | SIMPLE      | bornyear | range  | attribute_type_id,attribute_value,person_id | attribute_value | 5       | NULL                     | 1265229 |   100.00 | Using where              |
|  1 | SIMPLE      | location | ref    | attribute_type_id,attribute_value,person_id | person_id       | 5       | test1.bornyear.person_id |       4 |   100.00 | Using where              |
|  1 | SIMPLE      | eyecolor | ref    | attribute_type_id,attribute_value,person_id | person_id       | 5       | test1.bornyear.person_id |       4 |   100.00 | Using where              |
|  1 | SIMPLE      | gender   | ref    | attribute_type_id,attribute_value,person_id | person_id       | 5       | test1.eyecolor.person_id |       4 |   100.00 | Using where              |
|  1 | SIMPLE      | person   | eq_ref | PRIMARY                                     | PRIMARY         | 4       | test1.location.person_id |       1 |   100.00 | Using where; Using index |
+----+-------------+----------+--------+---------------------------------------------+-----------------+---------+--------------------------+---------+----------+--------------------------+
5 rows in set, 1 warning (0.02 sec)

Pembuatan profil:

+------------------------------+-----------+
| Status                       | Duration  |
+------------------------------+-----------+
| Sending data                 |  3.069452 |
| Waiting for query cache lock |  0.000017 |
| Sending data                 |  2.968915 |
| Waiting for query cache lock |  0.000019 |
| Sending data                 |  3.042468 |
| Waiting for query cache lock |  0.000043 |
| Sending data                 |  3.264984 |
| Waiting for query cache lock |  0.000017 |
| Sending data                 |  2.823919 |
| Waiting for query cache lock |  0.000038 |
| Sending data                 |  2.863903 |
| Waiting for query cache lock |  0.000014 |
| Sending data                 |  2.971079 |
| Waiting for query cache lock |  0.000020 |
| Sending data                 |  3.053197 |
| Waiting for query cache lock |  0.000087 |
| Sending data                 |  3.099053 |
| Waiting for query cache lock |  0.000035 |
| Sending data                 |  3.064186 |
| Waiting for query cache lock |  0.000017 |
| Sending data                 |  2.939404 |
| Waiting for query cache lock |  0.000018 |
| Sending data                 |  3.440288 |
| Waiting for query cache lock |  0.000086 |
| Sending data                 |  3.115798 |
| Waiting for query cache lock |  0.000068 |
| Sending data                 |  3.075427 |
| Waiting for query cache lock |  0.000072 |
| Sending data                 |  3.658319 |
| Waiting for query cache lock |  0.000061 |
| Sending data                 |  3.335427 |
| Waiting for query cache lock |  0.000049 |
| Sending data                 |  3.319430 |
| Waiting for query cache lock |  0.000061 |
| Sending data                 |  3.496563 |
| Waiting for query cache lock |  0.000029 |
| Sending data                 |  3.017041 |
| Waiting for query cache lock |  0.000032 |
| Sending data                 |  3.132841 |
| Waiting for query cache lock |  0.000050 |
| Sending data                 |  2.901310 |
| Waiting for query cache lock |  0.000016 |
| Sending data                 |  3.107269 |
| Waiting for query cache lock |  0.000062 |
| Sending data                 |  2.937373 |
| Waiting for query cache lock |  0.000016 |
| Sending data                 |  3.097082 |
| Waiting for query cache lock |  0.000261 |
| Sending data                 |  3.026108 |
| Waiting for query cache lock |  0.000026 |
| Sending data                 |  3.089760 |
| Waiting for query cache lock |  0.000041 |
| Sending data                 |  3.012763 |
| Waiting for query cache lock |  0.000021 |
| Sending data                 |  3.069694 |
| Waiting for query cache lock |  0.000046 |
| Sending data                 |  3.591908 |
| Waiting for query cache lock |  0.000060 |
| Sending data                 |  3.526693 |
| Waiting for query cache lock |  0.000076 |
| Sending data                 |  3.772659 |
| Waiting for query cache lock |  0.000069 |
| Sending data                 |  3.346089 |
| Waiting for query cache lock |  0.000245 |
| Sending data                 |  3.300460 |
| Waiting for query cache lock |  0.000019 |
| Sending data                 |  3.135361 |
| Waiting for query cache lock |  0.000021 |
| Sending data                 |  2.909447 |
| Waiting for query cache lock |  0.000039 |
| Sending data                 |  3.337561 |
| Waiting for query cache lock |  0.000140 |
| Sending data                 |  3.138180 |
| Waiting for query cache lock |  0.000090 |
| Sending data                 |  3.060687 |
| Waiting for query cache lock |  0.000085 |
| Sending data                 |  2.938677 |
| Waiting for query cache lock |  0.000041 |
| Sending data                 |  2.977974 |
| Waiting for query cache lock |  0.000872 |
| Sending data                 |  2.918640 |
| Waiting for query cache lock |  0.000036 |
| Sending data                 |  2.975842 |
| Waiting for query cache lock |  0.000051 |
| Sending data                 |  2.918988 |
| Waiting for query cache lock |  0.000021 |
| Sending data                 |  2.943810 |
| Waiting for query cache lock |  0.000061 |
| Sending data                 |  3.330211 |
| Waiting for query cache lock |  0.000025 |
| Sending data                 |  3.411236 |
| Waiting for query cache lock |  0.000023 |
| Sending data                 | 23.339035 |
| end                          |  0.000807 |
| query end                    |  0.000023 |
| closing tables               |  0.000325 |
| freeing items                |  0.001217 |
| logging slow query           |  0.000007 |
| logging slow query           |  0.000011 |
| cleaning up                  |  0.000104 |
+------------------------------+-----------+
100 rows in set (0.00 sec)

Struktur tabel:

CREATE TABLE `attribute` (
  `attribute_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `attribute_type_id` int(11) unsigned DEFAULT NULL,
  `attribute_value` int(6) DEFAULT NULL,
  `person_id` int(11) unsigned DEFAULT NULL,
  PRIMARY KEY (`attribute_id`),
  KEY `attribute_type_id` (`attribute_type_id`),
  KEY `attribute_value` (`attribute_value`),
  KEY `person_id` (`person_id`)
) ENGINE=MyISAM AUTO_INCREMENT=40000001 DEFAULT CHARSET=utf8;

CREATE TABLE `person` (
  `person_id` int(11) unsigned NOT NULL AUTO_INCREMENT,
  `person_name` text CHARACTER SET latin1,
  PRIMARY KEY (`person_id`)
) ENGINE=MyISAM AUTO_INCREMENT=20000001 DEFAULT CHARSET=utf8;

Permintaan telah dilakukan pada server virtual DigitalOcean dengan SSD dan RAM 1GB.

Saya berasumsi mungkin ada masalah dengan desain database. Apakah Anda punya saran untuk merancang situasi ini dengan lebih baik? Atau hanya untuk menyesuaikan pilih di atas?

Martin
sumber
4
Itulah harga yang Anda bayar untuk desain EAV. Anda mungkin ingin mencoba indeks gabungan padaattribute (person_id, attribute_type_id, attribute_value)
mustaccio
1
Saya akan mencoba menambahkan indeks ini: (attribute_type_id, attribute_value, person_id)dan (attribute_type_id, person_id, attribute_value)
ypercubeᵀᴹ
5
Dan gunakan InnoDB, buang MyISAM. Ini tahun 2015, MyiSAM sudah lama mati.
ypercubeᵀᴹ
2
Hal pertama - singkirkan bergabungnya LEFT, itu tidak berpengaruh ketika Anda menggunakan semua tabel dalam kondisi WHERE Anda, secara efektif mengubah semua gabungan menjadi gabungan INNER (pengoptimal harus dapat memahami dan mengoptimalkan itu, tetapi lebih baik tidak membuatnya lebih sulit ). Hal kedua - nonaktifkan cache permintaan kecuali Anda memiliki alasan kuat untuk menggunakannya (= Anda mengujinya dan mengukurnya membantu Anda)
jkavalik
2
OT: tidakkah aneh jika Anda menggunakan LIMIT dengan PESANAN kami OLEH? Ini akan mengembalikan beberapa 100000 baris acak?
ibre5041

Jawaban:

6

Pilih beberapa atribut untuk disertakanperson . Buat indeks dalam beberapa kombinasi - gunakan indeks komposit, bukan indeks satu kolom.

Itu pada dasarnya satu-satunya jalan keluar dari EAV-sucks-at-performance, yang mana Anda berada.

Berikut ini diskusi lebih lanjut: http://mysql.rjweb.org/doc.php/eav termasuk saran untuk menggunakan JSON dan bukan tabel nilai kunci.

Rick James
sumber
3

Tambahkan indeces ke attributeuntuk:

  • (person_id, attribute_type_id, attribute_value) dan
  • (attribute_type_id, attribute_value, person_id)

Penjelasan

Dengan desain Anda saat ini, EXPLAINmengharapkan permintaan Anda untuk memeriksa 1,265,229 * 4 * 4 * 4 = 80,974,656baris attribute. Anda dapat mengurangi jumlah ini dengan menambahkan indeks komposit pada attributeuntuk (person_id, attribute_type_id). Dengan menggunakan indeks ini, kueri Anda hanya akan memeriksa 1 daripada 4 baris untuk masing-masing location, eyecolordan gender.

Anda bisa memperpanjang indeks yang meliputi attribute_type_valuejuga: (person_id, attribute_type_id, attribute_value). Ini akan mengubah indeks ini menjadi indeks penutup untuk permintaan ini, yang juga akan meningkatkan kinerja.

Lebih jauh lagi, menambahkan indeks pada (attribute_type_id, attribute_value, person_id)(lagi-lagi indeks penutup dengan memasukkan person_id) akan meningkatkan kinerja dibandingkan hanya menggunakan indeks di attribute_valuemana lebih banyak baris harus diperiksa. Dalam hal ini akan mempercepat langkah pertama dalam penjelasan Anda: memilih rentang dari bornyear.

Menggunakan dua indeces tersebut menurunkan waktu eksekusi permintaan Anda di sistem saya dari ~ 2.0 s ke ~ 0.2 s dengan output yang dijelaskan seperti ini:

+----+-------------+----------+--------+-------------------------------------+-------------------+---------+--------------------------------+---------+----------+--------------------------+
| id | select_type | table    | type   | possible_keys                       | key               | key_len | ref                            |    rows | filtered | Extra                    |
+----+-------------+----------+--------+-------------------------------------+-------------------+---------+--------------------------------+---------+----------+--------------------------+
|  1 | SIMPLE      | bornyear | range  | person_type_value,type_value_person | type_value_person |       9 |                                | 1861881 |   100.00 | Using where; Using index |
|  1 | SIMPLE      | location | ref    | person_type_value,type_value_person | person_type_value |       8 | bornyear.person_id,const       |       1 |   100.00 | Using where; Using index |
|  1 | SIMPLE      | eyecolor | ref    | person_type_value,type_value_person | person_type_value |       8 | bornyear.person_id,const       |       1 |   100.00 | Using where; Using index |
|  1 | SIMPLE      | gender   | ref    | person_type_value,type_value_person | person_type_value |      13 | bornyear.person_id,const,const |       1 |   100.00 | Using index              |
|  1 | SIMPLE      | person   | eq_ref | PRIMARY                             | PRIMARY           |       4 | bornyear.person_id             |       1 |   100.00 | Using index              |
+----+-------------+----------+--------+-------------------------------------+-------------------+---------+--------------------------------+---------+----------+--------------------------+
wolfgangwalther
sumber
1
Terima kasih atas jawaban & penjelasannya yang luas. Saya melakukan semua yang Anda sebutkan tetapi kueri masih memakan waktu ~ 2 menit. Tolong, tipe tabel apa (innodb, myisam) yang Anda gunakan dan apa query yang dilakukan?
Martin
1
Selain menambahkan indeces saya menggunakan data yang sama persis dan definisi yang Anda lakukan, jadi saya menggunakan MyISAM. Saya mengubah baris pertama dari kueri Anda SELECT person.person_idkarena jika tidak maka tidak akan berjalan. Apakah Anda melakukannya ANALYZE TABLE attributesetelah menambahkan indeces? Anda mungkin ingin menambahkan EXPLAINoutput baru Anda (setelah menambahkan indeces) ke pertanyaan Anda juga.
wolfgangwalther
3

Saya berasumsi mungkin ada masalah dengan desain database.

Anda menggunakan desain Entity-Attribute-Value, yang sering berkinerja buruk, baik, menurut desain.

Apakah Anda punya saran untuk merancang situasi ini dengan lebih baik?

Cara relasional klasik untuk mendesain ini akan membuat tabel terpisah untuk setiap atribut. Secara umum, Anda dapat memiliki ini tabel terpisah: location, gender, bornyear, eyecolor.

Berikut ini tergantung pada apakah atribut tertentu selalu ditentukan untuk seseorang, atau tidak. Dan, apakah seseorang hanya dapat memiliki satu nilai atribut. Misalnya, biasanya orang tersebut hanya memiliki satu jenis kelamin. Dalam desain Anda saat ini, tidak ada yang menghentikan Anda untuk menambahkan tiga baris untuk orang yang sama dengan nilai berbeda untuk gender di dalamnya. Anda juga dapat menetapkan nilai gender bukan ke 1 atau 2, tetapi ke beberapa nomor yang tidak masuk akal, seperti 987 dan tidak ada kendala dalam database yang akan mencegahnya. Namun, ini adalah masalah terpisah lainnya dalam menjaga integritas data dengan desain EAV.

Jika Anda selalu mengetahui jenis kelamin orang tersebut, maka tidak masuk akal untuk meletakkannya di tabel terpisah dan itu jauh lebih baik untuk memiliki kolom non-nol GenderIDdi dalam persontabel, yang akan menjadi kunci asing ke tabel pencarian dengan daftar semua jenis kelamin yang mungkin dan nama mereka. Jika Anda paling sering mengetahui jenis kelamin orang tersebut, tetapi tidak selalu, Anda dapat membuat kolom ini dapat dibatalkan dan atur NULLketika informasi tidak tersedia. Jika sebagian besar waktu jenis kelamin seseorang tidak diketahui, maka mungkin lebih baik untuk memiliki tabel terpisah genderyang terhubung ke person1: 1 dan memiliki baris hanya untuk orang-orang yang memiliki jenis kelamin yang diketahui.

Pertimbangan serupa berlaku untuk eyecolordan bornyear- orang tersebut tidak mungkin memiliki dua nilai untuk suatu eyecoloratau bornyear.

Jika mungkin bagi seseorang untuk memiliki beberapa nilai untuk suatu atribut, maka Anda pasti akan meletakkannya di tabel terpisah. Misalnya, tidak jarang seseorang memiliki beberapa alamat (rumah, kantor, pos, liburan, dll.), Jadi Anda akan mencantumkan semuanya dalam sebuah tabel location. Tabel persondan locationakan dihubungkan 1: M.


Atau hanya untuk menyesuaikan pilih di atas?

Jika menggunakan desain EAV, maka saya setidaknya akan melakukan hal berikut.

  • Set kolom attribute_type_id, attribute_value, person_iduntuk NOT NULL.
  • Siapkan kunci asing yang terhubung attribute.person_iddengan person.person_id.
  • Buat satu indeks pada tiga kolom (attribute_type_id, attribute_value, person_id). Urutan kolom penting di sini.
  • Sejauh yang saya tahu, MyISAM tidak menghormati kunci asing, jadi jangan gunakan itu, gunakan InnoDB sebagai gantinya.

Saya akan menulis kueri seperti ini. Gunakan INNERalih-alih LEFTbergabung dan secara eksplisit menulis subquery untuk setiap atribut untuk memberikan optimizer semua peluang untuk menggunakan indeks.

SELECT person.person_id
FROM
    person
    INNER JOIN
    (
        SELECT attribute.person_id
        FROM attribute
        WHERE attribute_type_id = 1
            AND location.attribute_value BETWEEN 3000 AND 7000
    ) AS location ON location.person_id = person.person_id
    INNER JOIN
    (
        SELECT attribute.person_id
        FROM attribute
        WHERE attribute_type_id = 2
            AND location.attribute_value = 1
    ) AS gender ON gender.person_id = person.person_id
    INNER JOIN
    (
        SELECT attribute.person_id
        FROM attribute
        WHERE attribute_type_id = 3
            AND location.attribute_value BETWEEN 1980 AND 2000
    ) AS bornyear ON bornyear.person_id = person.person_id
    INNER JOIN
    (
        SELECT attribute.person_id
        FROM attribute
        WHERE attribute_type_id = 4
            AND location.attribute_value IN (2, 3)
    ) AS eyecolor ON eyecolor.person_id = person.person_id
LIMIT 100000;

Juga, mungkin patut partisi yang attributetable oleh attribute_type_id.

Vladimir Baranov
sumber
Hati-hati kinerja: JOIN ( SELECT ... )tidak mengoptimalkan dengan baik. JOINinglangsung ke meja bekerja lebih baik (tetapi masih bermasalah).
Rick James
2

Saya harap saya menemukan solusi yang memadai. Itu terinspirasi oleh artikel ini .

Jawaban singkat:

  1. Saya telah membuat 1 tabel dengan semua atribut. Satu kolom untuk satu atribut. Ditambah kolom kunci utama.
  2. Nilai atribut disimpan dalam sel teks (untuk pencarian teks lengkap) dalam format seperti CSV.
  3. Membuat indeks teks lengkap. Sebelum itu penting untuk mengatur ft_min_word_len=1(untuk MyISAM) di [mysqld]bagian dan innodb_ft_min_token_size=1(untuk InnoDb) dalam my.cnffile, restart layanan mysql.
  4. Mencari contoh: SELECT * FROM person_index WHERE MATCH(attribute_1) AGAINST("123 456 789" IN BOOLEAN MODE) LIMIT 1000di mana 123, 456sebuah 789adalah ID yang orang seharusnya terkait dalam attribute_1. Permintaan ini berlangsung kurang dari 1 detik.

Jawaban terinci:

Langkah 1. Membuat tabel dengan indeks teks lengkap. InnoDb mendukung indeks teks lengkap dari MySQL 5.7 jadi jika Anda menggunakan 5.5 atau 5.6, Anda harus menggunakan MyISAM. Terkadang bahkan lebih cepat untuk pencarian FT daripada InnoDb.

CREATE TABLE `person_attribute_ft` (
  `person_id` int(11) NOT NULL,
  `attr_1` text,
  `attr_2` text,
  `attr_3` text,
  `attr_4` text,
  PRIMARY KEY (`person_id`),
  FULLTEXT KEY `attr_1` (`attr_1`),
  FULLTEXT KEY `attr_2` (`attr_2`),
  FULLTEXT KEY `attr_3` (`attr_3`),
  FULLTEXT KEY `attr_4` (`attr_4`),
  FULLTEXT KEY `attr_12` (`attr_1`,`attr_2`)
) ENGINE=MyISAM DEFAULT CHARSET=utf8

Langkah 2. Masukkan data dari tabel EAV (entitas-atribut-nilai). Misalnya dinyatakan dalam pertanyaan itu dapat dilakukan dengan 1 SQL sederhana:

INSERT IGNORE INTO `person_attribute_ft`
SELECT
    p.person_id,
    (SELECT GROUP_CONCAT(a.attribute_value SEPARATOR ' ') FROM attribute a WHERE a.attribute_type_id = 1 AND a.person_id = p.person_id LIMIT 10) attr_1,
    (SELECT GROUP_CONCAT(a.attribute_value SEPARATOR ' ') FROM attribute a WHERE a.attribute_type_id = 2 AND a.person_id = p.person_id LIMIT 10) attr_2,
    (SELECT GROUP_CONCAT(a.attribute_value SEPARATOR ' ') FROM attribute a WHERE a.attribute_type_id = 3 AND a.person_id = p.person_id LIMIT 10) attr_3,
    (SELECT GROUP_CONCAT(a.attribute_value SEPARATOR ' ') FROM attribute a WHERE a.attribute_type_id = 4 AND a.person_id = p.person_id LIMIT 10) attr_4
FROM person p

Hasilnya harus seperti ini:

mysql> select * from person_attribute_ft limit 10;
+-----------+--------+--------+--------+--------+
| person_id | attr_1 | attr_2 | attr_3 | attr_4 |
+-----------+--------+--------+--------+--------+
|         1 | 541    | 2      | 1927   | 3      |
|         2 | 2862   | 2      | 1939   | 4      |
|         3 | 6573   | 2      | 1904   | 2      |
|         4 | 2432   | 1      | 2005   | 2      |
|         5 | 2208   | 1      | 1995   | 4      |
|         6 | 8388   | 2      | 1973   | 1      |
|         7 | 107    | 2      | 1909   | 4      |
|         8 | 5161   | 1      | 2005   | 1      |
|         9 | 8022   | 2      | 1953   | 4      |
|        10 | 4801   | 2      | 1900   | 3      |
+-----------+--------+--------+--------+--------+
10 rows in set (0.00 sec)

Langkah 3. Pilih dari tabel dengan kueri seperti ini:

mysql> SELECT SQL_NO_CACHE *
    -> FROM `person_attribute_ft`
    -> WHERE 1 AND MATCH(attr_1) AGAINST ("3000 3001 3002 3003 3004 3005 3006 3007" IN BOOLEAN MODE)
    -> AND MATCH(attr_2) AGAINST ("1" IN BOOLEAN MODE)
    -> AND MATCH(attr_3) AGAINST ("1980 1981 1982 1983 1984" IN BOOLEAN MODE)
    -> AND MATCH(attr_4) AGAINST ("2,3" IN BOOLEAN MODE)
    -> LIMIT 10000;
+-----------+--------+--------+--------+--------+
| person_id | attr_1 | attr_2 | attr_3 | attr_4 |
+-----------+--------+--------+--------+--------+
|     12131 | 3002   | 1      | 1982   | 2      |
|     51315 | 3007   | 1      | 1984   | 2      |
|    147283 | 3001   | 1      | 1984   | 2      |
|    350086 | 3005   | 1      | 1982   | 3      |
|    423907 | 3004   | 1      | 1982   | 3      |
... many rows ...
|   9423907 | 3004   | 1      | 1982   | 3      |
|   9461892 | 3007   | 1      | 1982   | 2      |
|   9516361 | 3006   | 1      | 1980   | 2      |
|   9813933 | 3005   | 1      | 1982   | 2      |
|   9986892 | 3003   | 1      | 1981   | 2      |
+-----------+--------+--------+--------+--------+
90 rows in set (0.17 sec)

Kueri memilih semua baris:

  • cocok dengan setidaknya satu ID ini di attr_1:3000, 3001, 3002, 3003, 3004, 3005, 3006 or 3007
  • DAN pada saat pencocokan sama 1di attr_2(kolom ini mewakili jenis kelamin jadi jika larutan ini disesuaikan, itu harus smallint(1)dengan indeks sederhana, dll ...)
  • DAN pada saat pencocokan yang sama setidaknya satu dari 1980, 1981, 1982, 1983 or 1984diattr_3
  • DAN pada saat yang sama cocok 2atau 3dalamattr_4

Kesimpulan:

Saya tahu solusi ini tidak sempurna dan ideal untuk banyak situasi tetapi dapat digunakan sebagai alternatif yang baik untuk desain tabel EAV.

Saya harap ini akan membantu seseorang.

Martin
sumber
1
Saya merasa sangat tidak mungkin bahwa desain ini akan tampil lebih baik daripada desain asli Anda dengan indeks komposit. Tes apa yang Anda lakukan untuk membandingkannya?
ypercubeᵀᴹ
0

Coba gunakan petunjuk indeks permintaan yang terlihat sesuai

Petunjuk Indeks Mysql

Muhammad Muazzam
sumber
1
Petunjuk dapat membantu satu versi kueri, tetapi kemudian melukai yang lain. Perhatikan bahwa Pengoptimal memilih Bornyear sebagai tabel pertama terbaik, mungkin karena jika disaring baris yang paling tidak diinginkan.
Rick James