SQL - 只保留每天的第一条和最后一条记录
SQL - Keep only the first and last record of each day
我有一个存储简单日志数据的table:
CREATE TABLE chronicle (
id INT auto_increment PRIMARY KEY,
data1 VARCHAR(256),
data2 VARCHAR(256),
time DATETIME
);
table 接近 100 万条记录,所以我想开始合并数据。
我希望能够每天获取每个 DISTINCT(data1, data2)
的第一条和最后一条记录,然后删除所有其余记录。
我知道如何只提取数据并以我想要的任何语言处理它,然后删除具有巨大 IN (...)
query 的记录,但似乎更好的选择是使用 SQL直接(我错了吗?)
我尝试了几个查询,但我对 JOIN 之外的 SQL 不是很好。
这是我目前的情况:
SELECT id, Max(time), Min(time)
FROM (SELECT id, data1 ,data2, time, Cast(time AS DATE) AS day
FROM chronicle) AS initial
GROUP BY day;
这让我得到每天的第一次和最后一次,但它没有被数据分开(即我得到每天的最后一条记录,而不是每天每组不同数据的最后一条记录。 ) 此外,id
仅适用于 Min(time)。
我在这个特定问题上找到的信息仅用于查找当天的最后一条记录,而不是数据集的每条最后记录。
重要提示:我想要每天每个 DISTINCT(data1, data2)
的 first/last 记录,而不仅仅是每天的 first/last 记录table。每天会有2条以上的记录。
解法:
我的解决方案感谢 Jonathan Dahan 和 Gordon Linoff:
SELECT o.data1, o.data2, o.time FROM chronicle AS o JOIN (
SELECT Min(id) as id FROM chronicle GROUP BY DATE(time), data1, data2
UNION SELECT Max(id) as id FROM test_chronicle GROUP BY DATE(time), data1. data2
) AS n ON o.id = n.id;
从这里开始,只需引用相同的 table 即可删除行。
你的想法是对的。您只需重新加入即可获取原始信息。
SELECT c.*
FROM chronicle c JOIN
(SELECT date(time) as day, min(time) as mint, max(time) as maxt
FROM chronicle
GROUP BY date(time)
) cc
ON c.time IN (cc.mint, cc.maxt);
请注意,join
条件不需要明确包含 day
,因为它是 time
的一部分。当然,如果你愿意,你可以添加date(c.time) = cc.day
。
我建议您创建一个新的 table,而不是删除原始 table 中的行。这是谎言:
create table ChronicleByDay like chronicle;
insert into ChronicleByDay
SELECT c.*
FROM chronicle c JOIN
(SELECT date(time) as day, min(time) as mint, max(time) as maxt
FROM chronicle
GROUP BY date(time)
) cc
ON c.time IN (cc.mint, cc.maxt);
这样,您可以在需要时获得更详细的信息。
这将提高搜索日期时的性能。
ALTER TABLE chronicle
ADD INDEX `ix_chronicle_time` (`time` ASC);
这将删除记录:
CREATE TEMPORARY TABLE #tmp_ids (
`id` INT NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO #tmp_ids (id)
SELECT
min(id)
FROM
chronicle
GROUP BY
CAST(day as DATE),
data1,
data2
UNION
SELECT
Max(id)
FROM
chronicle
GROUP BY
CAST(day as DATE),
data1,
data2;
DELETE FROM
chronicle
WHERE
ID not in (select id FROM #tmp_ids)
AND date <= '2015-01-01'; -- if you want to consider all dates, then remove this condition
我有一个存储简单日志数据的table:
CREATE TABLE chronicle (
id INT auto_increment PRIMARY KEY,
data1 VARCHAR(256),
data2 VARCHAR(256),
time DATETIME
);
table 接近 100 万条记录,所以我想开始合并数据。
我希望能够每天获取每个 DISTINCT(data1, data2)
的第一条和最后一条记录,然后删除所有其余记录。
我知道如何只提取数据并以我想要的任何语言处理它,然后删除具有巨大 IN (...)
query 的记录,但似乎更好的选择是使用 SQL直接(我错了吗?)
我尝试了几个查询,但我对 JOIN 之外的 SQL 不是很好。
这是我目前的情况:
SELECT id, Max(time), Min(time)
FROM (SELECT id, data1 ,data2, time, Cast(time AS DATE) AS day
FROM chronicle) AS initial
GROUP BY day;
这让我得到每天的第一次和最后一次,但它没有被数据分开(即我得到每天的最后一条记录,而不是每天每组不同数据的最后一条记录。 ) 此外,id
仅适用于 Min(time)。
我在这个特定问题上找到的信息仅用于查找当天的最后一条记录,而不是数据集的每条最后记录。
重要提示:我想要每天每个 DISTINCT(data1, data2)
的 first/last 记录,而不仅仅是每天的 first/last 记录table。每天会有2条以上的记录。
解法: 我的解决方案感谢 Jonathan Dahan 和 Gordon Linoff:
SELECT o.data1, o.data2, o.time FROM chronicle AS o JOIN (
SELECT Min(id) as id FROM chronicle GROUP BY DATE(time), data1, data2
UNION SELECT Max(id) as id FROM test_chronicle GROUP BY DATE(time), data1. data2
) AS n ON o.id = n.id;
从这里开始,只需引用相同的 table 即可删除行。
你的想法是对的。您只需重新加入即可获取原始信息。
SELECT c.*
FROM chronicle c JOIN
(SELECT date(time) as day, min(time) as mint, max(time) as maxt
FROM chronicle
GROUP BY date(time)
) cc
ON c.time IN (cc.mint, cc.maxt);
请注意,join
条件不需要明确包含 day
,因为它是 time
的一部分。当然,如果你愿意,你可以添加date(c.time) = cc.day
。
我建议您创建一个新的 table,而不是删除原始 table 中的行。这是谎言:
create table ChronicleByDay like chronicle;
insert into ChronicleByDay
SELECT c.*
FROM chronicle c JOIN
(SELECT date(time) as day, min(time) as mint, max(time) as maxt
FROM chronicle
GROUP BY date(time)
) cc
ON c.time IN (cc.mint, cc.maxt);
这样,您可以在需要时获得更详细的信息。
这将提高搜索日期时的性能。
ALTER TABLE chronicle
ADD INDEX `ix_chronicle_time` (`time` ASC);
这将删除记录:
CREATE TEMPORARY TABLE #tmp_ids (
`id` INT NOT NULL,
PRIMARY KEY (`id`)
);
INSERT INTO #tmp_ids (id)
SELECT
min(id)
FROM
chronicle
GROUP BY
CAST(day as DATE),
data1,
data2
UNION
SELECT
Max(id)
FROM
chronicle
GROUP BY
CAST(day as DATE),
data1,
data2;
DELETE FROM
chronicle
WHERE
ID not in (select id FROM #tmp_ids)
AND date <= '2015-01-01'; -- if you want to consider all dates, then remove this condition