3

3 つの列 (名前、価格、新しい価格) が同じデータと一致する場合にのみ、重複データを削除したいと考えています。しかし、他の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()
4

2 に答える 2

3

これは、サーバーへのラウンドトリップを追加することなく、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;

ウィンドウ関数row_number()には PostgreSQL 8.4 以降が必要です。
重複のセットのうち、最小の ID が生き残ります。に変更した
ことに注意してください。"new price"new_price

またはEXISTS、@wildplasser が同じ効果のコメントとして投稿した半結合を使用します。


または、サブクエリの代わりに CTE を使用して、CTE 愛好家 @wildplasser の特別な要求により... :)

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;

CTE を変更するデータには、Postgres 9.1 以降が必要です。
このフォームは、サブクエリを使用したフォームとほぼ同じように機能します。

于 2012-09-02T14:26:02.677 に答える
2

SQL ステートメントを使用GROUP BYして、最初の主キーと共に行を識別します。

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

上記のクエリは、複数の主キーidがある (name, price, "new price") 行のグループごとに最も低い主キーを選択します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])

主キーが同じ 3 列idの最初の行よりも大きい行を削除することに注意してください。最初の重複では、 8idの行のみが一致し、2 回目の重複では10 の行が一致します。idid

これは、見つかった複製ごとに個別の削除を行います。WHERE EXISTSこれを、サブ選択クエリを使用して 1 つのステートメントに組み合わせることができます。

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 に指示します。

于 2012-09-02T14:09:21.683 に答える