sql - SQL复制与空值

标签 sql database postgresql algorithm

由于一些疯狂的商业原因,从我们的客户那里得到相当混乱的数据,我有以下问题;
1)我有一个表有6个半唯一标识符和一个自动递增的唯一ID。该表有更多字段但是,这些对这次讨论并不重要。字段所包含的数据类型也不准确。
2)我想获得至少参与一个重复关系的所有行的唯一ID的列表。(标识所有表示重复的行对时没有任何附加值。但是,如果解决方案提供了这一点,那么检索一组重复行就相当简单了。所以,那也没关系)
3)副本定义为:
3A)对于这6个字段中的每一个,记录A必须与记录B匹配,或者其中一个必须为空
3b)至少有一个字段必须完全匹配(即两者都不为空)
4)所有可能重复的感兴趣字段都是字符串,而不是空字符串。许多行至少有一个感兴趣的字段为空,但(至少假设我们的摄取逻辑工作正常)没有一个行可以有3个以上的字段为空。
5)精确的字符串内容匹配是可以的我们不需要任何基于正则表达式的、不区分大小写的…有点匹配。
6)表中的实际副本相当罕见。
7)我们正在运行PostgreSQL 9可以使用特定于数据库的功能。
8)该表有500000行因此,下面提供的我刚开始使用的天真的查询需要太长时间才能实现。据推测,它主要在指数时间内运行。理想情况下,运行在中端服务器上的结果应该在一分钟内返回。

SELECT a.id
FROM myTable a
JOIN myTable b ON a.id < b.id
AND (a.field1 = b.field1 OR a.field1 IS NULL OR b.field1 IS NULL )
AND (a.field2 = b.field2 OR a.field2 IS NULL OR b.field2 IS NULL)
....
WHERE 
a.field1 = b.field1 OR a.field2 = b.field2 ...

9)我还研究了使用“分组”但是,如果其中一个分组列包含空值,而另一个包含值,则“group by”不认为两行相等。除非有办法实现这种行为,否则group by不适用于我的“两个都相等或至少一个为空”逻辑。
10)可以假定每一行中可能出现的值集与其他列不重叠。也就是说,除了null之外,您不会期望字段1中的值出现在字段2的任何行中。
更新:很抱歉没有信息。我将尽可能地提供一个近似的表模式。不幸的是,所讨论的项目处于防御状态,甚至仅仅是表的字段名就可以显示有关操作安全的信息。
CREATE TABLE a (
id serial NOT NULL PRIMARY KEY,
f1 character varying,
f2 character varying,
f3 character varying,
f4 character varying,
f5 character varying,
f6 character varying,
...Other columns that aren't really relevant
)

CREATE INDEX f1_idx
  ON public.a
  USING btree
  (f1 COLLATE pg_catalog."default");

...Same index for the other 5 fields.

为了便于参考,我将复制Lorenze Albe的问题并在这里回答。
如果你有三排
(1,2,3,4,空,6)
(1, 2, 3, NULL, 5, NULL)

(1, 2, 3, 4, 7, NULL)

哪些是重复的?
(1, 2, 3, NULL, 5, NULL)


(1, 2, 3, 4, 7, NULL)

不重复,因为字段5在两者中均为非空且它们不相等。另外两个是复制品。
为了清楚起见,我将再举几个自己的例子。(为了完整起见,我将以字符串的形式提供行示例。但是,正如我所说的,它们的弦性并不重要,因为我们需要精确的弦匹配。
 ("1", "2", "3", "4", NULL, NULL)


 ("1","2","3",NULL,"9",NULL)

是重复的,因为列4、5和6在至少一个字段中为空,而所有其他字段都相等。
("1", "2", "3", "4", NULL, "6")


("1","2","3",NULL,"9","7")

不是重复的,因为字段6不同,并且都不是空的
以及两个比较典型的实例的实际数据;
(NULL, NULL, "3",   NULL, "5",  "6")


("1", "2",    NULL, "4",  NULL, "6")

是重复的,因为它们不同的所有字段,至少有一面是空的。
(NULL, NULL, "3",   NULL, "5",  "6")


("1", "2",    NULL, "4",  NULL, "6")

是的,这意味着
(NULL, NULL, NULL, "4", "5", "6")


("1", "2", "3", NULL, NULL, NULL)

如果没有至少一个字段完全匹配的要求,则将是重复的。哪些字段为空,哪些不是非常随机的。我们只需要从我们的数据提供程序中至少提供6个字段中的2个。
另一个更新:我更新了第2点,以反映我希望所有参与至少一个重复对的行。所以,对于三排
(1,2,3,4,空,6)
(1, 2, 3, NULL, 5, NULL)

(1, 2, 3, 4, 7, NULL)

这三行都将被返回,因为即使第2行和第3行不被视为彼此重复,但第1行和第2行是重复的,第1行和第3行是重复的,因此这三行都参与重复关系,因此都将被返回。

最佳答案

使用count() over(partition by ...)然后过滤大于1的任何计数的结果:

CREATE TABLE mytable(
   ID   INTEGER  NOT NULL PRIMARY KEY 
  ,col1 VARCHAR(2) NOT NULL
  ,col2 VARCHAR(2)
  ,col3 VARCHAR(2) NOT NULL
  ,col4 VARCHAR(2)
  ,col5 VARCHAR(2)
  ,col6 VARCHAR(2)
);
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1001,'a1','b1','c1','d1','e1','f1');
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1002,'a1',NULL,'c1','d1','e1','f1');
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1003,'a1','b1','c1','d1','e1','f1');
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1004,'b1','c1','d1','e1','f1',NULL);
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1005,'a1','b1','c1',NULL,'e1','f1');
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1006,'b1','c1','d1','e1','f1',NULL);
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1007,'f1',NULL,'b1','c1','d1','e1');
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1008,'b1','c1','d1','e1','f1',NULL);
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1009,'c1','d1','e1','f1',NULL,NULL);
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1010,'c1','d1','e1','f1',NULL,'a1');
INSERT INTO mytable(ID,col1,col2,col3,col4,col5,col6) VALUES (1011,'a1','b1','c1','d1','e1','f1');

select
       *
     , count(*) over(partition by
                       coalesce(col1,'NULL')
                     , coalesce(col2,'NULL')
                     , coalesce(col3,'NULL')
                     , coalesce(col4,'NULL')
                     , coalesce(col5,'NULL')
                     , coalesce(col6,'NULL')
                     ) cv
from mytable

ID COL1 COL2 COL3 COL4 COL5 COL6简历
---::—————————————————————————————————
1001 | a1 | b1 | c1 | d1 | e1 | f1 | 3
1003 | a1 | b1 | c1 | d1 | e1 | f1 | 3
1011 A1 B1 C1 D1 E1 F1 3
1005 A1 B1 C1空E1 F1 1
1002 A1空C1 D1 E1 F1 1
1008 b1 c1 d1 e1 f1空3
1004 b1 c1 d1 e1 f1空3
1006 b1 c1 d1 e1 f1空3
1010 C1 D1 E1 F1空A1 1
1009 C1 D1 E1 F1零零1
1007 f1零b1 c1 d1 e1 1
使用上面的方法作为子查询,然后使用where cv > 1来定位在这6列中具有“重复项”的所有行。
分贝<>小提琴here
请注意使用一些样本数据的能力。实际上,您的责任是为您的问题提供样本数据(因为您已经拥有了这些数据)不要试图仅用文字来解释,用数据来说明“现状”和“未来”,你会发现你的问题更容易准备,回答更快。见Why should I provide a MCVE

关于sql - SQL复制与空值,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/52979692/

相关文章:

SQL - 选择其子行的列包含特定字符串的行

sql - 如果第二个表中有匹配项或没有匹配项,如何编写 SQL 查询来获取记录?

sql - Binary_Checksum 与 HashBytes 函数

postgresql - 压缩列为 NULL 的行

arrays - Postgres jsonb 选择特定的数组字段

sql - 有没有办法在不卸载的情况下完全重置 SQL Server 实例?

mysql - 针对几列重复数据的关系数据库设计

mysql - 如何通过sql将更多的字符串搜索成更小的字符串

database - 树系统中基于角色的访问控制

python - 运行 pgadmin 抛出意外错误