備註:以下更新的演算法適合所有資料庫,示例採用postgresql,其它資料庫只用修改語法即可.
題外話:軟體思想很重要,不要侷限於某種語言\工具\資料庫,思想才是最重要的.有時候乙個靈光一閃的想法說不定就是乙個好的軟體.
開始談正事,在update全表資料時,常規寫法下大家經常抱怨更新太慢,語句如下:
update 表 set 欄位1=0
,欄位n=n
實際上這裡由於在update前,資料庫需要讀取整個表,然後才開始update,所以上面的語句可以分成兩步看
select
*from 表
update 表 set 欄位1
=select
.欄位,欄位n=
select
.欄位n where 主鍵=
select
.主鍵
select是乙個全表掃瞄,update慢的原因主要就是這裡了.
知道了問題所在,現在要想辦法避開全表掃瞄,使用primary key掃瞄,並且不一次更新完,而是分批次更新,直到全表資料更新完成.
drop
table
ifexists test;
create
table test(
objectid bigint
notnull
, name text
notnull
,describe jsonb,
flag integer
default(2
)not
null
,/*演示要更新的字段*/
constraint pk_test_objectid primary
key(objectid)
with
(fillfactor=60
))with
(fillfactor=60
);
經常更新的表建議設定fillfactor係數以提公升update和delete效能,預設為90.
drop function if exists gen_random_zh(int,int);
create or replace function gen_random_zh(int,int)
returns text
as $$
select string_agg(chr((random()*(20901-19968)+19968 )::integer) , '') from generate_series(1,(random()*($2-$1)+$1)::integer);
$$ language sql;
注意:這個函式是sql語言函式
--$1為起始編號,每呼叫一次寫入1000000條記錄
drop
function
ifexists ins_test(
bigint);
create
orreplace
function ins_test(
bigint
)returns void
as $$
declare
begin
for i in
1..1000
loop
insert
into test(objectid,name)
select
((id +
(i-1)*
1000)+
($1-1)
)as objectid,gen_random_zh(8,
32)as name from generate_series(1,
1000
)as id;
--raise notice '%', i;
endloop
;end
;$$ language plpgsql;
注意:這個函式是plpgsql語言函式
匯入1000萬測試資料,為提高效率,開10個程序匯入,每個寫入100萬.
根據機器配置不同,每次執行時間約20秒至幾分鐘不等.
select ins_test(1)
;select ins_test(
1000001);
select ins_test(
2000001);
select ins_test(
3000001);
select ins_test(
4000001);
select ins_test(
5000001);
select ins_test(
6000001);
select ins_test(
7000001);
select ins_test(
8000001);
select ins_test(
9000001
);
為節約時間全部匯入完成後用以下命令統計表資訊
analyze test;
5.1常規方法
沒有測試,肯定很慢
update test set flag=1;
analyze test;
5.2分批層逐條更新do $$
declare
v_currentid bigint
; v_rec record;
v_index integer
; v_isok boolean
;begin
v_currentid :=
0; v_index :=0;
loop
v_isok :=
false
; v_index := v_index +1;
for v_rec in
select objectid from test where objectid>v_currentid order
by objectid limit
1000
loop
update test set flag=
1where objectid=v_rec.objectid;
v_isok:=
true
; v_currentid := v_rec.objectid;
endloop
;--raise notice '%,%', v_index,v_currentid;if(
false
= v_isok )
then
return
;endif;
endloop
;end
;$$;
--time: 320023.745 ms
analyze test;
--第二個版本
do $$
declare
v_currentid bigint
; v_rec record;
begin
v_currentid :=-1
;loop
for v_rec in
(with cte as
(select objectid from test where objectid>v_currentid order
by objectid limit
1000
)select array_agg(objectid)
as ids from cte)
loop
update test set flag=
1where objectid=
any(v_rec.ids)
; v_currentid := v_rec.ids[
1000];
endloop
;--raise notice '%', v_currentid;
if( v_currentid is
null
)then
return
;endif;
endloop
;end
;$$;
do $$
declare
v_currentid bigint
;begin
v_currentid :=0;
loop
with cte as
(insert
into enterprise_mores(objectid,bank,registered,number)
select objectid,bank,registered,number from enterprises where objectid>v_currentid order
by objectid limit
1000
returning objectid
)select
max(objectid)
into v_currentid from cte;
raise notice '%'
, v_currentid;
if( v_currentid is
null
)then
return
;endif;
endloop
;end
;$$;
(資料庫 MySQL)表 資料更新
如果我們需要修改或更新 mysql 中的資料,我們可以使用 sql update 命令來操作。以下是update 命令修改 mysql 資料表資料的通用 sql 語法 update low priority ignore table name set column name1 expr1,colum...
資料庫 資料更新
資料庫更新操作有三種 在表中新增若干行資料 修改表中的資料和刪除表中的若干行資料。sql中有三類相應的語句,分別是插入資料 insert 修改資料 update 刪除資料 delete insert values 插入單行或多行元組資料 例 向資料庫mysql test的表customers中插入這...
快速更新 資料庫物件 擁有者
今天恢復資料庫的時候,發現有很多procedure的所有者不是dbo,而是一些其他使用者,這樣造成使用者不能訪問它。首先想到的是刪除這些procedure然後再重建,發現數量太多不太現實。突然又想到可以把所有儲存過程列出來,然後用procedure sp changeobjectowner 來更改它...