这个问题困扰了我们组的很多人。不知道这边有没有人可以提些意见帮忙解决?

davidyujie 2010-01-27 08:50:36
以下存储过程从程序上看没什么问题,但是我来描述 一下问题:
1)TA723008 简称A表,TA723003_prepare简称B表。
2)A表里面有4千万条以上的数据,B表里面有1万条以上的数据,现在要对A表的某字段更新,匹配条件是A表中的日期为20071231之前的。然后就是拿A表中的某个字段去关联B表的字段,得到B表中的中文名然后更新到A表中去。
可是因为客观原因,不可能让一个存储过程跑一个晚上还在更新,因为按照以下的程序是一个笛卡尔乘积,拿一个表的全表扫描去匹配另一个表的全表扫描。
3)程序详解:逐条更新,然后通过计数器每更新1万条就commit一次。
希望有人能提出有建设性的方案来解决笛卡尔乘积 然后又能满足以上所说功能。不甚感激!!!!



PROCEDURE R_MOVE_TA723008(OI_FLAG OUT INTEGER, --0 成功, -1 失败
OS_MESG OUT VARCHAR2 --出错详细信息
) IS
VI_COUNT INTEGER := 0; --计数器
VI_COUNT_all INTEGER := 0; --计数器
CURSOR CUR_TA723008 IS
SELECT ROWID, T.*
FROM BIFT.TA723008 T
WHERE T.TA723008002 <= '20071231'
AND t.ta723008015 IS NULL;

BEGIN
OI_FLAG := 0;
OS_MESG := '更新开始';

FOR VT_TA723008 IN CUR_TA723008 LOOP
UPDATE TA723008
SET TA723008015 = (SELECT TA723003007
FROM TA723003_PREPARE
WHERE TA723003_PREPARE.TA723003005 = TA723008009
AND ROWNUM = 1)
WHERE TA723008.ROWID = VT_TA723008.ROWID;

VI_COUNT := VI_COUNT + 1;
VI_COUNT_all := VI_COUNT_all + 1;
IF (VI_COUNT >= 10000) THEN
COMMIT;
VI_COUNT := 0;
END IF;
END LOOP;
OI_FLAG := 0;
OS_MESG := '更新结束';
COMMIT;
EXCEPTION
WHEN OTHERS THEN
OI_FLAG := 1;
OS_MESG := SUBSTRB(OS_MESG || '时出错! 错误代码:' || SQLCODE || ',错误信息:' ||
SQLERRM,
1,
400);

RETURN;

END R_MOVE_TA723008;
...全文
123 10 打赏 收藏 举报
写回复
10 条回复
切换为时间正序
请发表友善的回复…
发表回复
fingerfox 2010-03-23
  • 打赏
  • 举报
回复
SELECT ROWID, T.*
FROM BIFT.TA723008 T
WHERE T.TA723008002 <= '20071231'
AND t.ta723008015 IS NULL;

这样选出来太多行了,把表B连接进来
shiyiwan 2010-01-28
  • 打赏
  • 举报
回复
补充一下,去重之后,在A表关联条件那一列加索引

然后使用for all批量更新。
shiyiwan 2010-01-28
  • 打赏
  • 举报
回复
如果是一行的所有列都需要更新,那么不能用merge了,以为关联条件列也会被更新。

我想的是既然关联条件TA723003_PREPARE.TA723003005的值有很多重复,你可以建一个视图先去重,
select min(c1),min(c2),... from TA723003_PREPARE t1 group by TA723003005;

该表1万条数据也不多,而且既然你在更新时使用rownum <= 1,说明这些值应该都是一致或相似的,随便用哪个值更新A表都可以,所以select后面的全部列都加上Min应该没关系。
shiyiwan 2010-01-28
  • 打赏
  • 举报
回复
所有列的字段?

其实列和字段是一个意思,你是指一行的所有列(字段)需要更新还是指所有行的某一个列(字段)要更新?

[Quote=引用 3 楼 davidyujie 的回复:]
之所以要选择所有的列,是因为这个表里所有列的字段都要更新。还有就是您说的加索引,如何添加,能否附在代码上。谢谢!

[/Quote]
wh62592855 2010-01-27
  • 打赏
  • 举报
回复
UPDATE TA723008
SET TA723008015 = (SELECT TA723003007
FROM TA723003_PREPARE
WHERE TA723003_PREPARE.TA723003005 = TA723008009
AND ROWNUM = 1)
WHERE TA723008.ROWID = VT_TA723008.ROWID;

所有字段都要更新?
这里好像你只更新了TA723008015这一个字段而已啊
davidyujie 2010-01-27
  • 打赏
  • 举报
回复
坦白说这个存储过程是必须在白天工作的时候更新,按天更新的话量还是不变的呀?
[Quote=引用 4 楼 crazylaa 的回复:]
楼主可以考虑下按天更新。用job在晚上没人工作的时候调用存储过程去执行update
[/Quote]
crazylaa 2010-01-27
  • 打赏
  • 举报
回复
楼主可以考虑下按天更新。用job在晚上没人工作的时候调用存储过程去执行update
davidyujie 2010-01-27
  • 打赏
  • 举报
回复
之所以要选择所有的列,是因为这个表里所有列的字段都要更新。还有就是您说的加索引,如何添加,能否附在代码上。谢谢!
[Quote=引用 1 楼 wh62592855 的回复:]
CURSOR CUR_TA723008 IS
      SELECT ROWID, T.*
        FROM BIFT.TA723008 T
      WHERE T.TA723008002 <= '20071231'
        AND t.ta723008015 IS NULL;

这里游标为什么要取出表里的所有列呢?
只取出要比较的那一列不就好了么 在这列上加上索引
[/Quote]
wh62592855 2010-01-27
  • 打赏
  • 举报
回复
楼主你去百度一下merge into

不知道你们试过没有
你这种情况也许用得到
wh62592855 2010-01-27
  • 打赏
  • 举报
回复
CURSOR CUR_TA723008 IS
SELECT ROWID, T.*
FROM BIFT.TA723008 T
WHERE T.TA723008002 <= '20071231'
AND t.ta723008015 IS NULL;

这里游标为什么要取出表里的所有列呢?
只取出要比较的那一列不就好了么 在这列上加上索引
发帖
Oracle 高级技术

3472

社区成员

Oracle 高级技术相关讨论专区
社区管理员
  • 高级技术社区
加入社区
帖子事件
创建了帖子
2010-01-27 08:50
社区公告
暂无公告