I have a DB with events, workshops in each event (1:N), participants in each workshop/event(N:M), the role of a participant in each workshop, and a table (letture) with access control for each participant in the workshop.
We create a unique QR code for participants/workshops. With this QR code, we can identify who went to which workshop and we can calculate for how many hours he remained.
But not all workshops need this in/out (entrata=1/2) value, for some are sufficient check who are present (entrata=0).
To print the final certificate for every participant in every event/workshop I try to populate the table attestati_dati
with this query :
TRUNCATE attestati_dati;
INSERT INTO attestati_dati
(id_participant,id_event,id_workshop,id_role,id_attestato,ore_totali)
SELECT * FROM
(
SELECT
p.id_participant AS id_participant,
i1.id_event AS id_event,
i1.id_sala AS id_workshop,
r.id_role AS id_role,
1 AS id_attestato,
SEC_TO_TIME(SUM(TIMESTAMPDIFF(SECOND,
i1.datalettura,
IFNULL(i2.datalettura, i1.datalettura)))) AS ore_totali
FROM
letture AS i1
LEFT JOIN
letture AS i2 ON (i1.qrcode = i2.qrcode
AND i2.datalettura > i1.datalettura
AND i1.id_event = i2.id_event
AND i1.id_sala = i2.id_sala
AND i2.entrata = 2)
LEFT JOIN
letture AS i3 ON (i3.qrcode = i2.qrcode
AND i3.datalettura > i1.datalettura
AND i1.id_event = i3.id_event
AND i1.id_sala = i3.id_sala
AND i3.entrata = 2
AND i3.datalettura < i2.datalettura)
CROSS JOIN
event_participants ep ON ep.qrcode = i1.qrcode
AND ep.id_event = i1.id_event
LEFT JOIN
participants p ON p.id_participant = ep.id_participant
LEFT JOIN
roles r ON ep.id_role = r.id_role
LEFT JOIN
events e ON i1.id_event = e.id_event
LEFT JOIN
workshop_participant wp ON wp.id_participant = p.id_participant
AND wp.id_workshop = i1.id_sala
WHERE
i1.entrata = 1
AND i3.datalettura IS NULL
AND i1.id_Event = 52
GROUP BY i1.qrcode
UNION SELECT
p.id_participant AS id_participant,
i1.id_event AS id_event,
i1.id_sala AS id_workshop,
r.id_role AS id_role,
1 AS id_attestato,
SEC_TO_TIME(0) AS ore_totali
FROM
letture AS i1
CROSS JOIN
event_participants ep ON ep.qrcode = i1.qrcode
AND ep.id_event = i1.id_event
LEFT JOIN
participants p ON p.id_participant = ep.id_participant
LEFT JOIN
roles r ON ep.id_role = r.id_role
LEFT JOIN
events e ON i1.id_event = e.id_event
LEFT JOIN
workshop_participant wp ON wp.id_participant = p.id_participant
AND wp.id_workshop = i1.id_sala
WHERE
i1.entrata = 0 AND i1.id_Event = 52
GROUP BY i1.qrcode , DAYOFMONTH(i1.datalettura)
) A;
I'd like to improve this query because this is the best I can do to solve this problem, but I think that is optimizable, especially in the group by/where/joins part.
Show Create table:
CREATE TABLE `attestati_dati` (
`id_attestati_participants` int(11) NOT NULL AUTO_INCREMENT,
`id_participant` int(11) DEFAULT NULL,
`id_event` int(11) DEFAULT NULL,
`id_workshop` int(11) DEFAULT NULL,
`id_role` int(11) DEFAULT NULL,
`id_attestato` int(11) DEFAULT NULL,
`ore_totali` time DEFAULT NULL,
PRIMARY KEY (`id_attestati_participants`)
) ENGINE=MyISAM AUTO_INCREMENT=25 DEFAULT CHARSET=latin1;
Best Answer
e
andwp
are not used, so remove the `LEFT JOINs to them.Some composite indexes needed:
My suggested index for
letture
helps with all 3 uses of that table, and in different ways. The order of the columns is critical, except that the first two can be swapped.If
AND i2.datalettura > i1.datalettura
is part of doing a "groupwise max", then it is especially inefficient. This discusses much faster ways.AND i3.datalettura IS NULL
and itsLEFT JOIN
can probably be replaced byNOT EXISTS ( SELECT 1 ... )
The
CROSS JOIN
is technically anINNER JOIN
, but since MySQL ignoresCROSS
andINNER
, either of those works the same. Ditto for plainJOIN
.The index recommendations come from looking at the
WHERE
clauses, and noting that nothing can be done about theGROUP BY
. More discussion here.If you need to discuss this further, please provide
SHOW CREATE TABLE
andEXPLAIN SELECT ...
.