mar*_*ion 9 postgresql duplication
我有一个表,其架构如下:
create_table "questions_tags", :id => false, :force => true do |t|
t.integer "question_id"
t.integer "tag_id"
end
add_index "questions_tags", ["question_id"], :name => "index_questions_tags_on_question_id"
add_index "questions_tags", ["tag_id"], :name => "index_questions_tags_on_tag_id"
Run Code Online (Sandbox Code Playgroud)
我想删除重复的记录,即它们既具有相同的记录tag_id又question_id与另一条记录相同。
SQL 看起来像什么?
Erw*_*ter 15
根据我的经验(以及许多测试中所示),@gsiemsNOT IN所证明的速度相当慢,而且扩展性极差。逆IN通常更快(您可以在这种情况下重新制定这种方式),但是这个查询EXISTS(完全按照您的要求进行)应该更快 - 使用数量级的大表:
DELETE FROM questions_tags q
WHERE EXISTS (
SELECT FROM questions_tags q1
WHERE q1.ctid < q.ctid
AND q1.question_id = q.question_id
AND q1.tag_id = q.tag_id
);
Run Code Online (Sandbox Code Playgroud)
删除其中的每一行与同另一行(tag_id, question_id)和一个较小的ctid存在。(根据元组的物理顺序有效地保留第一个实例。)ctid在没有更好的替代方案的情况下使用,您的表似乎没有 PK 或任何其他唯一(一组)列。
ctid是每行中存在的内部元组标识符,并且必须是唯一的。进一步阅读:
我运行了一个测试用例,该表与您的问题和 100k 行匹配:
CREATE TABLE questions_tags(
question_id integer NOT NULL
, tag_id integer NOT NULL
);
INSERT INTO questions_tags (question_id, tag_id)
SELECT (random()* 100)::int, (random()* 100)::int
FROM generate_series(1, 100000);
ANALYZE questions_tags;
Run Code Online (Sandbox Code Playgroud)
在这种情况下,索引没有帮助。
NOT IN
该SQLfiddle超时。
在本地尝试了相同的方法,但几分钟后我也取消了它。
EXISTS
在这个SQLfiddle 中完成半秒。
如果要删除大部分行,将幸存者选择到另一个表中,删除原始表并重命名幸存者表会更快。小心,如果您在原始文件上定义了视图或外键(或其他依赖项),这会产生影响。
如果您有依赖项并希望保留它们,您可以:
SELECT 幸存者到一个临时表。TRUNCATE 原本的。INSERT幸存者。CREATE索引和外键。视图可以保持不变,它们对性能没有影响。更多在这里或在这里。您可以使用 ctid 来完成此操作。例如:
创建一个重复的表:
=# create table foo (id1 integer, id2 integer);
CREATE TABLE
=# insert into foo values (1,1), (1, 2), (1, 2), (1, 3);
INSERT 0 4
=# select * from foo;
id1 | id2
-----+-----
1 | 1
1 | 2
1 | 2
1 | 3
(4 rows)
Run Code Online (Sandbox Code Playgroud)
选择重复数据:
=# select foo.ctid, foo.id1, foo.id2, foo2.min_ctid
-# from foo
-# join (
-# select id1, id2, min(ctid) as min_ctid
-# from foo
-# group by id1, id2
-# having count (*) > 1
-# ) foo2
-# on foo.id1 = foo2.id1 and foo.id2 = foo2.id2
-# where foo.ctid <> foo2.min_ctid ;
ctid | id1 | id2 | min_ctid
-------+-----+-----+----------
(0,3) | 1 | 2 | (0,2)
(1 row)
Run Code Online (Sandbox Code Playgroud)
删除重复数据:
=# delete from foo
-# where ctid not in (select min (ctid) as min_ctid from foo group by id1, id2);
DELETE 1
=# select * from foo;
id1 | id2
-----+-----
1 | 1
1 | 2
1 | 3
(3 rows)
Run Code Online (Sandbox Code Playgroud)
在您的情况下,以下内容应该有效:
delete from questions_tags
where ctid not in (
select min (ctid) as min_ctid
from questions_tags
group by question_id, tag_id
);
Run Code Online (Sandbox Code Playgroud)
| 归档时间: |
|
| 查看次数: |
13455 次 |
| 最近记录: |