【问题标题】:How to count columns where values differ?如何计算值不同的列?
【发布时间】:2014-08-18 23:27:59
【问题描述】:

我有一张大桌子,我需要检查类似的行。我不需要所有列值都相同,只是相似。行不能是“遥远的”(由对其他表的查询确定),没有值可能相差太大(我已经针对这些条件进行了查询),并且大多数其他值必须相同。我必须预料到一些歧义,所以一两个不同的值不应该破坏“相似性”(好吧,我可以通过只接受“完全相等”的行来获得更好的性能,但这种简化可能会导致错误;我将这样做作为选项)。

我要解决这个问题的方法是通过 PL/pgSQL:创建一个 FOR LOOP 迭代先前查询的结果。对于每一列,我都有一个 IF 测试是否不同;如果是,我增加一个差异计数器并继续。在每个循环结束时,我将值与阈值进行比较,看看是否应该将行保持为“相似”。

与纯 SQL 查询或涉及一些 PL/pgSQL 函数的 SQL 查询相比,这种重 PL/pgSQL 的方法似乎很慢。如果我知道哪些行应该不同,则很容易测试除 X 等效行之外的所有行,但差异可能出现在大约 40 行中的任何一行。 有没有办法通过单个查询来解决这个问题?如果没有,有没有比检查所有行更快的方法?

编辑:我提到了一个表,实际上它是一组以 1:1 关系链接的六个表。我不想解释什么是什么,那是a different question。从一张桌子上推断出我的情况对我来说很容易。所以我简化了它(但没有过度简化它——它应该展示我在那里遇到的所有困难)并做了一个例子来展示我需要什么。 Null 和其他任何东西都应该算作“不同”。无需编写脚本来测试这一切 - 我只需要找出是否有可能以任何比我想象的更有效的方式进行。

重点是我不需要计算行数(像往常一样),只需要计算列数。

EDIT2:previous fiddle - 这不是那么短,所以我把它放在这里只是为了存档。

EDIT3:简化示例here - 只是 NOT NULL 整数,省略了预处理。当前数据状态:

select * from foo;
     id | bar1 | bar2 | bar3 | bar4 | bar5 
    ----+------+------+------+------+------
      1 |    4 |    2 |    3 |    4 |   11 
      2 |    4 |    2 |    4 |    3 |   11 
      3 |    6 |    3 |    3 |    5 |   13 

当我运行select similar_records( 1 ); 时,我应该只得到第 2 行(2 列具有不同的值;这是在限制范围内),而不是 3(4 个不同的值 - 最多在两个差异的限制之外)。

【问题讨论】:

  • 请发布表的定义(如create table)一些示例数据和预期输出。听起来您可能正在寻找类似 @​​987654328@ 的东西
  • 粘贴表定义,粘贴你查询/查询
  • @a_horse_with_no_name:老实说,这不是一张表,而是六张1:1关系的表,而且SQL查询很大,肯定太大了,无法插入SO问题。我考虑了您的 SUM 建议一段时间,但我认为不是这样(但也许我只是误解了一些东西)。无论如何,我会尽快尝试编辑我的问题,提供一些指向 SQLFiddle 或类似内容的链接。
  • 另一个编辑。第一小提琴不清楚;我希望现在很清楚。
  • 你知道SSCCE的缩写吗? Short, Self Contained, Correct (Compilable), Example。我相信你现实生活中的任务很复杂,你必须考虑很多事情。但这与您的 question 几乎没有关系。将您的示例简化为您真正想问的事情,并消除所有其他干扰。

标签: sql postgresql plpgsql


【解决方案1】:

要查找仅在给定的最大列数上不同的行:

WITH cte AS (
   SELECT id
         ,unnest(ARRAY['bar1', 'bar2', 'bar3', 'bar4', 'bar5']) AS col  -- more
         ,unnest(ARRAY[bar1::text, bar2::text, bar3::text
                     , bar4::text, bar5::text]) AS val -- more
   FROM   foo
   )
SELECT b.id, count(a.val <> b.val OR NULL) AS cols_different
FROM   (SELECT * FROM cte WHERE id =  1) a
JOIN   (SELECT * FROM cte WHERE id <> 1) b USING (col)
GROUP  BY b.id
HAVING count(a.val <> b.val OR NULL) < 3 -- max. diffs allowed
ORDER  BY 2;

我忽略了你问题中所有其他令人分心的细节。

用 5 列进行演示。根据需要添加更多。

如果列可以是NULL,您可能需要使用IS DISTINCT FROM 而不是&lt;&gt;

这是使用有点非正统但方便的并行unnest()。两个数组必须具有相同数量的元素才能工作。详情:

SQL Fiddle(以你的为基础)。

【讨论】:

  • 哇,我有一半的预期是,花在提出和改进这个问题上的时间只会导致“这在 SQL 中是不可能的”,而且您不仅理解了我的要求,而且证明了它是可能的。 +1 并接受 :-) 更重要的是,非常感谢您让我尝试在实践中学习 SSCCE!
【解决方案2】:

代替循环将每一行与其他所有行进行比较

select f0.id, f1.id
from foo f0 inner join foo f1 on f0.id < f1.id
where
    f0.bar1 = f1.bar1 and f0.bar2 = f1.bar2
    and
    @(f0.bar3 - f1.bar3) <= 1 
    and
    f0.bar4 = f1.bar4 and f0.bar5 = f1.bar5
    or
    f0.bar4 = f1.bar5 and f0.bar5 = f1.bar4
    and
    @(f0.bar6 - f1.bar6) <= 2
    and
    f0.bar7 is not null and f1.bar7 is not null and @(f0.bar7 - f1.bar7) <= 5 
    or
    f0.bar7 is null and f1.bar7 <= 3
    or
    f1.bar7 is null and f0.bar7 <= 3
    and
    f0.bar8 = f1.bar8
    and
    @(f0.bar11 - f1.bar11) <= 5
;
 id | id 
----+----
  1 |  4
  1 |  5
  4 |  5
(3 rows)

select * from foo;
 id | bar1 | bar2 | bar3 | bar4 | bar5 | bar6 | bar7 | bar8 | bar9 | bar10 | bar11 
----+------+------+------+------+------+------+------+------+------+-------+-------
  1 | abc  |    4 |    2 |    3 |    4 |   11 |    7 | t    | t    | f     |  42.1
  2 | abc  |    5 |    1 |    6 |    2 |    8 |   39 | t    | t    | t     |  19.6
  3 | xyz  |    4 |    2 |    3 |    5 |   14 |   82 | t    | f    |       |    95
  4 | abc  |    4 |    2 |    4 |    3 |   11 |    7 | t    | t    | f     |  42.1
  5 | abc  |    4 |    2 |    3 |    4 |   13 |    6 | t    | t    |       |  37.7

您是否知道and 运算符的优先级高于or?我问是因为看起来你的函数中的 where 子句不是你想要的。我的意思是,在你的表达中,f0.bar7 is null and f1.bar7 &lt;= 3 成为 true 就足以包含这对

【讨论】:

  • 所以我的小提琴具有误导性 :-( 你复制的只是我的“预处理”——我要问的是如何计算值略有不同的行(在这个过滤器给出的范围内)。我会完成小提琴并插入我要替换的部分。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-25
  • 2021-08-05
  • 1970-01-01
  • 1970-01-01
  • 2020-08-23
  • 1970-01-01
相关资源
最近更新 更多