我正在编写一个Django-ORM增强版,它尝试缓存模型并推迟模型保存直到事务结束。这几乎已经完成了,但是我在SQL语法中遇到了意想不到的困难。
我不是一名DBA,但据我所知,数据库对于许多小型查询并不能真正有效地工作。几个更大的查询要好得多。例如,最好使用大批量插入(比如一次100行)而不是100个单行插入。
现在,从我所看到的,SQL并没有真正提供任何语句来对表执行批量更新。这个词似乎是令人困惑所以,我会解释我的意思。我有一个任意数据数组,每个条目描述一个表中的一行。我想更新表中的某些行,每个行都使用数组中相应条目的数据。这个想法非常类似于批量插入。
例如:我的表可以有两列"id"
和"some_col"
。现在,描述批量更新数据的数组包含三个条目(1, 'first updated')
,(2, 'second updated')
和(3, 'third updated')
。在更新之前,该表包含行:(1, 'first')
,(2, 'second')
,(3, 'third')
。
我来到这篇文章:
Why are batch inserts/updates faster? How do batch updates work?
这似乎做了我想要的,但我最终无法弄清楚语法。
我还可以删除所有需要更新的行,并使用批量插入重新插入它们,但我发现很难相信这实际上会有更好的表现。
我使用PostgreSQL 8.4,因此这里也可以使用一些存储过程。然而,当我计划最终开源项目时,我们非常欢迎任何更便携的想法或在不同的RDBMS上做同样事情的方法。
跟进问题:如何批处理“插入或更新”/“upsert”语句?
测试结果
我已经执行了100次10次插入操作,分布在4个不同的表中(总共1000个插入)。我使用PostgreSQL 8.4后端在Django 1.3上进行了测试。
结果如下:
结论:在单个connection.execute()中执行尽可能多的操作。 Django本身带来了巨大的开销。
免责声明:除了默认主键索引之外,我没有引入任何索引,因此插入操作可能会因此而运行得更快。
答案 0 :(得分:50)
您可以通过Ketema修改三列的批量插入:
INSERT INTO "table" (col1, col2, col3)
VALUES (11, 12, 13) , (21, 22, 23) , (31, 32, 33);
变成:
INSERT INTO "table" (col1, col2, col3)
VALUES (unnest(array[11,21,31]),
unnest(array[12,22,32]),
unnest(array[13,23,33]))
用占位符替换值:
INSERT INTO "table" (col1, col2, col3)
VALUES (unnest(?), unnest(?), unnest(?))
您必须将数组或列表作为参数传递给此查询。这意味着你可以在不进行字符串连接的情况下进行大量的批量插入(以及它的所有谜题和危险:sql注入和引用地狱)。
PostgreSQL已将FROM扩展添加到UPDATE。您可以这样使用它:
update "table"
set value = data_table.new_value
from
(select unnest(?) as key, unnest(?) as new_value) as data_table
where "table".key = data_table.key;
手册缺少一个很好的解释,但postgresql-admin mailing list上有一个例子。我试着详细说明一下:
create table tmp
(
id serial not null primary key,
name text,
age integer
);
insert into tmp (name,age)
values ('keith', 43),('leslie', 40),('bexley', 19),('casey', 6);
update tmp set age = data_table.age
from
(select unnest(array['keith', 'leslie', 'bexley', 'casey']) as name,
unnest(array[44, 50, 10, 12]) as age) as data_table
where tmp.name = data_table.name;
StackExchange上还有other posts使用UPDATE...FROM..
子句而不是子查询来解释VALUES
。它们可能更容易阅读,但仅限于固定数量的行。
答案 1 :(得分:13)
我使用了3种策略进行批量交易工作:
flush()
执行Session
方法,而不是基础JDBC连接。它完成了与JDBC批处理相同的事情。顺便提一下,Hibernate还支持集合提取中的批处理策略。如果使用@BatchSize
注释集合,则在获取关联时,Hibernate将使用IN
而不是=
,从而导致更少的SELECT
语句来加载集合。
答案 2 :(得分:12)
批量插入可以这样完成:
INSERT INTO "table" ( col1, col2, col3)
VALUES ( 1, 2, 3 ) , ( 3, 4, 5 ) , ( 6, 7, 8 );
将插入3行。
多重更新由SQL标准定义,但未在PostgreSQL中实现。
引用:
“根据标准,列列表语法应该允许列表 从单个行值表达式分配的列,例如 一个子选择:
UPDATE帐户SET(contact_last_name,contact_first_name)= (SELECT last_name,first_name FROM salesmen sales在哪里salesmen.id = accounts.sales_id);“
参考:http://www.postgresql.org/docs/9.0/static/sql-update.html
答案 3 :(得分:2)
将json填充到记录集(postgresql 9.3 +)
非常快big_list_of_tuples = [
(1, "123.45"),
...
(100000, "678.90"),
]
connection.execute("""
UPDATE mytable
SET myvalue = Q.myvalue
FROM (
SELECT (value->>0)::integer AS id, (value->>1)::decimal AS myvalue
FROM json_array_elements(%s)
) Q
WHERE mytable.id = Q.id
""",
[json.dumps(big_list_of_tuples)]
)
答案 4 :(得分:0)
关闭自动提交,最后只进行一次提交。在纯SQL中,这意味着在开始时发出BEGIN,在结尾发出COMMIT。您需要创建一个function才能进行实际的upsert。