web-dev-qa-db-ja.com

不正な日時値:行1の列「tmp_field_2」の「0000-00-00 00:00:00」

次のクエリを実行しようとしています。

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` ;

last_full_sessionがまだ存在しない場合は問題なく動作しました。この新しい列は次のように作成されます。

ALTER TABLE `user_domain_rating_days` ADD `last_full_session` timestamp NULL AFTER `session_count`;

次のsql_modeONLY_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を使用してmysql(具体的にはPercona 5.7)を実行しています。 NO_ZERO_DATEからsql_modeを削除すると、クエリは正常に実行されます(宛先テーブルの0000-00-00 00:00:00値でさえも)。

クエリのSELECT部分のみを実行すると、正常に実行されます。 @last_full_session := MAX(latest.last_full_session)は、宛先テーブルでサポートされているNULLです。ただし、INSERT INTOの変更と組み合わせて、NULL0000-00-00 00:00:00に変換されるようです。

何が欠けていますか?

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

これはバグのように見えるので、エラーを再現する小さな例(問題のある列のみを保持)を使用して、PerconaまたはMySQLにバグレポートを提出することをお勧めします。

それまでの間、エラーを取り除く可能性のある提案:

クエリには2つの_GROUP BY_操作があり、1つはlatestサブクエリで実行され、グループごとの最新のタイムスタンプを検索します。もう1つはメインクエリで実行され、メインテーブルからさまざまな列のSUMを取得します。

これには、MAXサブクエリからの結果に対して集計(latest)を使用する必要があります-ただし、グループからの行は1つしかありません。これを回避するために(2番目の_GROUP BY_をlatestに適用)、それを(2番目のGROUP BY)を別のサブクエリに移動してから、latestに結合します。

これにより、メインクエリにGROUP BYを含めることができなくなります(そして、私はMAX()NULLに適用されたlatest値のタイムスタンプに適用することによって引き起こされるエラーを取り除くことができると思います[$ var] _サブクエリ)。書き換えられたクエリ:

-- 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ᵀᴹ