【问题标题】:Compare result of two table functions using one column from each使用每个表中的一列比较两个表函数的结果
【发布时间】:2015-03-02 11:34:19
【问题描述】:

根据here 的说明,我创建了两个使用EXECUTE FORMAT 并返回(int,smallint) 的同一张表的函数。

示例定义:

CREATE OR REPLACE FUNCTION function1(IN _tbl regclass, IN _tbl2 regclass, 
IN field1 integer) 
RETURNS TABLE(id integer, dist smallint)

CREATE OR REPLACE FUNCTION function2(IN _tbl regclass, IN _tbl2 regclass, 
IN field1 integer) 
RETURNS TABLE(id integer, dist smallint)

两个函数返回完全相同的行数。示例结果(将始终按 dist 排序):

(49,0)
(206022,3)
(206041,3)
(92233,4)

有没有办法比较相同行的两个函数之间的第二个字段的值,以确保两个结果相同:

例如:

SELECT
function1('tblp1','tblp2',49),function2('tblp1_v2','tblp2_v2',49)

返回类似:

(49,0)      (49,0)
(206022,3)  (206022,3)
(206041,3)  (206041,3)
(92233,4)   (133,4)

虽然我并不期待相同的结果(每个函数都是一个 topK 查询,并且我有任意中断的关系/在第二个函数中进行了一些优化以获得更快的性能)我可以确保这两个函数如果每一行的结果中的第二个数字相同,则返回正确的结果。在上面的例子中,我可以确保得到正确的结果,因为:

1st row 0 = 0,
2nd row 3 = 3,
3rd row 3 = 3,
4th row 4 = 4

尽管对于第 4 行,92233!=133

有没有办法只获取每个函数结果的第二个字段,以批量比较它们,例如类似于:

SELECT COUNT(*)
FROM 
(SELECT
function1('tblp1','tblp2',49).field2,
function2('tblp1_v2','tblp2_v2',49).field2 ) n2
WHERE  function1('tblp1','tblp2',49).field2 != function1('tblp1','tblp2',49).field2;

我使用的是 PostgreSQL 9.3。

【问题讨论】:

  • 您必须使用select * from function1('tblp1','tblp2',49) 来设置返回函数。不要将它们放入选择列表中。
  • 您添加了(will be always ordered by dist)。你确定,这不会为误报打开可能性吗?
  • @ErwinBrandstetter 这是一个topK查询(ORDERED BY dist LIMIT k),所以函数内部的结果是按dist排序的。我想检查一个具体的例子,我得到一个 dist = 1 的答案,两个 dist = 3 的答案和一个 dist = 4 的答案 k = 4。这两个函数都在不同的表上运行(第二个函数在优化表上运行),我想确保两个函数对所有 k 结果返回相同的 dist。在我们的例子中,dist 1 和 dist 3 的结果总是相同的(没有其他结果与 dist=3),但在 4 的情况下,我可能会得到 dist =4 的许多结果。
  • 那么,行是按匹配的顺序返回的吗?其余的对于这个问题并不重要。
  • @a_horse_with_no_name:这实际上没有抓住问题的重点。

标签: sql postgresql postgresql-9.3 set-returning-functions


【解决方案1】:

有没有办法只获取每个函数结果的第二个字段,对它们进行批量比较?

以下所有答案均假设行按匹配顺序返回。

Postgres 9.3

具有从 SRF 函数中分解行的古怪特性,并行返回 相同的行数:

SELECT count(*) AS mismatches
FROM  (
   SELECT function1('tblp1','tblp2',49) AS f1
        , function2('tblp1_v2','tblp2_v2',49) AS f2
   ) sub
WHERE  (f1).dist <> (f2).dist;  -- note the parentheses!

行类型周围的括号对于消除可能的表引用的歧义是必要的。 Details in the manual here.

如果返回的行数不同,则默认为行的笛卡尔积(这会完全破坏它)。

Postgres 9.4

WITH ORDINALITY 动态生成行号

您可以使用WITH ORDINALITY 即时生成行号,而无需依赖SELECT 列表中SRF 函数的配对结果:

SELECT count(*) AS mismatches
FROM      function1('tblp1','tblp2',49)       WITH ORDINALITY AS f1(id,dist,rn)
FULL JOIN function2('tblp1_v2','tblp2_v2',49) WITH ORDINALITY AS f2(id,dist,rn) USING (rn)
WHERE  f1.dist IS DISTINCT FROM f2.dist;

这适用于每个函数的相同行数以及不同的行数(这将被视为不匹配)。

相关:

ROWS FROM逐行加入集合

SELECT count(*) AS mismatches
FROM   ROWS FROM (function1('tblp1','tblp2',49)
                , function2('tblp1_v2','tblp2_v2',49)) t(id1, dist1, id2, dist2)
WHERE  t.dist1 IS DISTINCT FROM t.dist2;

相关答案:

旁白:
EXECUTE FORMAT 不是一套 plpgsql 功能。 RETURN QUERY 是。 format() 只是一个方便的构建查询字符串的函数,可以在 SQL 或 plpgsql 的任何地方使用。

【讨论】:

  • 你确定它适用于 PostgreSQL 9.3 吗?我收到一个语法错误,我只在 postgresql.org/docs/devel/static/functions-srf.html 上看到过。
  • @Alexandros 这是 9.4+ 的功能
  • @Alexandros 据我了解,即使它在 9.3 中工作,它也会对结果集进行编号,可能与函数内部的顺序不同,因此必须在函数内部分配行号。
  • @Alexandros:是的,抱歉,WITH ORDINALITY 是针对 pg 9.4,而不是 9.3。我又加了一些。
  • @ClodoaldoNeto: WITH ORDINALITY 编号行,就像它们从函数返回一样。所以是的,如果返回的行顺序不匹配,我看不出有办法解决这个事后问题,我们需要在每个函数 inside 添加行号并返回它们...
【解决方案2】:

无法保证从函数返回行的顺序。如果您可以从函数中返回row_number()(下例中为rn),那么:

select
    count(f1.dist is null or f2.dist is null or null) as diff_count
from
    function1('tblp1','tblp2',49) f1
    inner join
    function2('tblp1_v2','tblp2_v2',49) f2 using(rn)

【讨论】:

    【解决方案3】:

    供将来参考:

    检查行数的差异:

    SELECT 
    ABS(count(f1a.*)-count(f2a.*))  
    FROM
    (SELECT f1.dist, row_number()  OVER(ORDER BY f1.dist) rn
    FROM
    function1('tblp1','tblp2',49) f1)
    f1a FULL JOIN 
    
    (SELECT f2.dist, row_number() OVER(ORDER BY f2.dist) rn
    FROM
    function2('tblp1_v2','tblp2_v2',49) f2) f2a
    USING (rn);
    

    检查相同有序行的 dist 差异:

    SELECT 
    COUNT(*)  
    
    FROM
    
    (SELECT f1.dist, row_number()  OVER(ORDER BY f1.dist) rn
    FROM
    function1('tblp1','tblp2',49) f1)
    f1a 
    (SELECT f2.dist, row_number() OVER(ORDER BY f2.dist) rn
    FROM
    function2('tblp1_v2','tblp2_v2',49) f2) f2a
    WHERE f1a.rn=f2a.rn
    AND f1a.distance <> f2a.distance;
    

    一个简单的 OVER() 也可以工作,因为函数的结果已经被排序但被添加以进行额外检查。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2013-08-27
      • 1970-01-01
      相关资源
      最近更新 更多