如何有效地比较大表的两个行的子集?

时间:2017-07-25 15:19:01

标签: database postgresql stored-procedures cursor plpgsql

我使用PostgreSQL 9.6,我的表架构如下:department, key, value1, value2, value3, ...每个部门都有数亿个唯一密钥,但所有部门的密钥集大致相同。某些部门可能不存在某些键,但这种情况很少见。

我想准备一份报告,指出两个部门指出每个键值的差异(比较涉及一些仅基于键值的逻辑)。

我的第一个方法是在python中编写一个外部工具:

  1. 为查询创建服务器端游标:SELECT * FROM my_table WHERE department = 'ABC' ORDER BY key;
  2. 为查询创建另一个服务器端游标:SELECT * FROM my_table WHERE department = 'XYZ' ORDER BY key;
  3. 遍历两个游标,并比较值。
  4. 它工作正常,但我认为在PostgreSQL中执行存储过程内的比较会更有效。我编写了一个存储过程,它接受两个游标作为参数,迭代它们并比较值。任何差异都写入临时表。最后,外部工具在临时表上进行迭代 - 那里不应该有很多行。

    我认为后一种方法会更有效率,因为它不需要在数据库之外传输大量数据。 令我惊讶的是,结果却慢了近40%。

    为了隔离问题,我比较了在存储过程中迭代游标的性能,以及python:

    FETCH cur_1 INTO row_1;
    WHILE (row_1 IS NOT NULL) LOOP
        rows = rows + 1;
        FETCH FROM cur_1 INTO row_1;
    END LOOP;
    

    VS

    conn = psycopg2.connect(PG_URI)
    cur = conn.cursor('test')
    cur.execute(query)
    cnt = 0
    for row in cur:
        cnt += 1
    

    两种情况下的查询都是相同的。同样,外部工具更快。 我的假设是,这是因为存储过程逐行获取行(FETCH FROM curs_1 INTO row_1),而应用程序以2000的批量获取行。但是我找不到从光标获取一批行的方法在PgSQL程序中。因此,我无法检验这一假设。

    所以我的问题是可以加快我的存储过程吗?

    这类问题的最佳方法是什么?

2 个答案:

答案 0 :(得分:0)

为什么不进行自我加入而不是使用游标?类似的东西:

SELECT t1.key, t1.value1 - t2.value1 as diff1, 
t1.value2 - t2.value2 as diff2,  ...
FROM my_table t1 inner join my_table t2 on t1.key = t2.key
WHERE t1.department = 'XYZ' and t2.department = 'ABC'
UNION
SELECT t1.key, t1.value1 as diff1, 
t1.value2  as diff2,  ...
FROM my_table t1 WHERE NOT EXISTS (SELECT 1 FROM my_table t2 WHERE
t1.key = t2.key AND t2.dept = 'ABC') AND t1.dept = 'XYZ'
UNION
SELECT t1.key, t1.value1 as diff1, 
t1.value2  as diff2,  ...
FROM my_table t1 WHERE NOT EXISTS (SELECT 1 FROM my_table t2 WHERE
t1.key = t2.key AND t2.dept = 'XYZ') AND t1.dept = 'ABC';

第一部分涉及所有常见案例,两个工会拿起缺失值。我原本以为这会比光标方法快得多。

答案 1 :(得分:0)

这可能会更快,因为它只会返回至少一个值中不同的行:

select *
from ( 
   SELECT key, value1, value2, value3  
   FROM my_table 
   WHERE department = 'ABC'
) d1
  full join (
    SELECT key, value1, value2, value3  
    FROM my_table 
    WHERE department = 'XYZ'
  ) d2 using (key)
where d1 is distinct from d2;