Preciso eliminar duplicatas de uma planilha de horas. Eu encontrei esta solução e a adaptei para minhas próprias necessidades:
DROP TABLE IF EXISTS `activity`;
CREATE TABLE IF NOT EXISTS `activity` (
`id` int(11) NOT NULL AUTO_INCREMENT,
`planned_start` datetime DEFAULT NULL,
`planned_end` datetime DEFAULT NULL,
`actual_start` datetime DEFAULT NULL,
`actual_end` datetime DEFAULT NULL,
`code_id` int(11) DEFAULT NULL,
`setting_id` int(11) DEFAULT NULL,
`notes` text,
`travel_distance` decimal(8,2) DEFAULT NULL,
`created_by` int(11) NOT NULL,
`updated_by` int(11) DEFAULT NULL,
`submitted` tinyint(1) DEFAULT NULL,
`approved` datetime DEFAULT NULL,
`approved_by` int(11) DEFAULT NULL,
`created` datetime NOT NULL,
`updated` datetime NOT NULL,
`peer_engagement_id` int(11) DEFAULT NULL,
`person_id` int(11) DEFAULT NULL,
`travel_notes` varchar(8000) DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `id` (`id`),
KEY `code_id_idx` (`code_id`),
KEY `setting_id_idx` (`setting_id`),
KEY `created_by_idx` (`created_by`),
KEY `updated_by_idx` (`updated_by`),
KEY `approved_by_idx` (`approved_by`),
KEY `activity_peer_engagement_id_fk` (`peer_engagement_id`),
KEY `activity_person_id_fk` (`person_id`),
KEY `actual_start` (`actual_start`,`actual_end`),
KEY `created` (`created`),
KEY `person_id` (`person_id`,`actual_start`,`actual_end`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=165796 ;
SELECT
COUNT(*) as occurrence
, sub.id
, SEC_TO_TIME(SUM(
IF(a2start > a1start, a1end - a2start, a2end - a1start))) as duration
FROM
( SELECT
a1.id
, UNIX_TIMESTAMP(a1.actual_start) as a1start
, UNIX_TIMESTAMP(a1.actual_end) as a1end
, UNIX_TIMESTAMP(a2.actual_start) as a2start
, UNIX_TIMESTAMP(a2.actual_end) as a2end
FROM activity a1
INNER JOIN activity a2
ON (a1.id <> a2.id and a1.person_id=a2.person_id
AND NOT(a1.actual_start > a2.actual_end OR a1.actual_end < a2.actual_start))
) sub
O problema é que não consigo nem executar a explicação na minha consulta, meu servidor mysql entra em 100% de uso da CPU e parece ficar lá por minutos.
Posso executar a explicação na consulta interna:
explain SELECT
a1.id
, UNIX_TIMESTAMP(a1.actual_start) as a1start
, UNIX_TIMESTAMP(a1.actual_end) as a1end
, UNIX_TIMESTAMP(a2.actual_start) as a2start
, UNIX_TIMESTAMP(a2.actual_end) as a2end
FROM activity a1
INNER JOIN activity a2
ON (a1.id <> a2.id and a1.person_id=a2.person_id
AND NOT(a1.actual_start > a2.actual_end OR a1.actual_end < a2.actual_start))
+----+-------------+-------+-------+----------------------------------------------+-----------+---------+--------------------------------------+--------+--------------------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-------+-------+----------------------------------------------+-----------+---------+--------------------------------------+--------+--------------------------+
| 1 | SIMPLE | a1 | index | activity_person_id_fk,actual_start,person_id | person_id | 23 | NULL | 176586 | Using index |
| 1 | SIMPLE | a2 | ref | activity_person_id_fk,actual_start,person_id | person_id | 5 | mabel_mindandbody_co_nz.a1.person_id | 19705 | Using where; Using index |
+----+-------------+-------+-------+----------------------------------------------+-----------+---------+--------------------------------------+--------+--------------------------+
2 rows in set (0.00 sec)
Minhas perguntas:
- Por que não explica o trabalho aqui?
- Como posso otimizar essa consulta para fornecer resultados de velocidade aceitáveis?
Em relação à otimização - não consigo encontrar nada além dos índices que já usei em minha tabela.
Uma outra opção que pensei é adicionar um campo adicional, codificando todos os dias em um número. Eu sei que uma entrada na planilha de horas nunca dura mais de 24 horas e tenho certeza de que excluir as entradas da planilha de horas que abrangem a meia-noite seria aceitável. Portanto, com isso, espero usar um índice menor nesta coluna adicional na consulta interna.
Com a suposição de que nenhuma entrada no quadro de horários abrange a meia-noite, adicionei uma coluna
Isso está sendo calculado durante a noite com
Também simplifiquei a consulta interna para:
Em primeiro lugar, porque meus usuários registram muito seu tempo em limites horários e temos uma sobreposição de '0:00', em segundo lugar, com a1.id<>a2.id, encontramos cada entrada duas vezes, quando precisamos apenas de uma.
Concluí que não há uma boa maneira de otimizar para a1.id < a2.id, portanto, com person_date estou otimizando para outra coisa.