有这样一个表test,需要成批的插入数据;表中列a要求唯一;对于a对应的值,如果表中没有则插入数据,有则更新
create table test(a varchar2(100) not null,b varchar2(10) not null);
create unique index un_test_a on test(a);
我编写了如下的包,
create or replace package p_test is
TYPE Varchar_Array IS TABLE OF VARCHAR(4000) INDEX BY BINARY_INTEGER;
TYPE Integer_Array IS TABLE OF INTEGER INDEX BY BINARY_INTEGER;
PROCEDURE Proc_test
(
Arr_IndexIN OUT Varchar_Array,
Arr_CountIN OUT Varchar_Array,
Arr_Errors IN OUT Varchar_Array
);
end p_test;
create or replace package body p_test is
PROCEDURE Proc_Test
(
Arr_IndexIN OUT Varchar_Array,
Arr_CountIN OUT Varchar_Array,
Arr_Errors IN OUT Varchar_Array --用于存放出错
) IS
errors NUMBER;
dml_errors EXCEPTION;
err_index INTEGER := 1;
PRAGMA EXCEPTION_INIT(dml_errors, -24381);
BEGIN
FORALL i IN Arr_Index.FIRST .. Arr_Index.LAST SAVE EXCEPTIONS
UPDATE Test a
SET a.b= a.b || Arr_Count(i)
WHERE a.a = Arr_Index(i);
COMMIT WRITE BATCH NOWAIT;---允许丢部分数据
FOR i IN Arr_Index.FIRST .. Arr_Index.LAST LOOP
IF SQL%BULK_ROWCOUNT(i) = 0 THEN
BEGIN
INSERT INTO Test
VALUES (Arr_Index(i),Arr_Count(i));
COMMIT WRITE BATCH NOWAIT;
EXCEPTION
WHEN OTHERS THEN
arr_errors(err_index) := TO_CHAR(i) || '|' || SQLERRM;
err_index := err_index + 1;
END;
END IF;
END LOOP;
EXCEPTION
WHEN dml_errors THEN
errors := SQL%BULK_EXCEPTIONS.COUNT;
FOR i IN 1..errors LOOP
arr_errors(err_index) := TO_CHAR(i) || '|' || TO_CHAR(SQL%BULK_EXCEPTIONS(i).ERROR_INDEX) || '|' || SQLERRM(-SQL%BULK_EXCEPTIONS(i).ERROR_CODE);
err_index := err_index + 1;
END LOOP;
END Proc_Test;
end p_test;
使用php调用这个包,循环调用,在每次执行完之后都会提交。语句如下
.......
for ($j=0;;$j++){
.......
$stmt = @oci_parse($conn, "BEGIN P_Test.Proc_Test(:arr_index,:arr_count,:arr_errors); END;");
if ($stmt){
oci_bind_array_by_name($stmt, ":arr_index", $arr_in_index, $arr_realcount, -1, SQLT_CHR);
oci_bind_array_by_name($stmt, ":arr_count", $arr_in_count, $arr_realcount, -1, SQLT_INT);
oci_bind_array_by_name($stmt, ":arr_errors", $arr_errors, $arr_realcount, 4000, SQLT_CHR);
if (!@oci_execute($stmt)){
//错误处理
$e = @oci_error($stmt);
$errorcount = $errorcount + count($arr_errors);
}
@oci_commit($conn);
}else{
//未连接到数据库
$e = @oci_error($stmt);
}//if ($stmt)
}
经过使用发现,这样会产生死锁,而且是持续的锁,只要发生,以后会继续锁住。请问一下,怎样避免这种情况的发生,或者怎样调整设计,来实现这种类似merge语句的情况?表中每天的数据量在百万左右,每五分钟处理一次,更新(插入)几万条。谢谢!
|