【问题标题】:How to efficiently compare two subsets of rows of a large table?如何有效地比较大表的两个行子集?
【发布时间】:2017-12-31 14:58:51
【问题描述】:

我使用 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. 遍历两个游标,并比较值。

它工作得很好,但我认为在 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;

对比

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 过程。因此,我无法检验假设。

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

解决此类问题的最佳方法是什么?

【问题讨论】:

  • 缺少一些关键元素以获得最佳答案:精确的表定义(CREATE TABLE 显示数据类型和约束的语句)。有关数据分布的更多信息:每个 (department, key) 有多少行:min、avg、max。现有索引。 point out differences 用简单的英语听起来不错,但没有定义比较和结果的确切要求。更具体。

标签: database postgresql stored-procedures cursor plpgsql


【解决方案1】:

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

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;

【讨论】:

    【解决方案2】:

    为什么不能使用游标而不是自连接?比如:

    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';
    

    第一部分处理所有常见情况,两个联合提取缺失值。我原以为这会比游标方法快得多。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-11-02
      • 2022-01-10
      • 2017-06-12
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多