在 PostgreSQL 中查找所有列重复且没有唯一字段的行
Find rows with all columns duplicated and no unique field in PostgreSQL
假设我有一个这样的 table,其中没有任何列或列组合保证是唯一的:
GAME_EVENT
USERNAME
ITEM
QUANTITY
sell
poringLUVR
sword
1
sell
poringLUVR
sword
1
kill
daenerys
civilians
200000
kill
daenerys
civilians
200000
invoke
sylvanas
undead
1000000
我想检索存在不止一次的所有行的列表(其中所有列的组合出现不止一次)。
(在这种情况下,我希望得到一个包含“sell/poringLUVR”和“kill/daenerys”行的列表)
解决这个问题的好方法是什么?合并索引会有帮助吗?也欢迎对非 Postgres 方法提出建议。
假设所有列 NOT NULL
,这将执行:
SELECT *
FROM tbl t1
WHERE EXISTS (
SELECT FROM tbl t2
WHERE (t1.*) = (t2.*)
AND t1.ctid <> t2.ctid
);
ctid
is a system column,在没有实际PK(你显然没有)的情况下,可以作为穷人PK的“元组标识符”/“项目指针”,并且只能在单个查询的范围。相关:
- Delete duplicate rows from small table
- How do I decompose ctid into page and row numbers?
如果列可以是 NULL
,(成本更高)使用 IS NOT DISTINCT FROM
而不是 =
进行操作。参见:
- How do I (or can I) SELECT DISTINCT on multiple columns?
(t1.*) = (t2.*)
正在比较 ROW 值。这个较短的语法是等效的:t1 = t2
除非底层 table 中存在同名的列,在这种情况下,第二种形式失败而第一种形式不会。参见:
索引?
如果任何列具有特别高的基数(许多不同的值,很少重复),为了这个答案的目的,我们称之为 hi_cardi_column
,仅在该列上的普通 btree 索引可能是有效的为你的任务。一些小列与多列索引的组合也可以工作。关键是要有一个小而快的索引,否则开销不会支付。
SELECT *
FROM tbl t1
WHERE EXISTS (
SELECT FROM tbl t2
WHERE t1.hi_cardi_column = t2.hi_cardi_column -- logically redundant
AND (t1.*) = (t2.*)
AND t1.ctid <> t2.ctid
);
添加的条件t1.hi_cardi_column = t2.hi_cardi_column
在逻辑上是多余的,但有助于利用所述索引。
除此之外,我认为索引支持的潜力不大,因为无论如何都必须访问 table 的所有行,并且必须检查所有列。
假设我有一个这样的 table,其中没有任何列或列组合保证是唯一的:
GAME_EVENT | USERNAME | ITEM | QUANTITY |
---|---|---|---|
sell | poringLUVR | sword | 1 |
sell | poringLUVR | sword | 1 |
kill | daenerys | civilians | 200000 |
kill | daenerys | civilians | 200000 |
invoke | sylvanas | undead | 1000000 |
我想检索存在不止一次的所有行的列表(其中所有列的组合出现不止一次)。
(在这种情况下,我希望得到一个包含“sell/poringLUVR”和“kill/daenerys”行的列表)
解决这个问题的好方法是什么?合并索引会有帮助吗?也欢迎对非 Postgres 方法提出建议。
假设所有列 NOT NULL
,这将执行:
SELECT *
FROM tbl t1
WHERE EXISTS (
SELECT FROM tbl t2
WHERE (t1.*) = (t2.*)
AND t1.ctid <> t2.ctid
);
ctid
is a system column,在没有实际PK(你显然没有)的情况下,可以作为穷人PK的“元组标识符”/“项目指针”,并且只能在单个查询的范围。相关:
- Delete duplicate rows from small table
- How do I decompose ctid into page and row numbers?
如果列可以是 NULL
,(成本更高)使用 IS NOT DISTINCT FROM
而不是 =
进行操作。参见:
- How do I (or can I) SELECT DISTINCT on multiple columns?
(t1.*) = (t2.*)
正在比较 ROW 值。这个较短的语法是等效的:t1 = t2
除非底层 table 中存在同名的列,在这种情况下,第二种形式失败而第一种形式不会。参见:
索引?
如果任何列具有特别高的基数(许多不同的值,很少重复),为了这个答案的目的,我们称之为 hi_cardi_column
,仅在该列上的普通 btree 索引可能是有效的为你的任务。一些小列与多列索引的组合也可以工作。关键是要有一个小而快的索引,否则开销不会支付。
SELECT *
FROM tbl t1
WHERE EXISTS (
SELECT FROM tbl t2
WHERE t1.hi_cardi_column = t2.hi_cardi_column -- logically redundant
AND (t1.*) = (t2.*)
AND t1.ctid <> t2.ctid
);
添加的条件t1.hi_cardi_column = t2.hi_cardi_column
在逻辑上是多余的,但有助于利用所述索引。
除此之外,我认为索引支持的潜力不大,因为无论如何都必须访问 table 的所有行,并且必须检查所有列。