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