出于与从客户那里获取混乱数据有关的一些疯狂的业务原因,我遇到以下问题;
1)我有一张桌子,上面有6个半唯一标识符和一个自动递增的唯一ID。该表具有更多字段。但是,这些对于这次讨论并不重要。字段所保存的数据也不是确切类型。
2)我想获得参与至少一个重复关系的所有行的唯一ID的列表。 (识别所有表示重复的行对没有任何附加价值。但是,如果解决方案提供了该功能,则检索重复行的集合是相当琐碎的。所以,这也很好)
3)重复项定义为;
3a)对于这6个字段,记录A必须与记录B匹配,或者其中一个必须为空
3b)至少一个字段必须完全匹配(即都不为空)
4)所有可能感兴趣的重复字段都是字符串,而不是空字符串。许多行中至少有一个感兴趣的字段为空,但是(至少假设我们的摄取逻辑正常工作)它们都不可以有超过3个为空。
5)精确的字符串内容匹配就可以了。我们不需要任何基于正则表达式,不区分大小写的匹配项。
6)表中的实际重复项很少见。
7)我们正在运行PostgreSQL9。可以使用特定于数据库的功能。
8)该表有500,000行。因此,下面提供的我刚开始使用的幼稚查询花费了太长的时间才变得可行。据推测,它主要在指数时间内运行。理想情况下,结果应在不到一分钟的时间内返回,并在中端服务器上运行。
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不能满足我的“等于或至少一个为null”的逻辑。
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,NULL,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)
AND
("1","2","3",NULL,"9",NULL)
是重复的,因为在至少一个字段中第4、5和6列为空,而其他所有字段都相等。
("1", "2", "3", "4", NULL, "6")
AND
("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)
如果不是为了至少一个字段完全匹配的要求,将是重复的。哪些字段为null,哪些字段不是几乎是随机的。我们需要数据提供者提供的所有信息是,必须至少提供6个字段中的2个。
另一次更新:我已经更新了第2点,以反映我希望所有参与至少一个重复对的行的事实。因此,对于三行 (1、2、3、4,NULL,6)
(1, 2, 3, NULL, 5, NULL)
(1, 2, 3, 4, 7, NULL)
将返回所有三个,因为即使第2行和第3行不会被视为彼此重复,第1,2行对也是重复的,而第1,3对是重复的,因此所有这三个对都属于重复关系,因此将返回。
答案 0 :(得分:0)
使用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 | cv ---: | :--- | :--- | :--- | :--- | :--- | :--- | -: 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 | null | e1 | f1 | 1 1002 | a1 | null | c1 | d1 | e1 | f1 | 1 1008 | b1 | c1 | d1 | e1 | f1 | null | 3 1004 | b1 | c1 | d1 | e1 | f1 | null | 3 1006 | b1 | c1 | d1 | e1 | f1 | null | 3 1010 | c1 | d1 | e1 | f1 | null | a1 | 1 1009 | c1 | d1 | e1 | f1 | null | null | 1 1007 | f1 | null | b1 | c1 | d1 | e1 | 1
使用上述方法作为子查询,然后使用where cv > 1
在这6列中查找所有具有“重复项”的行。
db <>提琴here
请请注意可以使用一些示例数据的功能。确实,您有责任提供带有问题的样本数据(因为您已经拥有该数据了)。不要试图单独用言语来解释,而要使用数据来说明“现状”和“将要存在”,您会发现自己的问题更容易准备且回答更快。参见Why should I provide a MCVE