Postgres: distinct mais pour une seule colonne

120

J'ai une table sur pgsql avec des noms (ayant plus de 1 million de lignes), mais j'ai aussi de nombreux doublons. Je sélectionne 3 champs: id, name, metadata.

Je veux les sélectionner au hasard avec ORDER BY RANDOM()et LIMIT 1000, donc je fais cela de nombreuses étapes pour économiser de la mémoire dans mon script PHP.

Mais comment puis-je faire cela pour qu'il ne me donne qu'une liste sans doublons dans les noms.

Par exemple [1,"Michael Fox","2003-03-03,34,M,4545"]sera retourné mais pas [2,"Michael Fox","1989-02-23,M,5633"]. Le champ de nom est le plus important et doit être unique dans la liste à chaque fois que je fais la sélection et il doit être aléatoire.

J'ai essayé avec GROUP BY name, mais il s'attend à ce que j'aie également un identifiant et des métadonnées dans la GROUP BYfonction ou dans une fonction agrégée, mais je ne veux pas les filtrer d'une manière ou d'une autre.

Tout le monde sait comment récupérer plusieurs colonnes mais n'en fait qu'une seule sur une colonne?

NovumCoder
la source

Réponses:

226

Pour faire une distinction sur une seule (ou n) colonne (s):

select distinct on (name)
    name, col1, col2
from names

Cela renverra l'une des lignes contenant le nom. Si vous souhaitez contrôler laquelle des lignes sera renvoyée, vous devez commander:

select distinct on (name)
    name, col1, col2
from names
order by name, col1

Renvoie la première ligne lorsqu'elle est commandée par col1.

distinct on:

SELECT DISTINCT ON (expression [, ...]) ne conserve que la première ligne de chaque ensemble de lignes où les expressions données sont évaluées comme égales. Les expressions DISTINCT ON sont interprétées en utilisant les mêmes règles que pour ORDER BY (voir ci-dessus). Notez que la «première ligne» de chaque ensemble est imprévisible sauf si ORDER BY est utilisé pour garantir que la ligne souhaitée apparaît en premier.

Les expressions DISTINCT ON doivent correspondre aux expressions ORDER BY les plus à gauche. La clause ORDER BY contient normalement des expressions supplémentaires qui déterminent la priorité souhaitée des lignes dans chaque groupe DISTINCT ON.

Clodoaldo Neto
la source
Bonne prise à la commande. Je ne l'ai pas inclus car ils ont mentionné vouloir un ordre aléatoire, mais il est important de le mentionner quand même.
Craig Ringer
Est-ce order by namenécessaire? Cela produirait-il un résultat différent avec order by col1?
Elliot Chance
1
@elliot oui nameest nécessaire. Consultez distinct onle manuel.
Clodoaldo Neto
1
Je souhaite que l'équipe TSQL puisse fournir une manière aussi sensée de le faire.
JTW
Veuillez ajouter la référence
Ogaga Uzoh
17

Tout le monde sait comment récupérer plusieurs colonnes mais n'en fait qu'une seule sur une colonne?

Vous voulez la DISTINCT ONclause .

Vous n'avez pas fourni d'exemples de données ni de requête complète, je n'ai donc rien à vous montrer. Vous voulez écrire quelque chose comme:

SELECT DISTINCT ON (name) fields, id, name, metadata FROM the_table;

Cela renverra un ensemble de lignes imprévisible (mais pas «aléatoire»). Si vous voulez le rendre prévisible, ajoutez une ORDER BYréponse par Clodaldo. Si vous voulez le rendre vraiment aléatoire, vous le voudrez ORDER BY random().

Craig Ringer
la source
Notez simplement qu'avec cette clause DISTINCT ON, vous ne pouvez ORDER BY que la même chose + plus. Donc, si vous dites DISTINCT ON (nom), vous devez ORDER BY nom puis tout ce que vous voulez. À peine idéal.
Kevin Parker
Kevin, vous pouvez simplement utiliser un CTE ou une sous-requête dans FROM et ORDER BY dans la requête externe
Craig Ringer
Oui, et regardez les performances aller ... Tous les résultats possibles de l'espace d'index seront recherchés. Cela transforme ce qui pourrait être une requête de 10 à 20 ms avec le bon index en une requête de 900 ms simplement parce que posgres ne peut pas gérer un ordre différent par. Peu importe l'ordre de la requête externe, il utilisera l'index de la sous-requête interne pour trouver les correspondances en premier, puis trier à nouveau. Heureux de faire des frais de consultation pour de vraies solutions à nos problèmes sur dba.stackexchange.com/questions/260852/…
Kevin Parker
4
SELECT NAME,MAX(ID) as ID,MAX(METADATA) as METADATA 
from SOMETABLE
GROUP BY NAME
David Jashi
la source
2
Juste un mot d'avertissement: cela pourrait ne pas renvoyer la valeur de l'ID ou la valeur des métadonnées qui appartiennent «ensemble»
a_horse_with_no_name
@Novum Non. Cela signifie que chat prend une valeur d'identifiant sur l'une des lignes de Michael et les métadonnées d'une autre, car il a été demandé pour les max de Michael.
Clodoaldo Neto
Eh bien oui, cela dépend grandement des données réelles utilisées par OP, dont je ne suis absolument pas conscient. Vous devrez peut-être utiliser MIN ou autre. Je viens de montrer comment vous pouvez inclure des champs qui ne figurent pas dans une GROUP BYclause.
David Jashi
Ce n'est pas une bonne solution car différentes valeurs de différentes lignes seront mélangées.
Elliot Chance