MySQL从每个组中选择最大记录并插入另一个表

时间:2016-12-31 15:56:49

标签: mysql database aggregate-functions greatest-n-per-group

表A中有4列,id,name,create_time和content。

create table A
(
    id int primary key,
    name varchar(20),
    create_time datetime,
    content varchar(4000)
);
create table B like A;

我想在同一个create_time中选择最多name个记录,然后插入另一个表格B

执行sql如下,但时间消耗是不可接受的。

insert into B
select A.*
from A,
    (select name, max(create_time) create_time from B group by name) tmp
where A.name = tmp.name
  and A.create_time = tmp.create_time;

一个表有1000W行和10GB,执行sql花费200s。

有没有办法更快地完成这项工作,或者更改MySQL Server中哪些参数运行得更快。

号码: 表A可以是任何类型,paration表或其他一些。

2 个答案:

答案 0 :(得分:1)

首先确保你对A(name,create_time)和B(name,create_time)有适当的索引 然后尝试使用显式连接和condtion

insert into B 
select A.* 
from A 
inner join ( 
    select name, max(create_time) create_time 
    from B 
    group by name) tmp on  ( A.name = tmp.name and A.create_time = tmp.create_time)

答案 1 :(得分:1)

您需要的查询是:

INSERT INTO B
SELECT m.*
FROM A m                                      # m from "max"
LEFT JOIN A l                                 # l from "later"
    ON m.name = l.name                        # the same name
        AND m.create_time < l.create_time     # "l" was created later than "m"
WHERE l.name IS NULL                          # there is no "later"

工作原理:

它将A别名作为m(来自“max”)与其自身别名为l(来自“later”比“max”)。 LEFT JOIN确保在没有WHERE子句的情况下,m中的所有行都出现在结果集中。 m中的每一行都与l中具有相同namem.name = l.name)的所有行相结合,并在m的行之后创建({{ 1}})。 m.create_time < l.create_time条件仅在结果集中保留WHEREm中没有任何匹配项的行(没有相同名称和更长创建时间的记录)。

讨论

如果l中有多个行具有相同的Aname,则查询将返回所有行。为了只保留其中一个,需要额外的条件。

添加:

creation_time

OR (m.create_time = l.create_time AND m.id < l.id) 子句(在ON之前)。调整/替换条件的WHERE部分以满足您的需要(此版本支持在表格中先前插入的行)。

确保表m.id < l.id在查询使用的列上有索引(Aname)。否则与原始列表相比性能提升查询不重要。