web-dev-qa-db-fra.com

PG :: Erreur: les expressions SELECT DISTINCT, ORDER BY doivent apparaître dans la liste de sélection

ActionView :: Template :: Error (PG :: Error: ERROR: pour SELECT DISTINCT, les expressions ORDER BY doivent apparaître dans la liste de sélection

Je crée un site Web d'événements et j'essaie de trier les rsvps rendus par l'heure de début de l'événement. Il y a beaucoup de RSVPS donc je les regroupe avec différents, mais j'ai eu beaucoup de difficulté ces derniers jours à trier les résultats sans que cette erreur n'apparaisse sur PG. J'ai regardé certaines des questions précédentes sur le sujet et je suis toujours assez perdu. Comment puis-je faire en sorte que cela fonctionne? Merci beaucoup!

@rsvps = Rsvp.where(:voter_id => current_user.following.collect {|f| f["id"]}, :status => 'going').where("start_time > ? AND start_time < ?", Time.now, Time.now + 1.month).order("count_all desc").count(:group => :event_id).collect { |f| f[0] }

<%= render :partial => 'rsvps/rsvp', :collection => Rsvp.where(:event_id => @rsvps).select("DISTINCT(event_id)").order('start_time asc') %>
43
Andy

La clause ORDER BY ne peut être appliquée que après le DISTINCT a été appliqué. Étant donné que seuls les champs de l'instruction SELECT sont pris en considération pour les opérations DISTINCT, ce sont les seuls champs qui peuvent être utilisés dans ORDER BY.

Logiquement, si vous voulez simplement une liste distincte de valeurs event_id, l'ordre dans lequel elles se produisent ne devrait pas être pertinent. Si la commande est importante, vous devez ajouter start_time à la liste SELECT afin qu'il y ait du contexte pour la commande.

De plus, ces deux clauses SELECT ne sont PAS équivalentes, alors faites attention:

SELECT DISTINCT(event_id, start_time) FROM ...

SELECT DISTINCT event_id, start_time FROM ...

Le second est le formulaire que vous souhaitez. Le premier retournera une série d'enregistrements avec les données représentées comme une construction ROW (une seule colonne avec un Tuple à l'intérieur). La seconde renverra des colonnes normales de sortie de données. Cela ne fonctionne que comme prévu dans le cas d'une seule colonne où la construction ROW est réduite car il ne s'agit que d'une seule colonne.

52
Matthew Wood

Je sais que c'est une question assez ancienne, mais je viens de passer par un petit exemple dans ma tête qui m'a aidé à comprendre pourquoi Postgres a cette restriction apparemment étrange sur les colonnes SELECT DISTINCT/ORDER BY.

Imaginez que vous disposez des données suivantes dans votre table Rsvp:

 event_id |        start_time
----------+------------------------
    0     | Mar 17, 2013  12:00:00
    1     |  Jan 1, 1970  00:00:00
    1     | Aug 21, 2013  16:30:00
    2     |  Jun 9, 2012  08:45:00

Maintenant, vous voulez récupérer une liste d'ID d'événement distincts, classés par leur heure de début respective. Mais où devrait 1 aller? Doit-il venir en premier, parce que celui que Tuple commence le 1er janvier 1970, ou devrait-il durer en raison du 21 août 2013?

Étant donné que le système de base de données ne peut pas prendre cette décision pour vous et que la syntaxe de la requête ne peut pas dépendre des données réelles sur lesquelles elle pourrait fonctionner (en supposant que event_id est unique), nous ne pouvons classer que par colonnes de la clause SELECT.

Quant à la question réelle - une alternative à la réponse de Matthew utilise une fonction d'agrégation comme MIN ou MAX pour le tri:

  SELECT event_id
    FROM Rsvp
GROUP BY event_id
ORDER BY MIN(start_time)

Le regroupement et l'agrégation explicites sur start_time permet à la base de données de proposer un ordre sans ambiguïté des tuples de résultat. Notez cependant que la lisibilité est définitivement un problème dans ce cas;)

67
AdrianoKF

Parce que vous utilisez la colonne start_time, vous pouvez utiliser row_number () qui est l'un des Window Functions of PostgreSQL et l'empiler dans

  • ordre de start_time, si vous attendez des valeurs de ligne avec le premier start_time

    Sélectionnez event_id dans (SELECT event_id, ROW_NUMBER () OVER (PARTITION BY event_id ORDER BY start_time) AS first_row FROM Rsvp) où first_row = 1

  • ordre inverse de start_time, si vous attendez des valeurs de ligne avec la dernière start_time

    Sélectionnez event_id dans (SELECT event_id, ROW_NUMBER () OVER (PARTITION BY event_id ORDER BY start_time desc) AS last_row FROM Rsvp) où last_row = 1

Vous pouvez également utiliser différents fonction fenêtre selon vos besoins.

0
SUKUMAR S