【问题标题】:How to remove duplicates in combination of three columns in python with postgresql如何使用postgresql删除python中三列组合中的重复项
【发布时间】:2012-09-02 13:50:59
【问题描述】:

仅当三列(名称、价格和新价格)与相同数据匹配时,我才想删除重复数据。但在另一个 python 脚本中。

所以数据可以插入到数据库中,但是使用其他 python 脚本,我想通过 cron 作业删除这些重复数据。

所以在这种情况下:

cur.execute("INSERT INTO cars VALUES(8,'Hummer',41400, 49747)")

cur.execute("INSERT INTO cars VALUES(9,'Volkswagen',21600, 36456)")

是重复的。插入数据的示例脚本:

import psycopg2
import sys

con = None

try:
    con = psycopg2.connect(database='testdb', user='janbodnar')    
    cur = con.cursor()

    cur.execute("CREATE TABLE cars(id INT PRIMARY KEY, name VARCHAR(20), price INT, new price INT)")
    cur.execute("INSERT INTO cars VALUES(1,'Audi',52642, 98484)")
    cur.execute("INSERT INTO cars VALUES(2,'Mercedes',57127, 874897)")
    cur.execute("INSERT INTO cars VALUES(3,'Skoda',9000, 439788)")
    cur.execute("INSERT INTO cars VALUES(4,'Volvo',29000, 743878)")
    cur.execute("INSERT INTO cars VALUES(5,'Bentley',350000, 434684)")
    cur.execute("INSERT INTO cars VALUES(6,'Citroen',21000, 43874)")
    cur.execute("INSERT INTO cars VALUES(7,'Hummer',41400, 49747)")
    cur.execute("INSERT INTO cars VALUES(8,'Hummer',41400, 49747)")
    cur.execute("INSERT INTO cars VALUES(9,'Volkswagen',21600, 36456)")
    cur.execute("INSERT INTO cars VALUES(10,'Volkswagen',21600, 36456)")

    con.commit()

except psycopg2.DatabaseError, e:
    if con:
        con.rollback()

    print 'Error %s' % e    
    sys.exit(1

finally:    
    if con:
        con.close()

【问题讨论】:

  • 澄清:您想识别重复的行,并删除重复的数据?
  • 与相同数据匹配的树表”到底是什么意思?您的意思是“三个匹配相同的数据”吗?
  • @a_horse_with_no_name:我已经读过它,当然提供的示例插入只显示列。编辑问题以改进语法、拼写和错误。

标签: python database postgresql


【解决方案1】:

您可以在一个语句中执行此操作,而无需额外往返服务器。

DELETE FROM cars
USING (
    SELECT id, row_number() OVER (PARTITION BY name, price, new_price
                                  ORDER BY id) AS rn
    FROM   cars
    ) x
WHERE cars.id = x.id
AND   x.rn > 1;

window function row_number() 需要 PostgreSQL 8.4 或更高版本。
在一组骗子中,最小的 id 幸存下来。
请注意,我将"new price" 更改为new_price

或者使用@wildplasser 作为评论发布的EXISTS 半连接,达到同样的效果。


或者,根据 CTE 奉献者 @wildplasser 的特殊要求,使用 CTE 而不是子查询... :)

WITH x AS (
    SELECT id, row_number() OVER (PARTITION BY name, price, new_price
                                  ORDER BY id) AS rn
    FROM   cars
    )
DELETE FROM cars
USING  x
WHERE  cars.id = x.id
AND    x.rn > 1;

Data modifying CTE 需要 Postgres 9.1 或更高版本。
这种形式的执行与带有子查询的形式大致相同。

【讨论】:

  • @wildplasser:你去吧,你的日常修复。 :)
  • 我很乐意承认不知道分区,不知道 row_number() 在 PostgreSQL 或 CTE 中的存在。 :-) 我现在要开始阅读这些内容了..
  • @MartijnPieters:这些都是非常酷的(或多或少是新的)功能,我希望我的手册链接有用。顺便说一句,PARTITION 子句只是窗口函数语法的一部分。 Partitioning 指的是完全不同的数据库设计领域。
  • @ErwinBrandstetter:这些链接确实很有用,还有很多阅读要做。
  • 酷,所以 CTE 基本上是存储在变量中的查询定义(过于简化),OVER (PARTITION BY ...) 创建一个类似于GROUP BY 的窗口,但可以访问组中的各个行.确实很漂亮,添加到我的技巧包中!
【解决方案2】:

使用GROUP BY SQL 语句与初始主键一起识别行:

duplicate_query = '''\
SELECT MIN(id), "name", price, "new price"
FROM cars
GROUP BY "name", price, "new price"
HAVING COUNT(ID) > 1
'''

上面的查询为每组(名称、价格、“新价格”)行选择最低的主键id,其中有多个主键id。对于您的示例数据,这将返回:

7, 'Hummer', 41400, 49747
9, 'Volkswagen', 21600, 36456

然后您可以使用返回的数据删除重复项:

delete_dupes = '''
DELETE
FROM cars
WHERE 
    "name"=%(name)s AND price=%(price)s AND "new price"=%(newprice)s AND
    id > %(id)s
'''

cur.execute(duplicate_query)
dupes = cur.fetchall()
cur.executemany(delete_dupes, [
    dict(name=r[1], price=r[2], newprice=r[3], id=r[0])
    for r in dupes])

请注意,我们删除主键 id 大于第一个 id 且具有相同 3 列的任何行。对于第一个欺骗,只有 id 8 的行将匹配,对于第二个欺骗,与 id 10 匹配的行。

这确实会为找到的每个骗子单独删除。您可以将其与WHERE EXISTS 子选择查询组合成一个语句:

delete_dupes = '''\
DELETE FROM cars cdel
WHERE EXISTS (
    SELECT *
    FROM cars cex
    WHERE 
        cex."name" = cdel."name" AND 
        cex.price = cdel.price AND
        cex."new price" = cdel."new price" AND
        cex.id > cdel.id
)
'''

cur.execute(delete_dupes)

这指示 PostgreSQL 删除任何具有相同名称、价格和新价格但主键高于当前行的其他行的行。

【讨论】:

  • 虽然这可能是对的客户端(python)端,它将其转换为字典(或数组,不知道),而字典又使用完全相同的列表来执行实际的删除。这一切都可以在一条 SQL 语句中完成:DELETE FROM cars dd WHERE EXISTS (SELECT * FROM cars ex WHERE ex.name = dd.name ...AND more mathing fields... AND ex.id < dd.id);
  • @wildplasser:总是更好,我会更新。 :-) 或者,只是支持竞争的答案..
  • 请注意,“inner”和“outer”都是 SQL 中的保留字。无论如何,大多数人都坚持使用一个或两个字母的相关名称。 (我经常将“ex”用于 exists() 子查询(或“nx”用于 NOT EXISTS 子查询),在这种情况下,“dd”用于 DELETE 主查询)。顺便说一句:“名称”在 Postgres 中是一个非保留词(这仍然意味着它对解析器来说是特殊的。最好也避免使用它。
  • @wildplasser:谢谢指点;改进了我的答案。您可能会说我通常不怎么处理 SQL。在我的域之外通常
猜你喜欢
  • 1970-01-01
  • 2020-05-31
  • 1970-01-01
  • 2020-02-04
  • 1970-01-01
  • 1970-01-01
  • 2019-07-07
  • 2022-07-22
  • 1970-01-01
相关资源
最近更新 更多