web-dev-qa-db-fra.com

Comment puis-je supprimer un nombre fixe de lignes avec le tri dans PostgreSQL?

J'essaie de porter d'anciennes requêtes MySQL sur PostgreSQL, mais j'ai des problèmes avec celui-ci:

DELETE FROM logtable ORDER BY timestamp LIMIT 10;

PostgreSQL ™ n'autorise pas l'ordre ou les limites dans sa syntaxe de suppression, et le tableau n'a pas de clé primaire, donc je ne peux pas utiliser de sous-requête. En outre, je souhaite préserver le comportement de suppression de la requête exactement du nombre donné ou des enregistrements - par exemple, si la table contient 30 lignes mais qu'elles ont toutes le même horodatage, je souhaite néanmoins supprimer 10, bien que peu importe lequel 10.

Alors; comment puis-je supprimer un nombre fixe de lignes avec le tri dans PostgreSQL?

Edit: Pas de clé primaire signifie qu'il n'y a pas de colonne log_id ou similaire. Ah, les joies des systèmes hérités!

77
Whatsit

Vous pouvez essayer d'utiliser ctid :

DELETE FROM logtable
WHERE ctid IN (
    SELECT ctid
    FROM logtable
    ORDER BY timestamp
    LIMIT 10
)

La ctid est:

L'emplacement physique de la version de la ligne dans sa table. Notez que bien que la variable ctid puisse être utilisée pour localiser très rapidement la version de la ligne, la variable ctid d’une ligne sera modifiée si elle est mise à jour ou déplacée par VACUUM FULL. Par conséquent, ctid est inutile en tant qu'identificateur de ligne à long terme.

Il y a aussi oid mais cela n'existe que si vous le demandez spécifiquement lorsque vous créez la table.

117
mu is too short

Les documents Postgres recommandent d'utiliser array au lieu de IN et de sous-requête. Cela devrait fonctionner beaucoup plus vite 

DELETE FROM logtable 
WHERE id = any (array(SELECT id FROM logtable ORDER BY timestamp LIMIT 10));

Ceci et quelques autres astuces peuvent être trouvés ici

39
criticus
delete from logtable where log_id in (
    select log_id from logtable order by timestamp limit 10);
11
Konrad Garus

En supposant que vous vouliez effacer 10 enregistrements (sans ordre), vous pouvez le faire:

DELETE FROM logtable as t1 WHERE t1.ctid < (select t2.ctid from logtable as t2  where (Select count(*) from logtable t3  where t3.ctid < t2.ctid ) = 10 LIMIT 1);

Pour mon cas d'utilisation, la suppression de 10 millions d'enregistrements s'est avérée plus rapide.

2
Patrick Hüsler

Vous pouvez écrire une procédure qui effectue une boucle sur la suppression pour des lignes individuelles. Elle peut également prendre un paramètre pour spécifier le nombre d’éléments à supprimer. Mais c'est un peu exagéré par rapport à MySQL.

1
Bernhard

Si vous n'avez pas de clé primaire, vous pouvez utiliser la syntaxe array Where où IN avec une clé composite.

delete from table1 where (schema,id,lac,cid) in (select schema,id,lac,cid from table1 where lac = 0 limit 1000);

Cela a fonctionné pour moi.

0
user2449151