MySQLはORDERBYで行の位置を取得します


86

次のMySQLテーブルを使用します。

+-----------------------------+
+ id INT UNSIGNED             +
+ name VARCHAR(100)           +
+-----------------------------+

で並べ替えたときに、テーブル内の他の行の中から1つの行とその位置を選択するにはどうすればよいですかname ASC。したがって、テーブルデータが次のようになっている場合、名前で並べ替えると次のようになります。

+-----------------------------+
+ id | name                   +
+-----------------------------+
+  5 | Alpha                  +
+  7 | Beta                   +
+  3 | Delta                  +
+ .....                       +
+  1 | Zed                    +
+-----------------------------+

そのBeta行の現在の位置を取得する行を選択するにはどうすればよいですか?私が探している結果セットは次のようになります。

+-----------------------------+
+ id | position | name        +
+-----------------------------+
+  7 |        2 | Beta        +
+-----------------------------+

SELECT * FROM tbl ORDER BY name ASCPHPで単純に行を列挙することはできますが、1行だけで大きな結果セットをロードするのは無駄に思えます。



回答:


120

これを使って:

SELECT x.id, 
       x.position,
       x.name
  FROM (SELECT t.id,
               t.name,
               @rownum := @rownum + 1 AS position
          FROM TABLE t
          JOIN (SELECT @rownum := 0) r
      ORDER BY t.name) x
 WHERE x.name = 'Beta'

...一意の位置値を取得します。この:

SELECT t.id,
       (SELECT COUNT(*)
          FROM TABLE x
         WHERE x.name <= t.name) AS position,
       t.name    
  FROM TABLE t      
 WHERE t.name = 'Beta'

...ネクタイに同じ値を与えます。IE:2番目に2つの値がある場合、最初のクエリで一方に2の位置、もう一方に3の位置が与えられると、両方の値は2になります...


@actual:言うことは何もありません-分析機能(PostgreSQL、Oracle、SQL Server、DB2 ...)をサポートする競合他社に移動する以外に選択肢はありません
OMG Ponies 2012

2
@OMGPoniesの後positionにコンマを忘れるだけですが、完璧です。
pierallard 2014年

かなり古い投稿であることは知っていますが、同様の解決策が必要です。ただし、内部結合、group by、order byを使用する場合、「position」フィールドはこれらを無視し、値はすべて混同されます。解決策はありますか?
ダニエル

@Daniel新しい質問をして、おそらくこれを参照する必要があります。
PeerBr 2015

@actual、これは単一行のクエリであるため、ここではパフォーマンスに関する重大な懸念はないはずです。完全なリストのランクを取得しようとしている場合は異なりますが、ポイント順に並べ替えることで、「チート」して暗黙のランクを使用することができます。
agmLauncher 2015

20

これは私が考えることができる唯一の方法です:

SELECT `id`,
       (SELECT COUNT(*) FROM `table` WHERE `name` <= 'Beta') AS `position`,
       `name`
FROM `table`
WHERE `name` = 'Beta'

2
+1素敵なトリック...しかし、おそらくname <= 'Beta'代わりに使用したいと思うでしょう
Daniel Vassallo 2010

このアプローチではposition、タイに同じ値が与えられます。
OMGポニー

(以前のコメントを削除しました-私は間違っていました)...LIMIT 1そこにを追加するとどうなりますか?同点の場合、同点の最後の位置で1行だけを取得します。
Daniel Vassallo 2010

OPがnameフィールドが一意であることを保証できる場合、クエリをより複雑にする理由はありません。彼ができない場合は、タイの名前に対する彼の結果の期待を待ちましょう。
zerkms 2010

8

クエリが単純で、返される結果セットのサイズが潜在的に大きい場合は、クエリを2つのクエリに分割してみてください。

ちょうどその行のデータ、及び第2の照会用途を取得する絞込フィルタリング基準に最初のクエリCOUNTWHERE句をて位置を計算します。

たとえばあなたの場合

クエリ1:

SELECT * FROM tbl WHERE name = 'Beta'

クエリ2:

SELECT COUNT(1) FROM tbl WHERE name >= 'Beta'

2Mレコードのテーブルでこのアプローチを使用します。これは、OMGPoniesのアプローチよりもはるかにスケーラブルです。


4

他の答えは私には複雑すぎるようです。

ここに簡単な例があります。たとえば、列のあるテーブルがあるとします。

userid | points

ユーザーIDをポイントで並べ替えて、行の位置(ユーザーの「ランキング」)を取得する場合は、次を使用します。

SET @row_number = 0;

SELECT 
    (@row_number:=@row_number + 1) AS num, userid, points
FROM
    ourtable
ORDER BY points DESC

num 行の位置(ランク付け)を示します。

MySQL 8.0以降を使用している場合は、ROW_NUMBER()を使用することをお勧めします。


2

テーブル内の行の位置は、ターゲット行よりも「優れている」行の数を表します。

したがって、これらの行を数える必要があります。

SELECT COUNT(*)+ 1 FROM tableWHEREname <'ベータ'

同点の場合、最高位が返されます。

既存の「ベータ」行の後に「ベータ」という同じ名前の別の行を追加した場合、分類内で同じ場所を共有するため、返される位置は2のままになります。

質問の所有者はすでに問題を解決していると私は信じているので、これが将来同様の何かを検索する人々に役立つことを願っています。


2

私は非常によく似た問題を抱えているので、同じ質問をしませんが、ここで私が何をしたかを共有します。AVGによるグループ化と注文も使用する必要がありました。署名とsocoreを持つ学生がいて、ランク付けする必要がありました(つまり、最初にAVGを計算し、次にDESCで注文し、最後に位置を追加する必要がありました(ランク付け)。何か非常に似たよう最善の答えに私の問題への調整はほとんど変化して、ここで、):

最後にposition(ランク)列を外部SELECTに配置しました

SET @rank=0;
SELECT @rank := @rank + 1 AS ranking, t.avg, t.name
  FROM(SELECT avg(students_signatures.score) as avg, students.name as name
FROM alumnos_materia
JOIN (SELECT @rownum := 0) r
left JOIN students ON students.id=students_signatures.id_student
GROUP BY students.name order by avg DESC) t 

この答えは、受け入れられたものよりも理解しやすかった。+1
Eric Seastrand 2017

1

私は受け入れられた答えを調べていました、そしてそれは少し複雑に見えたので、ここにそれの単純化されたバージョンがあります。

SELECT t,COUNT(*) AS position FROM t      
 WHERE name <= 'search string' ORDER BY name

0

テーブルのrank(Index)が必要な同様のタイプの問題がありますorder by votes desc。以下は私にとってはうまくいきます。

Select *, ROW_NUMBER() OVER(ORDER BY votes DESC) as "rank"
From "category_model"
where ("model_type" = ? and "category_id" = ?)

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.