web-dev-qa-db-fra.com

Valeur datetime incorrecte: '0000-00-00 00:00:00' pour la colonne 'tmp_field_2' à la ligne 1

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')
2
tersmitten

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` ;
1
ypercubeᵀᴹ