2017-01-11 5 views
0

J'ai deux tables, disons juste une table d'utilisateur, et une table de dates. Ils ressemblent à ceci:Multiple même table Left Joins très lent

utilisateur

ID_User | Title | Firstname | Surname | JobNumber 
1  | Mr | Bob  | Smith | JOB001 
2  | Mrs | Bobbi  | Smythe | JOB001 
... 
13000 

Dates

ID_Date | ID_User | DateType | DateAssigned | JobNumber 
1  | 1  | Intent | 21-Jun-2016 | JOB001 
2  | 1  | Reg  | 21-Apr-2017 | JOB001 
3  | 1  | Flight | 21-May-2017 | JOB001 
4  | 2  | Intent | 09-Dec-2016 | JOB001 
5  | 2  | Flight | 01-Jan-2017 | JOB001 
... 
5000 

L'index unique est ID_User + DateType + numéro_travail.

Il peut y avoir n'importe quelle quantité de DateTypes.

Lorsque je fais une requête comme celle-ci, cela prend beaucoup de temps.

select 
    ID_User, 
    Title, 
    Firstname, 
    Surname, 
    JobNumber, 
    DI.DateAssigned as Date_Intent, 
    DR.DateAssigned as Date_Reg, 
    DF.DateAssigned as Date_Flight 
from 
    User as U 
    left join Dates as DI on U.ID_User = DI.ID_User 
    and DI.JobNumber = "JOB001" 
    and DI.DateType = "Intent" 
    left join Dates as DR on U.ID_User = DR.ID_User 
    and DR.JobNumber = "JOB001" 
    and DR.DateType = "Reg" 
    left join Dates as DF on U.ID_User = DF.ID_User 
    and DF.JobNumber = "JOB001" 
    and DF.DateType = "Flight" 
where 
    U.JobNumber = "JOB001" 
order by 
    U.Surname, 
    U.Firstname; 

Chaque numéro_travail aura seulement 300 personnes en elle, avec un maximum de 5 dire différents types de date.

Pourquoi cela prend-il autant de temps? nous parlons 2 minutes.

Existe-t-il une autre façon d'écrire ceci?

Dates Tableau:

CREATE TABLE `ATL_V2_Assigned_Dates` (
    `ID_Date` bigint(7) unsigned NOT NULL AUTO_INCREMENT, 
    `JobNumber` varchar(10) NOT NULL DEFAULT '', 
    `ID_User` bigint(7) unsigned NOT NULL DEFAULT '0', 
    `DateAssigned` datetime NOT NULL, 
    `DateType` varchar(100) NOT NULL, 
    `Comment` text NOT NULL, 
    `Updated` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP, 
    `Inserted` datetime NOT NULL, 
    PRIMARY KEY (`ID_Date`), 
    UNIQUE KEY `ID_Date` (`ID_Date`) USING BTREE, 
    UNIQUE KEY `unq_idx` (`JobNumber`,`ID_User`,`DateType`) USING BTREE, 
    KEY `JobNumber` (`JobNumber`) USING BTREE, 
    KEY `ID_User` (`ID_User`) USING BTREE, 
    KEY `DateType` (`DateType`) USING BTREE 
) ENGINE=MyISAM AUTO_INCREMENT=3975 DEFAULT CHARSET=utf8; 

MISE À JOUR 12 janvier 2017

Très étrange, la requête est en cours d'exécution dans 0.06s maintenant, et est ici la sortie:

explain select 
    U.ID_User, 
    U.Title, 
    U.Firstname, 
    U.Surname, 
    U.JobNumber, 
    DI.DateAssigned as Date_Intent, 
    DR.DateAssigned as Date_Reg, 
    DF.DateAssigned as Date_Flight 
from 
    ATL_Users as U 
    left join ATL_V2_Assigned_Dates as DI on U.ID_User = DI.ID_User 
    and DI.JobNumber = "ACI001" 
    and DI.DateType = "Deadline - Intention" 
    left join ATL_V2_Assigned_Dates as DR on U.ID_User = DR.ID_User 
    and DR.JobNumber = "ACI001" 
    and DR.DateType = "Event - Registration" 
    left join ATL_V2_Assigned_Dates as DF on U.ID_User = DF.ID_User 
    and DF.JobNumber = "ACI001" 
    and DF.DateType = "Deadline - Flight" 
where 
    U.JobNumber = "ACI001" 
order by 
    U.Surname, 
    U.Firstname; 

+----+-------------+-------+--------+------------------------------------+-----------+---------+------------------------------------+------+----------------------------------------------------+ 
| id | select_type | table | type | possible_keys      | key  | key_len | ref        | rows | Extra            | 
+----+-------------+-------+--------+------------------------------------+-----------+---------+------------------------------------+------+----------------------------------------------------+ 
| 1 | SIMPLE  | U  | ref | JobNumber       | JobNumber | 32  | const        | 506 | Using index condition; Using where; Using filesort | 
| 1 | SIMPLE  | DI | eq_ref | unq_idx,JobNumber,ID_User,DateType | unq_idx | 342  | const,cclliveo_atl.U.ID_User,const | 1 | Using where          | 
| 1 | SIMPLE  | DR | eq_ref | unq_idx,JobNumber,ID_User,DateType | unq_idx | 342  | const,cclliveo_atl.U.ID_User,const | 1 | Using where          | 
| 1 | SIMPLE  | DF | eq_ref | unq_idx,JobNumber,ID_User,DateType | unq_idx | 342  | const,cclliveo_atl.U.ID_User,const | 1 | Using where          | 
+----+-------------+-------+--------+------------------------------------+-----------+---------+------------------------------------+------+----------------------------------------------------+ 

Je don Je ne sais pas ce que j'ai/nous avons fait, quelqu'un peut me pointer vers qui vous pensez avoir fourni la réponse et je vais le cocher. Merci les gars.

+0

Veuillez indiquer 'SHOW CREATE TABLE' pour les deux tables, il y a plusieurs choses que je veux vérifier. Fournissez également 'EXPLAIN SELECT ...'. –

Répondre

0

Vous pouvez essayer l'agrégation conditionnelle afin d'éviter tous les Étant donné rejoint

drop table if exists Userjobs; 
create table userjobs (ID_User int, Title varchar(10), Firstname varchar(10), Surname varchar(10), JobNumber varchar(10)); 
insert into userjobs values 
(1  , 'Mr' , 'Bob' ,  'Smith' , 'JOB001'), 
(2  , 'Mrs' , 'Bobbi',  'Smythe' , 'JOB001'); 


drop table if exists jobDates; 
create table jobdates(ID_Date int, ID_User int, DateType varchar(10), DateAssigned date, JobNumber varchar(10)); 
insert into jobdates values 
(1  , 1  , 'Intent' , '2016-06-21' , 'JOB001'), 
(2  , 1  , 'Reg'  , '2017-04-21' , 'JOB001'), 
(3  , 1  , 'Flight' , '2017-05-21' , 'JOB001'), 
(4  , 2  , 'Intent' , '2016-12-09' , 'JOB001'), 
(5  , 2  , 'Flight' , '2017-01-01' , 'JOB001'); 

MariaDB [sandbox]> select 
    -> u.ID_User, 
    -> Title, 
    -> Firstname, 
    -> Surname, 
    -> u.JobNumber, 
    -> max(case when datetype = 'intent' then dateassigned else null end) as intent, 
    -> max(case when datetype = 'reg' then dateassigned else null end) reg, 
    -> max(case when datetype = 'flight' then dateassigned else null end) as flight 
    -> from 
    -> Userjobs as U 
    -> left join jobDates as jd on U.ID_User = jd.ID_User 
    ->  and jd.JobNumber = u.jobnumber 
    -> where u.jobnumber = 'JOB001' 
    -> group by u.ID_User, 
    -> Title, 
    -> Firstname, 
    -> Surname, 
    -> u.JobNumber; 
+---------+-------+-----------+---------+-----------+------------+------------+------------+ 
| ID_User | Title | Firstname | Surname | JobNumber | intent  | reg  | flight  | 
+---------+-------+-----------+---------+-----------+------------+------------+------------+ 
|  1 | Mr | Bob  | Smith | JOB001 | 2016-06-21 | 2017-04-21 | 2017-05-21 | 
|  2 | Mrs | Bobbi  | Smythe | JOB001 | 2016-12-09 | NULL  | 2017-01-01 | 
+---------+-------+-----------+---------+-----------+------------+------------+------------+ 
2 rows in set (0.00 sec) 
+0

Cela ressemble à une bonne façon de le faire, je vais essayer. – Marty

0

Il vous manque probablement des index appropriés. Essayez:

create index idx_user (jobnumber, id_user); 
create index idx_dates (jobnumber, datetype, id_user, dateassigned); 
+0

J'ai ces index. – Marty

+0

Dommage. Vous pouvez essayer 'create index idx_dates2 (id_user, jobnumber, datetype, dateassigned);' aussi. Peut-être que l'accès direct via user_id est plus rapide que le filtrage par jobnumber et datetime en premier. J'en doute, mais ça vaut la peine d'essayer. –

+0

Ce qui est ennuyeux, c'est qu'il est de loin plus rapide d'utiliser 3 sous-critères pour les 3 types de données et de ne pas les joindre du tout. La requête prend des secondes plutôt que des minutes. MAIS, je déteste les sous-groupes et pense vraiment que les jointures devraient être plus rapides. – Marty

0

Ceci est la meilleure façon de joindre la même table, pas sûr du temps nécessaire. Même si vous interrogez à travers 30 000 enregistrements, cela ne prendrait pas 2 minutes. Cela doit être dû à un autre problème, comme plusieurs connexions à la base de données.

0

U besoins INDEX(JobNumber, Surname, Firstname). Cela devrait couvrir les WHERE et ORDER BY, évitant ainsi un «fichier».

Pour Dates, vous avez UNIQUE(ID_User, DateType, JobNumber), correct? Débarrassons-nous de id de cette table, puis remplacer le UNIQUE avec

PRIMARY KEY(JobNumber, ID_User, DateType) 

Cela rendra les recherches plus efficaces parce que le fond du BTree contiendra les DateAssignedet les trois rangées nécessaires seront adjacentes à cause de le "regroupement" du PK.

À moins que vous n'ayez d'autres requêtes (lire ou modifier) ​​touchant Dates, aucun autre index ne doit figurer dans cette table.

Quelle est la taille de ces tables? Vous réalisez que vous allez les lire tous les deux entièrement. Cependant, mes suggestions conduiront à lire chaque ligne une seule fois, pas plusieurs fois.

+0

J'ai modifié le message d'origine à ce que j'ai pour la table Dates. S'il vous plaît voir ci-dessus, il ne me laisse pas le poster ici. – Marty