使用一个子查询更新多列
Updating multiple columns with one subquery
我在 sqlite 中有一个经典的父子关系:
create table tq84_parent (
id int primary key,
avg_val float,
min_val float,
max_val float
);
create table tq84_child (
id_parent int references tq84_parent,
val int
);
其中填写如下
insert into tq84_parent (id) values(1);
insert into tq84_parent (id) values(2);
insert into tq84_child values (1, 1);
insert into tq84_child values (1, 2);
insert into tq84_child values (1, 3);
insert into tq84_child values (2, 6);
insert into tq84_child values (2, 7);
insert into tq84_child values (2, 8);
insert into tq84_child values (2, 9);
insert into tq84_child values (2, 10);
现在,我想更新 tq84_parent
其列 avg_val
、min_val
和 max_val
反映其子项 [=39= 的相应聚合值].
我可以用这个语句来实现:
update
tq84_parent
set
avg_val = (select avg(val) from tq84_child where id_parent = id),
min_val = (select min(val) from tq84_child where id_parent = id),
max_val = (select max(val) from tq84_child where id_parent = id);
结果如愿:
.mode columns
select * from tq84_parent;
1 2.0 1.0 3.0
2 8.0 6.0 10.0
然而,我担心随着 tq84_parent
和 tq84_child
中记录数量的增长,这种语句的性能不是最佳的,因为几乎相同的查询被进行了三次而不是一次。
所以,我想选择
update
tq84_parent
set
(
avg_val,
min_val,
max_val
)
= (select
avg(val),
min(val),
max(val)
from
tq84_child
where
id_parent = id
);
这行不通。
那么,有没有办法重写我的查询?
由于 id
和 id_parent
是主键和外键,这意味着 id
是唯一的并且 id_parent
总是引用现有的 ID。
这意味着您可以使用 REPLACE
来更改 table,因为唯一键总是被违反(也就是不会进行任何插入),并且 REPLACE
可以采用子查询和一次设置多个列;
REPLACE INTO tq84_parent (id, avg_val, min_val, max_val)
SELECT id_parent, AVG(val), MIN(val), MAX(val)
FROM tq84_child
GROUP BY id_parent;
注意:正如 CL 指出的那样,未在查询中设置的字段将具有其默认值,因为这些行是 replaced,而不是 updated。这意味着如果您在 table 中有其他字段需要保持不变,您的旧方法仍然是这样做的方式。
查询执行的实际速度主要取决于需要完成的磁盘数量I/O。
对于上面显示的数据库架构,每个子查询都需要扫描整个 table。
如果在计算聚合值所需的所有列上都有一个覆盖索引,则每个子查询只需要扫描索引的一小部分,因此在实践中,在为相同的 ID:
CREATE INDEX xxx ON tq84_child(id_parent, val);
我在 sqlite 中有一个经典的父子关系:
create table tq84_parent (
id int primary key,
avg_val float,
min_val float,
max_val float
);
create table tq84_child (
id_parent int references tq84_parent,
val int
);
其中填写如下
insert into tq84_parent (id) values(1);
insert into tq84_parent (id) values(2);
insert into tq84_child values (1, 1);
insert into tq84_child values (1, 2);
insert into tq84_child values (1, 3);
insert into tq84_child values (2, 6);
insert into tq84_child values (2, 7);
insert into tq84_child values (2, 8);
insert into tq84_child values (2, 9);
insert into tq84_child values (2, 10);
现在,我想更新 tq84_parent
其列 avg_val
、min_val
和 max_val
反映其子项 [=39= 的相应聚合值].
我可以用这个语句来实现:
update
tq84_parent
set
avg_val = (select avg(val) from tq84_child where id_parent = id),
min_val = (select min(val) from tq84_child where id_parent = id),
max_val = (select max(val) from tq84_child where id_parent = id);
结果如愿:
.mode columns
select * from tq84_parent;
1 2.0 1.0 3.0
2 8.0 6.0 10.0
然而,我担心随着 tq84_parent
和 tq84_child
中记录数量的增长,这种语句的性能不是最佳的,因为几乎相同的查询被进行了三次而不是一次。
所以,我想选择
update
tq84_parent
set
(
avg_val,
min_val,
max_val
)
= (select
avg(val),
min(val),
max(val)
from
tq84_child
where
id_parent = id
);
这行不通。
那么,有没有办法重写我的查询?
由于 id
和 id_parent
是主键和外键,这意味着 id
是唯一的并且 id_parent
总是引用现有的 ID。
这意味着您可以使用 REPLACE
来更改 table,因为唯一键总是被违反(也就是不会进行任何插入),并且 REPLACE
可以采用子查询和一次设置多个列;
REPLACE INTO tq84_parent (id, avg_val, min_val, max_val)
SELECT id_parent, AVG(val), MIN(val), MAX(val)
FROM tq84_child
GROUP BY id_parent;
注意:正如 CL 指出的那样,未在查询中设置的字段将具有其默认值,因为这些行是 replaced,而不是 updated。这意味着如果您在 table 中有其他字段需要保持不变,您的旧方法仍然是这样做的方式。
查询执行的实际速度主要取决于需要完成的磁盘数量I/O。
对于上面显示的数据库架构,每个子查询都需要扫描整个 table。 如果在计算聚合值所需的所有列上都有一个覆盖索引,则每个子查询只需要扫描索引的一小部分,因此在实践中,在为相同的 ID:
CREATE INDEX xxx ON tq84_child(id_parent, val);