次のクエリを実行しようとしています。
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_mode
: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
を使用して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
の変更と組み合わせて、NULL
は0000-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')
これはバグのように見えるので、エラーを再現する小さな例(問題のある列のみを保持)を使用して、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` ;
_