J'essaie d'exécuter la requête suivante:
INSERT INTO `user_domain_rating_days`
(
`user_domain_rating_id`,
`start`,
`user_id`,
`domain_id`,
`modified_count`,
`modified_count_diff`,
`rating`,
`rating_diff`,
`transformed_rating`,
`transformed_rating_diff`,
`q_score`,
`q_score_diff`,
`timestamp_diff`,
`session_count`,
`last_full_session`
)
SELECT MAX(`latest`.`user_domain_rating_id`),
:date,
MAX(`latest`.`user_id`),
MAX(`latest`.`domain_id`),
@modified_count := MAX(`latest`.`modified_count`),
@modified_count_diff := SUM(`group_by`.`modified_count_diff`),
@rating := MAX(`latest`.`rating`),
@rating_diff := SUM(`group_by`.`rating_diff`),
@transformed_rating := MAX(`latest`.`transformed_rating`),
@transformed_rating_diff := SUM(`group_by`.`transformed_rating_diff`),
@q_score := MAX(`latest`.`q_score`),
@q_score_diff := SUM(`group_by`.`q_score_diff`),
@timestamp_diff := SUM(`group_by`.`timestamp_diff`),
@session_count := MAX(`latest`.`session_count`),
@last_full_session := MAX(`latest`.`last_full_session`)
FROM `user_domain_rating_hours` AS `group_by`
LEFT JOIN
(
SELECT *
FROM `user_domain_rating_hours`
WHERE `id` IN
(
SELECT MAX(`id`)
FROM `user_domain_rating_hours`
WHERE `start` >= :date
AND `start` < DATE_ADD(DATE(:date), INTERVAL 1 DAY)
GROUP BY `user_id`,
`domain_id` ) ) AS `latest`
ON `group_by`.`user_id` = `latest`.`user_id`
AND `group_by`.`domain_id` = `latest`.`domain_id`
WHERE 1
AND NOW() > DATE_ADD(DATE(:date), INTERVAL 1 DAY)
AND `group_by`.`start` >= :date
AND `group_by`.`start` < DATE_ADD(DATE(:date), INTERVAL 1 DAY)
GROUP BY `group_by`.`user_id`,
`group_by`.`domain_id` LOCK IN SHARE MODE
ON DUPLICATE KEY
UPDATE `user_domain_rating_days`.`id` = `user_domain_rating_days`.`id` ;
qui a bien fonctionné lorsque last_full_session
n'était pas encore présent. Cette nouvelle colonne est créée comme ceci:
ALTER TABLE `user_domain_rating_days` ADD `last_full_session` timestamp NULL AFTER `session_count`;
Nous utilisons mysql (Percona 5.7 pour être spécifique) avec le sql_mode
Suivant: ONLY_FULL_GROUP_BY,STRICT_TRANS_TABLES,NO_ZERO_IN_DATE,NO_ZERO_DATE,ERROR_FOR_DIVISION_BY_ZERO,NO_AUTO_CREATE_USER,NO_ENGINE_SUBSTITUTION
. Lorsque je supprime NO_ZERO_DATE
De sql_mode
, La requête s'exécute correctement (même pas les valeurs de 0000-00-00 00:00:00
Dans la table de destination).
Lorsque j'exécute uniquement la partie SELECT
de la requête, cela fonctionne également correctement. @last_full_session := MAX(
Dernier .
Dernière_full_session )
Est NULL
qui est pris en charge par la table de destination. Cependant, en combinaison avec INSERT INTO
Quelque chose change, NULL
semble être converti en 0000-00-00 00:00:00
.
Qu'est-ce que je rate?
mysql> show variables like 'explicit_defaults_for_timestamp';
+---------------------------------+-------+
| Variable_name | Value |
+---------------------------------+-------+
| explicit_defaults_for_timestamp | OFF |
+---------------------------------+-------+
CREATE TABLE `user_domain_rating_days` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`user_domain_rating_id` int(10) unsigned NOT NULL,
`start` date NOT NULL,
`user_id` int(10) unsigned NOT NULL,
`domain_id` int(10) unsigned NOT NULL,
`modified_count` int(10) unsigned NOT NULL,
`modified_count_diff` int(10) unsigned NOT NULL,
`rating` double NOT NULL,
`rating_diff` double NOT NULL,
`transformed_rating` double DEFAULT NULL,
`transformed_rating_diff` double NOT NULL,
`q_score` smallint(4) unsigned DEFAULT NULL,
`q_score_diff` smallint(4) NOT NULL,
`timestamp_diff` int(10) unsigned NOT NULL,
`session_count` int(10) unsigned DEFAULT NULL,
`last_full_session` timestamp NULL DEFAULT NULL,
PRIMARY KEY (`id`),
UNIQUE KEY `user_domain_day` (`user_id`,`domain_id`,`start`),
UNIQUE KEY `user_domain_rating_id_day` (`user_domain_rating_id`,`start`),
KEY `domain_id` (`domain_id`),
CONSTRAINT `user_domain_rating_days_ibfk_1` FOREIGN KEY (`user_id`) REFERENCES `users` (`id`),
CONSTRAINT `user_domain_rating_days_ibfk_2` FOREIGN KEY (`domain_id`) REFERENCES `domains` (`id`),
CONSTRAINT `user_domain_rating_days_ibfk_3` FOREIGN KEY (`user_domain_rating_id`) REFERENCES `user_domain_ratings` (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8
mysql> SELECT version();
+-----------+
| version() |
+-----------+
| 5.7.24-27 |
+-----------+
# mysqld --version
mysqld Ver 5.7.24-27 for debian-linux-gnu on x86_64 (Percona Server (GPL), Release '27', Revision 'bd42700')
Cela ressemble à un bogue, donc je vous suggère de déposer un rapport de bogue à Percona ou à MySQL, avec un exemple plus petit qui reproduit l'erreur (ne conservez que la colonne problématique).
En attendant, une suggestion qui peut supprimer l'erreur:
Il y a deux opérations GROUP BY
Dans la requête, une effectuée dans la sous-requête latest
, pour trouver les derniers horodatages par groupe et une autre, effectuée dans la requête principale, pour obtenir les SOMMES de diverses colonnes du principal table.
Cela nécessite l'utilisation d'un agrégat (MAX
) pour les résultats provenant de la sous-requête latest
- bien que nous ayons seulement 1 ligne par groupe à partir de cela. Afin d'éviter cela (deuxième GROUP BY
Appliqué à latest
), nous pouvons déplacer ce (deuxième GROUP BY) vers une autre sous-requête, puis rejoindre latest
.
Cela nous permettrait de ne pas avoir de GROUP BY dans la requête principale (et comme je soupçonne de me débarrasser de l'erreur qui est en quelque sorte causée par l'application de MAX()
à NULL
horodatages de valeur qui proviennent de latest
sous-requête). La requête réécrite:
-- INSERT part (unchanged)
INSERT INTO `user_domain_rating_days`
(
`user_domain_rating_id`,
`start`,
`user_id`,
`domain_id`,
`modified_count`,
`modified_count_diff`,
`rating`,
`rating_diff`,
`transformed_rating`,
`transformed_rating_diff`,
`q_score`,
`q_score_diff`,
`timestamp_diff`,
`session_count`,
`last_full_session`
)
-- main query SELECT (aggregates removed)
SELECT `latest`.`user_domain_rating_id`,
:date,
`latest`.`user_id`,
`latest`.`domain_id`,
@modified_count := `latest`.`modified_count`,
@modified_count_diff := `group_by`.`modified_count_diff`,
@rating := `latest`.`rating`,
@rating_diff := `group_by`.`rating_diff`,
@transformed_rating := `latest`.`transformed_rating`,
@transformed_rating_diff := `group_by`.`transformed_rating_diff`,
@q_score := `latest`.`q_score`,
@q_score_diff := `group_by`.`q_score_diff`,
@timestamp_diff := `group_by`.`timestamp_diff`,
@session_count := `latest`.`session_count`,
@last_full_session := `latest`.`last_full_session`
FROM
-- group_by subquery
(
SELECT user_id,
domain_id,
SUM(g.`modified_count_diff`) AS modified_count_diff,
SUM(g.`rating_diff`) AS rating_diff,
SUM(g.`transformed_rating_diff`) AS transformed_rating_diff,
SUM(g.`q_score_diff`) AS q_score_diff,
SUM(g.`timestamp_diff`) AS timestamp_diff,
FROM `user_domain_rating_hours` AS g
WHERE 1
AND NOW() > DATE_ADD(DATE(:date), INTERVAL 1 DAY)
AND g.`start` >= :date
AND g.`start` < DATE_ADD(DATE(:date), INTERVAL 1 DAY)
GROUP BY g.`user_id`,
g.`domain_id` LOCK IN SHARE MODE
) AS `group_by`
-- latest subquery (unchanged)
LEFT JOIN
(
SELECT *
FROM `user_domain_rating_hours`
WHERE `id` IN
(
SELECT MAX(`id`)
FROM `user_domain_rating_hours`
WHERE `start` >= :date
AND `start` < DATE_ADD(DATE(:date), INTERVAL 1 DAY)
GROUP BY `user_id`,
`domain_id` ) ) AS `latest`
ON `group_by`.`user_id` = `latest`.`user_id`
AND `group_by`.`domain_id` = `latest`.`domain_id`
-- GROUP BY removed
-- GROUP BY ...
-- unchanged part
ON DUPLICATE KEY
UPDATE `user_domain_rating_days`.`id` = `user_domain_rating_days`.`id` ;