2010-01-28 166 views
4

我正在寻找任何建议来优化SAS程序中的以下PROC SQL语句。涉及的两个表格每个包含约500万条记录,运行时间约为46个小时。优化SQL UPDATE语句所需的建议。使用两百万条记录表

该声明正在寻找更新“旧”表的“新”版本。如果对于“PK_ID”的“旧”表没有列出“3RD_ID”和“CODE”的值,而在“新”表中列出了“PK_ID”,则记录列“3RD_ID”和“CODE”的值。

感谢您的任何建议...(代码格式化真的下面!对于某些原因,我的空间进行展示,缩进...)

PROC SQL _METHOD; 
UPDATE NEW_TABLE AS N 
    SET NEW_2ND_ID=(SELECT 2ND_ID FROM OLD_TABLE AS O 
       WHERE N.PK_ID=0.PK_ID 
        AND N.2ND_ID<>O.2ND_ID 
        AND O.3RD_ID IS NULL 
        AND O.CODE IS NULL 
        AND N.3RD_ID IS NOT NULL 
        AND N.CODE IS NOT NULL 
        AND N.2ND_ID IS NOT NULL) 
     WHERE N.3RD_ID IS NOT NULL 
      AND N.PK_ID IS NOT NULL 
      AND N.CODE IS NOT NULL 
      AND N.2ND_ID IS NOT NULL; 
QUIT; 
+0

在PK_ID,2ND_ID和3RD_ID上的两个表上都有简单的索引,以及两者上都有一个复合索引,包含所有这三个索引。 – whitespy9 2010-01-28 14:35:07

+0

当您找到“工作”解决方案时让我知道 – Pentium10 2010-01-28 17:35:20

回答

3

到目前为止所有的答案都坚定地面向你的问题的SQL部分,但在某种程度上忽略了SAS部分。我强烈建议尝试使用data step update/modify/merge而不是proc sql来进行这种更新。应该可以对两个表进行排序,并应用SQL中类似的逻辑,以确保更新正确的行/列。

我见过类似的更新在2000万或更多的行上在几分钟内运行。

此外,检查出http://runsubmit.com,一个SAS特定的stackoverflow风格的网站,更多SAS特定的答案。

披露:我是SAS员工。我与独立运行的runubmit无关。

+0

谢谢。我终于找到了子弹,并将这个waaaaaaaaaay分解为尽可能小的数据集。合并,然后最终更新。它现在运行平稳。不幸的是,它不得不做这些类型的“额外”数据步骤,我不喜欢使用SAS。我的代码现在在屏幕上长时间执行一个简单的更新语句。如果我必须再一次重做,我会在整个程序中使用复合哈希。 – whitespy9 2010-01-29 16:20:11

0

我觉得现在是嵌套子查询,所以Select语句将会被触发与条件匹配的记录数。

但是,我会建议你去SQL的更新与加入。可以在这里找到解释:http://bytes.com/topic/oracle/answers/65819-sql-update-join-syntax

当您使用JOIN进行更新时,应用适当的索引。

也不是说你不应该使用那些包含2nd_id的索引,它们应该被禁用,然后在更新后重建,因为它可以是海量数据更新。

+0

并非每个DBMS都将相关子查询视为单个查询。SQL Server 2005及以上版本将大多数相关的子查询转换为常规联接相当不错。 – ErikE 2010-01-28 20:32:55

0
UPDATE (
    SELECT O.2ND_ID, N.2ND_ID 
    FROM OLD_TABLE AS O 
    INNER JOIN NEW_TABLE AS N on O.PK_ID=N.PK_ID 
    WHERE N.2ND_ID <> O.2ND_ID 
    AND O.3RD_ID IS NULL 
    AND O.CODE IS NULL 
    AND N.3RD_ID IS NOT NULL 
    AND N.CODE IS NOT NULL 
    AND N.2ND_ID IS NOT NULL 
) t 
set N.2ND_ID = O.2ND_ID 
+1

只有在'old_table'中的'PRIMARY KEY'或'UNIQUE'索引覆盖'pk_id'时才能工作。 – Quassnoi 2010-01-28 14:51:11

+0

@Quassnoi:如果这是一个问题,那么初始海报的查询也是错误的。 – ErikE 2010-01-28 20:33:37

+0

@Emtucifor:如果'pk_id'值是唯一的,即使列没有被声明为'UNIQUE',原始查询也会工作。这个查询要求'old_table.pk_id'被声明为'UNIQUE',否则它将会因臭名昭着的'ORA-01779'而失败。 – Quassnoi 2010-01-28 23:52:51

1

不要使用update,创建一个类似的新表,并使用插入(字段)从两个表中选择字段。

  • 在运行查询之前删除索引。
  • 在运行查询之前删除触发器。

喜欢的东西:完成查询后

insert into NEW_TABLE (field1,field2,NEW_2ND_ID) 
select field1, field2, (SELECT 2ND_ID FROM OLD_TABLE....) from NEW_TABLE 
  • 重新创建索引。
  • 查询完成后重新创建触发器。

+0

我看到你正在发表对其他建议答案的评论,至今尚未发表。您是否尝试过我提出的解决方案? – Pentium10 2010-01-28 17:50:53

+0

我已经使用了部分解决方案。我已经从“旧”表中需要的内容创建了一个子集表。如果我无法找到更新优化的解决方案,我会考虑创建一个插入。 – whitespy9 2010-01-28 18:03:12

+0

请考虑'更新'是慢的,因为锁使用,和IO寻找,相比'全选','插入所有'。有时最好将数据传输到一个单独的表中。 – Pentium10 2010-01-28 18:12:14

3

我不熟悉您使用的SQL的变种(你到底将这个新表您现有的替换表)。但是,无论您是否获得更好的性能,您都应该使用ANSI连接语法。下面是它看起来像在T-SQL,修改你的系统:

UPDATE N 
SET N.2ND_ID = O.2ND_ID 
FROM 
    NEW_TABLE AS N 
    INNER JOIN OLD_TABLE AS O ON N.PK_ID = O.PK_ID 
WHERE 
    N.2ND_ID <> O.2ND_ID 
    AND N.3RD_ID IS NOT NULL 
    AND O.3RD_ID IS NULL 
    AND N.CODE IS NOT NULL 
    AND O.CODE IS NULL 

注意,我删除了额外的条件是没有必要的,例如N.2ND_ID <> O.2ND_ID已确保了这两列不为空。

但是,在两个500万行的表格中,你会得到糟糕的表现。这里有一些想法来加速它。我敢打赌,你可以通过正确的战略组合将这一点降到一小时以内。

  1. 将更新拆分成批(小块,循环遍及整个集合)。虽然这听起来与“不循环,使用集合”的正常数据库建议相反,但它确实不是:您只是使用较小的集合,而不是在行级循环。像这样批量更新的最好方法是“走聚集索引”。我不确定这个术语在你使用的DBMS中是否有意义,但实质上它意味着在每个循环中选择你更新的块,这是基于它们将在你正在更新的表对象中找到的顺序。 PK_ID听起来像是要使用的候选者,但是如果原始表数据没有按此列排序,那么它将变得更加复杂。在T-SQL配料循环可能是这样的:

    DECLARE 
        @ID int, 
        @Count int 
    
    SET @ID = 1 
    SET @Count = 1 
    
    WHILE @Count > 0 BEGIN 
        UPDATE N 
        SET N.2ND_ID = O.2ND_ID 
        FROM 
         NEW_TABLE AS N 
         INNER JOIN OLD_TABLE AS O ON N.PK_ID = O.PK_ID 
        WHERE 
         N.2ND_ID <> O.2ND_ID 
         AND N.3RD_ID IS NOT NULL 
         AND O.3RD_ID IS NULL 
         AND N.CODE IS NOT NULL 
         AND O.CODE IS NULL 
         AND N.PK_ID BETWEEN @ID AND @ID + 4999 
        SET @Count = @@RowCount 
        SET @ID = @ID + 5000 
    END 
    

    这个例子假设你的PK_ID列密密麻麻,每次更新将真正触及5000行。如果情况并非如此,那么切换到使用TOP 5000的方法,并将更新的PK_ID输出到表中,或者在一步中找到下一次更新的@StartID和@EndID,然后执行它。

    根据我的经验,好的批量大小一般在1000到20000行之间。在MS-SQL服务器中,最佳位置似乎低于一个数字,强制从查找切换到扫描(因为最终,数据库引擎认为单次扫描比多次查找便宜,尽管它经常处理500万行表格时出错)。

  2. 首先选择要更新到工作/临时表中的ID和数据,然后加入到该表中。这个想法是通过一个简单的INSERT语句来接受巨大的扫描,然后将索引添加到临时表并执行更新,而不需要复杂的WHERE子句。一旦表仅包含要更新的行和所需的列,不仅WHERE子句可能会丢失其大部分条件,而且临时表每页的行数少且行数多(因为它没有多余的列),这将大大提高性能。这甚至可以在创建New表的“阴影”,然后是Old表的“阴影”,然后是它们之间的连接,最后连接回New表来更新它的阶段完成。虽然这听起来像很多工作,但我认为你会对完全可以提供的疯狂速度感到惊讶。

  3. 任何你可以做的转换从旧表读取,而不是扫描将有助于。你可以做的任何事情来减少用来存放临时数据的磁盘数量(例如5百万行的巨大散列表)将会有所帮助。

+0

+1批处理(分区)。更新非常大的集合可能会产生巨大的事务/锁定开销和/或I/O活动。 – Arvo 2010-01-28 19:07:21

+0

+1也用于分区。一个500万行的单笔交易对数据库引擎来说是一场噩梦。很可能内存缓冲区已满,并且在知道Tx是否将被提交或回滚之前,更改会刷新到磁盘。 – 2010-01-28 21:03:52

+0

由于某些奇怪的原因,SAS Proc SQL不允许与更新进行连接。如此巨大的障碍。 – 2010-01-29 20:30:51

0

您也可以尝试将新表放在与旧表不同的设备中,以利用其并行性。当然,如果你能说服DBA。