Как повысить производительность запросов при применении нескольких фильтров? - PullRequest
0 голосов
/ 05 июня 2018

У меня есть троичные отношения, которые называются ternary, например:

id_Offer    -   id_Profile  -   id_Skill
1           -   1           -   1
1           -   2           -   1

[and so on, there would be more registers for each id_Offer from Offer but I want to limit the example]

Таблица профиля выглядит примерно так (profile_interest - это таблица, которая устанавливает связь между профилем и интересом, вот и все):

id_Profile -   profile_name
1          -   profile-1
2          -   profile-2
3          -   profile-3

Поэтому, когда я делаю следующий запрос, чем больше я добавляю предложений OR, тем хуже выполняется запрос, начиная с ~ 0,1-0,2 секунды, что я получаю для любого другого запроса, который я делаю,и до 1,5 секунд.

SELECT DISTINCT ternary_table.id_profile, COUNT(distinct profile_interest.id_interest) as matching 
FROM ternary_table INNER JOIN profile ON ternary_table.id_profile=profile.id_profile 
INNER JOIN profile_interest ON profile.id_profile=profile_interest.id_profile 
WHERE profile_interest.id_interest= '1' 
 OR profile_interest.id_interest = '2' 
 OR profile_interest.id_interest = '3'
 OR profile_interest.id_interest = '14'
 OR profile_interest.id_interest = '15'
 OR profile_interest.id_interest = '16'
GROUP BY(ternary_table.id_profile) 
ORDER BY matching DESC; 

Я пытался сделать поле profile_interest.id_interest индексированным столбцом с:

CREATE INDEX filter_interest ON profile_interest(id_interest );

Без каких-либо улучшений.База данных весит меньше гигабайта, это очень маленькая база данных с ~ 15 таблицами, поэтому я хотел бы знать, есть ли способ сократить задержку запроса.

Редактировать: чтобы добавить больше информации, причинаМеня это беспокоит, потому что единственная цель этих данных - подключиться к API, поэтому любая задержка в SQL будет задерживать каждый вызов этих данных.

Edit1: добавлен вывод EXPLAIN и удалены первые отличия, так как в этом нет необходимости

+----+-------------+---------------------+------------+--------+------------------------------------------------+------------+---------+------------------------------------+------+----------+-----------------------------------------------------------+
| id | select_type | table               | partitions | type   | possible_keys                                  | key        | key_len | ref                                | rows | filtered | Extra                                                     |
+----+-------------+---------------------+------------+--------+------------------------------------------------+------------+---------+------------------------------------+------+----------+-----------------------------------------------------------+
|  1 | SIMPLE      | profile_interest      | NULL       | range  | PRIMARY,id_interest,filter_interest | id_interest | 202 | NULL                               |   40 |   100.00 | Using where; Using index; Using temporary; Using filesort |
|  1 | SIMPLE      | perfil              | NULL       | eq_ref | PRIMARY                                        | PRIMARY    | 202     | BBDD.profile_interest.id_perfil    |    1 |   100.00 | Using index                                               |
|  1 | SIMPLE      | oferta_skill_perfil | NULL       | ref    | PRIMARY,id_skill,id_perfil                     | id_perfil  | 202     | BBDD.profile_interest.id_perfil    | 4609 |   100.00 | Using index                                               |
+----+-------------+---------------------+------------+--------+------------------------------------------------+------------+---------+------------------------------------+------+----------+-----------------------------------------------------------+

Edit 2: добавлено создание таблицы для каждого запроса

SET FOREIGN_KEY_CHECKS=1;

CREATE TABLE profile (
    id_profile VARCHAR(200) NOT NULL,
    name_profile VARCHAR(200),
    type_profile VARCHAR(200),
    PRIMARY KEY (id_profile)
);


CREATE TABLE ternary (
    id_oferta VARCHAR(200) NOT NULL,
    id_skill VARCHAR(200) NOT NULL,
    id_profile VARCHAR(200) NOT NULL,
    ranking_skill DOUBLE NOT NULL,
    PRIMARY KEY (id_oferta, id_skill, id_profile),
    FOREIGN KEY (id_oferta) REFERENCES oferta(id_oferta),
    FOREIGN KEY (id_skill) REFERENCES skill(id_skill),
    FOREIGN KEY (id_profile) REFERENCES profile(id_profile)
);

 CREATE TABLE interest (
    id_interest VARCHAR(200) NOT NULL,
    name_interes VARCHAR(200),
    PRIMARY KEY (id_interest)
 );


CREATE TABLE profile_interest (
    id_profile VARCHAR(200) NOT NULL,
    id_interest VARCHAR(200) NOT NULL, 
    PRIMARY KEY (id_profile, id_interest),
    FOREIGN KEY (id_profile) REFERENCES profile(id_profile),
    FOREIGN KEY (id_interest) REFERENCES interes(id_interest)
);

Ответы [ 4 ]

0 голосов
/ 07 июня 2018
SELECT  id_profile,
        COUNT(id_interest) as matching
    FROM  profile_interest AS pi
    WHERE  id_interest IN (1,2,4,14,15,16)
      AND EXISTS ( SELECT * FROM oferta_skill_perfil
                                          WHERE id_profile = pi.id_profile )
      AND EXISTS ( SELECT * FROM profile  WHERE id_profile = pi.id_profile )
    GROUP BY id_profile
    ORDER BY  matching DESC;

И profile_interest нуждается в INDEX(id_interest, id_profile) в этом порядке.

Я использовал EXISTS только потому, что это реальная цель касания других таблиц.Но, может быть, к ним не нужно прикасаться?

Замедление вызвано тем, что я называю "взорваться-взорваться" или "надуть-сдуть".Это происходит, когда вы JOIN некоторые таблицы (что приводит к большему количеству промежуточных строк), а затем GROUP BY возвращаются к тому, с чего вы начали.Техника избавления от него заключается в сначала сосредоточении на выполнении агрегатов (COUNT, в вашем случае), затем JOIN по мере необходимости.

EXISTs намного быстрее, когда вам просто нужно проверить существование, а не найти все 4609 строк.

profile_interest представляется таблицей сопоставления «многие ко многим».См. Мои советы здесь .

Обратите внимание, что он рекомендует индекс, который я предлагаю выше.

Обычно идентификаторы являются целыми числами;почему у вас VARCHAR(200)?Нет очевидного источника таких длинных строк.

0 голосов
/ 05 июня 2018

Ваш запрос может быть уменьшен до:

SELECT id_profile, 
   COUNT(distinct id_interest) as matching  -- or COUNT(*)??
FROM profile_interest 
WHERE id_interest IN(1,2,3,14,15,16) -- those ids are probably integers, not strings
GROUP BY id_profile
ORDER BY matching DESC; 
0 голосов
/ 05 июня 2018

Вы можете попробовать написать это как:

select tt.id_profile,
       (select count(distinct pi.id_interest)
        from profile_interest pi
        where tt.id_profile = pi.id_profile and
              pi.id_interest in (1, 2, 3, 14, 15, 16)
       ) as matching
from ternary_table tt;

Чтобы это работало, вам нужен индекс для profile_interest(id_profile, id_interest).

РЕДАКТИРОВАТЬ:

Если вы толькохотите строки, которые соответствуют, то вы можете добавить:

having matching > 0
0 голосов
/ 05 июня 2018

Не ответ.Слишком долго для комментария ...

FWIW, мне легче читать ...

SELECT x.id_profile
     , COUNT(DISTINCT i.id_interest) matching 
  FROM ternary_table x
  JOIN profile p 
    ON p.id_profile = x.id_profile
  JOIN profile_interest i
    ON i.id_profile = p.id_profile
 WHERE i.id_interest IN(1,2,3,14,15,16)
 GROUP 
    BY x.id_profile
 ORDER 
    BY matching DESC; 

Теперь, если бы мы только видели операторы SHOW CREATE TABLE и EXPLAINза это.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...