web-dev-qa-db-ja.com

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

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

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

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

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

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

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

簡単なSELECT * FROM tbl ORDER BY name ASCを実行してからPHPで行を列挙できますが、1行だけの潜在的に大きな結果セットをロードするのはもったいないようです。

77
leepowers

これを使って:

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になります...

108
OMG Ponies

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

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

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

その行のデータを取得するためだけの絞り込みフィルター条件を使用した最初のクエリと、2番目のクエリはCOUNT句とWHERE句を使用して位置を計算します。

例えばあなたの場合

クエリ1:

_SELECT * FROM tbl WHERE name = 'Beta'_

クエリ2:

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

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

8
Max

私は非常によく似た問題を抱えているので、同じ質問はしませんが、ここで私が何をしたかを共有します。AVGによるグループ化と順序付けも使用する必要がありました。署名とソコアを持つ学生がいて、それらをランク付けする必要がありました(つまり、最初にAVGを計算し、次にDESCでそれらを注文し、最後に位置を追加する必要がありました(ランク付け)ので、何か非常に似ているベストアンサーとして、ここで、私の問題に合わせて少し変更します):

最後に、外部SELECTにposition(私のランク)列を追加しました

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 

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

したがって、それらの行をカウントする必要があります。

SELECT COUNT(*)+ 1 FROM table WHERE name <'Beta'

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

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

質問の所有者がすでに問題を解決したと思うので、これが将来似たようなものを検索する人々を助けることを願っています。

1
NVG

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

ここに簡単な例があります、列のあるテーブルがあるとしましょう:

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() を使用できます。

1
Kai Noack

私は受け入れられた答えを通り抜けていましたが、少し複雑に思えたので、ここに簡単なバージョンがあります。

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