Question

J'ai une table que je voudrais être en mesure de présenter « classé X sur Y » données pour. En particulier, je voudrais être en mesure de présenter ces données pour une ligne individuelle d'une manière relativement efficace (à savoir sans sélectionner chaque ligne dans le tableau). Le classement lui-même est assez simple, il est une ORDER droite BY sur une seule colonne dans le tableau.

Postgres semble présenter des défis uniques à cet égard; AFAICT il ne possède pas de RANK ou ROW_NUMBER ou fonction équivalente (au moins en 8.3, que je suis coincé sur pour le moment). La réponse canonique dans les archives de la liste de diffusion semble être de créer une séquence temporaire et sélectionnez de celui-ci:

test=> create temporary sequence tmp_seq;
CREATE SEQUENCE
test=*> select nextval('tmp_seq') as row_number, col1, col2 from foo;

Il semble que cette solution ne sera pas utile encore quand je veux choisir une seule ligne de la table (et je veux le sélectionner par PK, et non par rang).

Je pourrais dénormaliser et stocker le rang dans une colonne séparée, ce qui rend la présentation des données trivial, mais juste reloge mon problème. Mise à jour ne supporte pas ORDER BY, donc je ne sais pas comment je construis une requête UPDATE pour définir les rangs (courts de sélectionner chaque ligne et l'exécution d'une mise à jour séparée pour chaque ligne, ce qui semble trop grande activité de DB déclencher chaque fois que les rangs ont besoin de mise à jour).

Suis-je manque quelque chose d'évident? Quelle est la bonne façon de le faire?

EDIT : Apparemment, je ne suis pas assez clair. Je suis au courant de OFFSET / LIMIT, mais je ne vois pas comment il aide à résoudre ce problème. Je ne cherche pas à sélectionner l'élément classé Xème, je suis en train de sélectionner un élément arbitraire (par son PK, par exemple), et être en mesure d'afficher à l'utilisateur quelque chose comme « 43e rang sur 312. »

Était-ce utile?

La solution

Si vous voulez que le rang, faire quelque chose comme

SELECT id,num,rank FROM (
  SELECT id,num,rank() OVER (ORDER BY num) FROM foo
) AS bar WHERE id=4

Ou si vous voulez vraiment le numéro de ligne, utilisez

SELECT id,num,row_number FROM (
  SELECT id,num,row_number() OVER (ORDER BY num) FROM foo
) AS bar WHERE id=4

Ils diffèrent quand vous avez des valeurs égales quelque part. Il y a aussi DENSE_RANK () si vous avez besoin que.

Cela nécessite PostgreSQL 8.4, bien sûr.

Autres conseils

est-il pas ceci:

SELECT  *
FROM    mytable
ORDER BY
        col1
OFFSET X LIMIT 1

Ou je manque quelque chose?

Mise à jour:

Si vous voulez montrer le rang, utilisez ceci:

SELECT  mi.*, values[1] AS rank, values[2] AS total
FROM    (
        SELECT  (
                SELECT  ARRAY[SUM(((mi.col1, mi.ctid) < (mo.col1, mo.ctid))::INTEGER), COUNT(*)]
                FROM    mytable mi
                ) AS values
        FROM    mytable mo
        WHERE   mo.id = @myid
        ) q

fonctionnalité ROW_NUMBER dans PostgreSQL est mis en oeuvre via LIMIT n OFFSET skip.

EDIT: Puisque vous demandez ROW_NUMBER() au lieu de classement simple: row_number() est introduit dans la version PostgreSQL 8.4. Donc, vous pourriez envisager de mettre à jour. Sinon cette solution pourrait être utile.

réponses précédentes abordent la question «sélectionnez toutes les lignes et obtenir leur rang » qui n'est pas ce que vous voulez ...

  • vous avez une ligne
  • vous voulez connaître son rang

Il suffit de faire:

SELECT COUNT (*) FROM table WHERE résultat> $ 1

Où $ 1 est le score de la ligne que vous venez de sélectionner (je suppose que vous souhaitez afficher si vous pourriez le sélectionner ...).

Ou:

SELECT a. (SELECT count () FROM table b WHERE note> b.score) AS rang FROM table AS WHERE pk = ...

Cependant, si vous sélectionnez une ligne qui est classé au dernier rang, oui, vous aurez besoin de compter toutes les lignes qui sont classés avant, donc vous aurez besoin de scanner toute la table, et il sera très lent.

Solution:

SELECT COUNT (*) FROM (SELECT 1 FROM table WHERE résultat> 1 $ LIMITE 30)

Vous obtiendrez classement précis pour les 30 meilleurs scores, et il sera rapide. Qui se soucie des perdants?

OK, si vous faites vraiment attention sur les perdants, vous aurez besoin de faire un histogramme:

Le score Suppose peut aller de 0 à 100, et vous avez 1000000 perdants avec score <80 et 10 gagnants ayant obtenu une note> 80.

Vous faites un histogramme de combien de lignes ont un score de X, il est simple petite table avec 100 lignes. Ajouter un déclencheur à votre table principale pour mettre à jour l'histogramme.

Maintenant, si vous voulez classer un perdant qui a marquer X, son rang est la somme (histo) où histo_score> X.

Étant donné que votre score est probablement pas entre 0 et 100, mais (par exemple) entre 0 et 1000000000, vous aurez besoin de truquer un peu, agrandir vos bacs d'histogramme, par exemple. si vous avez seulement besoin de 100 bacs max, ou utilisez une fonction de distribution log-histogramme.

Par postgres chemin fait quand vous analysez la table, donc si vous définissez statistics_target à 100 ou 1000 sur le score, ANALYSER, puis exécutez:

EXPLIQUER SELECT * FROM table WHERE Score> $ 1

vous obtiendrez une belle estimation du nombre de lignes.

Qui a besoin de réponses exactes?

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top