MySQLのROW_NUMBER()


281

MySQLにSQL Server関数を複製する良い方法はありますROW_NUMBER()か?

例えば:

SELECT 
    col1, col2, 
    ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY col3 DESC) AS intRow
FROM Table1

次に、たとえば、条件intRowを1に制限してcol3、各(col1, col2)ペアの最高の単一行を取得できます。


9
でタグ付けgreatest-n-per-groupして、同様の質問に案内します。
ビルカーウィン

1
Sql-Serverこれは結合タグ検索で最高の投票項目でしたが、SQL Serverには実際には関係がないため、タグを削除しました。
マーティンスミス

単純なmysql行番号関数については、datamakessense.com
rownum

MySQLの場合、唯一の効率的なソリューションはmysql.rjweb.org/doc.php/groupwise_maxで説明されています。多くの公開されているソリューションでは、フルテーブルスキャン(またはそれ以上)が必要です。
リックジェームズ

2
MySQLの実際の分析関数ROW_NUMBER、RANK、DESNSE_RANKの恥知らずな自己促進ソリューション
Kenneth Xu

回答:


102

各(col1、col2)ペアに対して単一の最も高いcol3を持つ行が必要です。

これはグループごとの最大値であり、最もよく聞かれるSQLの質問の1つです(簡単なように思えますが、実際はそうではありません)。

私はしばしばnull-self-joinのためにふくよかです:

SELECT t0.col3
FROM table AS t0
LEFT JOIN table AS t1 ON t0.col1=t1.col1 AND t0.col2=t1.col2 AND t1.col3>t0.col3
WHERE t1.col1 IS NULL;

「col1、col2に一致する他の行でcol3の値が高いテーブルの行を取得します。」(これに気づくでしょう。他のほとんどのグループワイズ最大のソリューションは、複数の行が同じcol1、col2、col3を持つ場合、複数の行を返します。それが問題である場合、いくつかの後処理が必要になる場合があります。)


2
しかし、(col1、col2)ペアにcol3の最大値が2つある場合はどうでしょうか。あなたは2行になるでしょう。
ポール

@ポール:はい!ティック前の回答にそのことについてのメモを追加しました。通常、アプリケーションレイヤーで不要な余分な行をランダムに後で簡単に削除できますが、同じcol3の行が多数ある場合、問題が発生する可能性があります。
ボビンス2009

1
ボビンス、ソリューションはここSOでかなり人気になりましたが、私は質問があります。ソリューションは基本的に、誰かが次のクエリで最大のIDを見つけようとする場合と同じです。単一の行を見つけるSELECT t1.id FROM test t1 LEFT JOIN test t2 ON t1.id>t2.id WHERE t2.id IS NULL;ためにn*n/2 + n/2IS NULL比較が必要ではありませんか?表示されない最適化は発生しますか?私は別のスレッドでビルに同様の質問をしようとしましたが、彼はそれを無視したようです。
12

2
@Paul-グループごとの最大に一致する複数の行が存在し、1つだけを取得したい場合に対処するために、常にON句ロジックに主キーを追加して、結合を解除できます... SELECT t0.col3 FROM table AS t0 LEFT JOINテーブルAS t1 ON t0.col1 = t1.col1 AND t0.col2 = t1.col2 AND(t1.col3、t1.pk)>(t0.col3、t0.pk)WHERE t1.col1 IS NULL;
ジョンアームストロング-Xgc 2012年

2
これはより読みやすくなりますSELECT t0.col3 FROM table AS t0 WHERE NOT EXISTS (select 1 from table AS t1 ON t0.col1=t1.col1 AND t0.col2=t1.col2 AND t1.col3>t0.col3)
wrschneider

204

MySQLにはランキング機能はありません。あなたが得ることができる最も近いものは変数を使うことです:

SELECT t.*, 
       @rownum := @rownum + 1 AS rank
  FROM YOUR_TABLE t, 
       (SELECT @rownum := 0) r

私の場合、それはどのように機能しますか?col1とcol2のそれぞれに1つずつ、2つの変数が必要ですか?col1が変更された場合、col2は何らかの方法でリセットする必要があります。

はい。Oracleの場合、LEAD関数を使用して、次の値でピークに達することができます。ありがたいことに、QuassnoiはMySQLに実装する必要があるもののロジックをカバーしています


1
うーん....だから、私の場合、それはどのように機能しますか?col1とcol2のそれぞれに1つずつ、2つの変数が必要ですか?col1が変更された場合、col2は何らかの方法でリセットする必要があります。
ポール

おかげで...上で述べたように、この答えはボビンスのものと同じように受け入れられますが、チェックできるのは1つだけです:-)
Paul

9
同じステートメント内のユーザー定義変数への割り当ておよびユーザー定義変数からの読み取りは信頼できません。これは、ここに記載されています:dev.mysql.com/doc/refman/5.0/en/user-variables.html:「一般的なルールとして、値をユーザー変数に割り当てて、同じステートメント内の値を読み取ることはできません。期待どおりの結果が得られる可能性がありますが、これは保証されません。ユーザー変数を含む式の評価順序は定義されておらず、特定のステートメントに含まれる要素に基づいて変更される場合があります。」
Roland Bouman

1
@Roland:私は小さなデータセットでのみテストしましたが、問題はありませんでした。MySQLはまだ機能に対応できていない-リクエストは2008年以来続いている
OMGポニー

2
ローランドが指摘するように、これは未定義の動作のようです。たとえば、これは私が試したテーブルに対して完全に正しくない結果を与えます:SELECT @row_num:=@row_num+1 AS row_number, t.id FROM (SELECT * FROM table1 WHERE col = 264 ORDER BY id) t, (SELECT @row_num:=0) var;
jberryman

81

私はいつもこのパターンに従います。このテーブルを考えると:

+------+------+
|    i |    j |
+------+------+
|    1 |   11 |
|    1 |   12 |
|    1 |   13 |
|    2 |   21 |
|    2 |   22 |
|    2 |   23 |
|    3 |   31 |
|    3 |   32 |
|    3 |   33 |
|    4 |   14 |
+------+------+

あなたはこの結果を得ることができます:

+------+------+------------+
|    i |    j | row_number |
+------+------+------------+
|    1 |   11 |          1 |
|    1 |   12 |          2 |
|    1 |   13 |          3 |
|    2 |   21 |          1 |
|    2 |   22 |          2 |
|    2 |   23 |          3 |
|    3 |   31 |          1 |
|    3 |   32 |          2 |
|    3 |   33 |          3 |
|    4 |   14 |          1 |
+------+------+------------+

このクエリを実行すると、変数を定義する必要がありません。

SELECT a.i, a.j, count(*) as row_number FROM test a
JOIN test b ON a.i = b.i AND a.j >= b.j
GROUP BY a.i, a.j

お役に立てば幸いです。


1
列がVARCHARまたはCHARの場合、この構造でそれをどのように処理できますか?
Tushar 14

3
あなたは素晴らしいモスティです、私はまさにこれを探しています
ラッキークリッシュ2014

row_numberのロジックを使用してこの回答を提供しました。ありがとう。
Utsav、2015

オペレーター@Tushar <><=>=アルファベット順にハンドルCHARとVARCHARデータ型。私は期待しています、まさにあなたが探しているものです。
アレックス、2017

1
@AlmazVildanovこのクエリは、単にフィルタアウトのサブクエリとして使用できるはずrow_numbers <= 2 です。この回答に非常に感謝しています。モスティ、完璧です。
Zax

61
SELECT 
    @i:=@i+1 AS iterator, 
    t.*
FROM 
    tablename AS t,
    (SELECT @i:=0) AS foo

1
最初の:=は@OMGポニーの回答から欠落しているようです。このピータージョンソンを投稿していただきありがとうございます。
ショルシンガー、2011

(SELECT @i:= 0)AS fooは、特に他のテーブルが副選択を使用している場合、FROMステートメントの最初のテーブルである必要があります
andig

なぜ「.. as foo」も必要なのですか?
Tom Chiverton、2015

@TomChiverton欠落している場合、「エラーコード:1248。すべての派生テーブルには独自のエイリアスが必要です」
ExStackChanger

1
ここでのランク割り当ては完全に未定義であり、これは質問に答えることすらありません
jberryman

27

この記事を確認してください。MySQLでパーティションを使用してSQL ROW_NUMBER()を模倣する方法を示しています。私はWordPressの実装でこれと同じシナリオに遭遇しました。ROW_NUMBER()が必要でしたが、ありませんでした。

http://www.explodybits.com/2011/11/mysql-row-number/

この記事の例では、フィールドごとに単一のパーティションを使用しています。追加のフィールドで分割するには、次のようにします。

  SELECT  @row_num := IF(@prev_value=concat_ws('',t.col1,t.col2),@row_num+1,1) AS RowNumber
         ,t.col1 
         ,t.col2
         ,t.Col3
         ,t.col4
         ,@prev_value := concat_ws('',t.col1,t.col2)
    FROM table1 t,
         (SELECT @row_num := 1) x,
         (SELECT @prev_value := '') y
   ORDER BY t.col1,t.col2,t.col3,t.col4 

concat_wsを使用すると、nullが処理されます。私は、int、date、およびvarcharを使用して、3つのフィールドに対してこれをテストしました。お役に立てれば。このクエリを分解して説明している記事をご覧ください。


1
驚くばかり。これは実際にパーティショニングを行います。非常に便利
スチュアートワット

1
自己結合と比較すると、これははるかに効率的ですが、ロジックに問題があり、row_numを計算する前に順序を実行する必要があります。concatも必要ありません。`` `SELECT @row_num:= IF(@ prev_col1 = t.col1 AND @ prev_col2 = t.col2)、@ row_num + 1、1)AS RowNumber、t.col1、t.col2、t.col3、t.col4 、@ prev_col1:= t.col1、@ prev_col2:= t.col2 FROM(SELECT * FROM table1 ORDER BY col1、col2、col3)t、(SELECT @row_num:= 1、@ prev_col1:= ''、@ prev_col2: = '')var `` `
Kenneth Xu

これをサブクエリに入れる必要がある場合はlimit 18446744073709551615、強制order by句に追加してください。
xmedeko 2017年

concat_ws空の文字列''は危険です:concat_ws('',12,3) = concat_ws('',1,23)。セパレータを使用するか、'_'@ Kenneth Xuソリューションを使用することをお勧めします。
xmedeko 2017年

opのリンクが停止しています。ここにリンクのアーカイブ
user2426679

25

MySQL 8.0.0以上から、ウィンドウ関数をネイティブに使用できます。

1.4 MySQL 8.0の新機能

ウィンドウ関数。

MySQLは、クエリの各行に対して、その行に関連する行を使用して計算を実行するウィンドウ関数をサポートするようになりました。これらには、RANK()、LAG()、NTILE()などの関数が含まれます。さらに、いくつかの既存の集約関数をウィンドウ関数として使用できるようになりました。たとえば、SUM()やAVG()などです。

ROW_NUMBER()over_clause

パーティション内の現在の行の番号を返します。行番号の範囲は、1からパーティション行の数です。

ORDER BYは、行が番号付けされる順序に影響します。ORDER BYがない場合、行番号付けは不確定です。

デモ:

CREATE TABLE Table1(
  id INT AUTO_INCREMENT PRIMARY KEY, col1 INT,col2 INT, col3 TEXT);

INSERT INTO Table1(col1, col2, col3)
VALUES (1,1,'a'),(1,1,'b'),(1,1,'c'),
       (2,1,'x'),(2,1,'y'),(2,2,'z');

SELECT 
    col1, col2,col3,
    ROW_NUMBER() OVER (PARTITION BY col1, col2 ORDER BY col3 DESC) AS intRow
FROM Table1;

DBFiddleデモ


1
ため息...ついに!
Used_By_Already

15

私はまた、彼のクエリコードに小さな変更を加えたモスティモスタチョのソリューションに投票します。

SELECT a.i, a.j, (
    SELECT count(*) from test b where a.j >= b.j AND a.i = b.i
) AS row_number FROM test a

同じ結果が得られます:

+------+------+------------+
|    i |    j | row_number |
+------+------+------------+
|    1 |   11 |          1 |
|    1 |   12 |          2 |
|    1 |   13 |          3 |
|    2 |   21 |          1 |
|    2 |   22 |          2 |
|    2 |   23 |          3 |
|    3 |   31 |          1 |
|    3 |   32 |          2 |
|    3 |   33 |          3 |
|    4 |   14 |          1 |
+------+------+------------+

テーブルの場合:

+------+------+
|    i |    j |
+------+------+
|    1 |   11 |
|    1 |   12 |
|    1 |   13 |
|    2 |   21 |
|    2 |   22 |
|    2 |   23 |
|    3 |   31 |
|    3 |   32 |
|    3 |   33 |
|    4 |   14 |
+------+------+

クエリがJOINとGROUP BYを使用しない唯一の違いは、代わりにネストされた選択に依存しています。


これはもっと良いはずですか?どちらも二次式のようですが、EXPLAIN出力をどのように解釈するかわかりません
jberryman

実際、ネストされた選択はMySQLであまり最適化されていないことがわかっているため、このanwserはクエリ手法のデモンストレーションにすぎません。上記の変数ベースの例は、ほとんどの実際的なケースに適しています。
abcdn 2017

1
変数ベースの回答が実際に定義された動作を使用しているとは確信していません...
jberryman

申し訳ありませんが、「定義された動作」によってあなたが何を意味するのかわかりません。それはあなたにとってうまくいかないということですか、それともそれが文書化されていないことを心配しているだけですか?
abcdn 2017

1
「未定義の動作」とは、動作することが文書化されていないか、動作することが保証されていないことが文書化されていることを意味します。このページのコメントにあるドキュメントの引用とリンクを参照してください。それ人が望んでいるものを(不当に)返したり、推測したり、仮定したり、空想したりするものを返すかもしれません。実装の特定のバージョンでは、CASEのインクリメントと変数の使用を使用する特定のクエリ式が、コードを調べることにより、Perconaのプログラマーによって機能することが示されています。これはリリースによって変わる可能性があります。
philipxy

12

私は関数を定義します:

delimiter $$
DROP FUNCTION IF EXISTS `getFakeId`$$
CREATE FUNCTION `getFakeId`() RETURNS int(11)
    DETERMINISTIC
begin
return if(@fakeId, @fakeId:=@fakeId+1, @fakeId:=1);
end$$

それから私はできる:

select getFakeId() as id, t.* from table t, (select @fakeId:=0) as t2;

これで、ビューに含めることができないサブクエリはありません。


1つの制限で機能します。クエリを複数回実行すると、同じ結果セットに対して、常に増加するfakeIdが取得されます
Stephan Richter

set @fakeId = 0を送信できます。クエリを実行するたびに、最適ではなく機能する
jmpeace

DETERMINISTICを削除すると、非常に奇妙な問題が発生します。その後、order byを使用すると、fakeIdが正しくありません。どうしてこれなの?
Chris Muench

8

mysqlのrow_numberのクエリ

set @row_number=0;
select (@row_number := @row_number +1) as num,id,name from sbs

これはUPDATEクエリで使用できますか?試行中ですが、「列のデータが切り捨てられました...」というエラーが表示されます。
ディエゴ

1
誰かがUPDATEでそれを使用することに興味がある場合は、機能するようにサブクエリとして使用する必要があります。UPDATE <テーブル> SET <フィールド> =(SELECT \ @row_number:= \ @row_number +1)ORDER BY <your order column>; 順序列は、行の値の順序を決定します。
ディエゴ

8

そこのような何の目的球はありませんrownumrow_num()MySQLのではなく、回避する方法は、以下のようなものです:

select 
      @s:=@s+1 serial_no, 
      tbl.* 
from my_table tbl, (select @s:=0) as s;

4

私が最もうまく機能することがわかった解決策は、次のようなサブクエリを使用することでした。

SELECT 
    col1, col2, 
    (
        SELECT COUNT(*) 
        FROM Table1
        WHERE col1 = t1.col1
        AND col2 = t1.col2
        AND col3 > t1.col3
    ) AS intRow
FROM Table1 t1

PARTITION BY列は「=」と比較され、ANDで区切られます。ORDER BY列は「<」または「>」と比較され、ORで区切られます。

これは少しコストがかかりますが、非常に柔軟であることがわかりました。


4

行番号機能を模倣することはできません。期待どおりの結果が得られるかもしれませんが、ある段階でがっかりするでしょう。mysqlのドキュメントには次のように記載されています。

SELECTなどの他のステートメントでは、期待どおりの結果が得られる場合がありますが、これは保証されていません。次のステートメントでは、MySQLが最初に@aを評価し、次に2番目に代入を行うと考えるかもしれません。SELECT @a、@a:= @ a + 1、...; ただし、ユーザー変数を含む式の評価の順序は定義されていません。

よろしく、Georgi。


私はついていません。「@i:= @i + 1 as position」は、「ROW_NUMBER()over(order(sum(score)desc)by position)」の直接の置き換えではないのですか?
Tom Chiverton、2015

1
@TomChivertonその動作は定義されていないので、マニュアルにはそのとおりです。
philipxy


2

「PARTITION BY」の部分をカバーする簡単な答えは見当たらないので、これが私のものです。

SELECT
    *
FROM (
    select
        CASE WHEN @partitionBy_1 = l THEN @row_number:=@row_number+1 ELSE @row_number:=1 END AS i
        , @partitionBy_1:=l AS p
        , t.*
    from (
        select @row_number:=0,@partitionBy_1:=null
    ) as x
    cross join (
        select 1 as n, 'a' as l
        union all
        select 1 as n, 'b' as l    
        union all
        select 2 as n, 'b' as l    
        union all
        select 2 as n, 'a' as l
        union all
        select 3 as n, 'a' as l    
        union all    
        select 3 as n, 'b' as l    
    ) as t
    ORDER BY l, n
) AS X
where i > 1
  • ORDER BY句は、ROW_NUMBERのニーズを反映する必要があります。したがって、すでに明確な制限があります。このフォームの複数のROW_NUMBER「エミュレーション」を同時に持つことはできません。
  • 「計算列」の順序は重要です。mysqlでこれらの列を別の順序で計算すると、機能しない場合があります。
  • この簡単な例では、1つだけ配置しますが、「PARTITION BY」パーツを複数持つことができます

        CASE WHEN @partitionBy_1 = part1 AND @partitionBy_2 = part2 [...] THEN @row_number:=@row_number+1 ELSE @row_number:=1 END AS i
        , @partitionBy_1:=part1 AS P1
        , @partitionBy_2:=part2 AS P2
        [...] 
    FROM (
        SELECT @row_number:=0,@partitionBy_1:=null,@partitionBy_2:=null[...]
    ) as x

1

少し遅れますが、答えを探す人にも役立つかもしれません...

行/行番号の例-任意のSQLで使用できる再帰クエリ:

WITH data(row_num, some_val) AS 
(
 SELECT 1 row_num, 1 some_val FROM any_table --dual in Oracle
  UNION ALL
 SELECT row_num+1, some_val+row_num FROM data WHERE row_num < 20 -- any number
)
SELECT * FROM data
 WHERE row_num BETWEEN 5 AND 10
/

ROW_NUM    SOME_VAL
-------------------
5           11
6           16
7           22
8           29
9           37
10          46

2
申し訳ありませんが、私の知る限り、MySQLは一般的なテーブル式をサポートしていません。
アルバロ・ゴンサレス

それは... @ÁlvaroGonzálezMySQLの8この答えは本当に古いMySQLバージョンで使用しても意味がありませんのでだけ.. CTEとウィンドウ機能をサポートし、今ん
レイモンドNijland

1

これにより、ROW_NUMBER()およびPARTITION BYが提供するのと同じ機能をMySQLで実現できます。

SELECT  @row_num := IF(@prev_value=GENDER,@row_num+1,1) AS RowNumber
       FirstName, 
       Age,
       Gender,
       @prev_value := GENDER
  FROM Person,
      (SELECT @row_num := 1) x,
      (SELECT @prev_value := '') y
  ORDER BY Gender, Age DESC

1

また少し遅くなりましたが、今日は同じニーズがあったため、Googleで検索し、最後に、Pinal Daveの記事http://blog.sqlauthority.com/2014/03/09/mysql-reset-rowにある簡単な一般的なアプローチを見つけました-行番号ごとの各グループパーティションの番号/

Paulの元の質問(これも私の問題でした)に焦点を当てたかったので、私の解決策を実際の例として要約します。

2つの列に分割したいので、反復中にSET変数を作成して、新しいグループが開始されたかどうかを識別します。

SELECT col1, col2, col3 FROM (
  SELECT col1, col2, col3,
         @n := CASE WHEN @v = MAKE_SET(3, col1, col2)
                    THEN @n + 1 -- if we are in the same group
                    ELSE 1 -- next group starts so we reset the counter
                END AS row_number,
         @v := MAKE_SET(3, col1, col2) -- we store the current value for next iteration
    FROM Table1, (SELECT @n := 0, @v := NULL) r -- helper table for iteration with startup values
   ORDER BY col1, col2, col3 DESC -- because we want the row with maximum value
) x WHERE row_number = 1 -- and here we select exactly the wanted row from each group

3は、MAKE_SETの最初のパラメーターで、SETに両方の値が必要であることを意味します(3 = 1 | 2)。もちろん、グループを構成する列が2つ以上ない場合は、MAKE_SET操作を削除できます。構造はまったく同じです。これは必要に応じて私のために働いています。Pinal Daveの明確なデモンストレーションに感謝します。


1
ORDER BYサブクエリでは無視できることに注意してください(mariadb.com/kb/en/mariadb/…を参照)。これに対する推奨される解決策はLIMIT 18446744073709551615、ソートを強制するサブクエリに追加することです。ただし、これはパフォーマンスの問題を引き起こす可能性があり、巨大なテーブルを本当に
狂わせるに

1

これも解決策になる可能性があります:

SET @row_number = 0;

SELECT 
    (@row_number:=@row_number + 1) AS num, firstName, lastName
FROM
    employees

ただし、パーティショニングは行われず、引用されているより高い回答
Caius Jard

1

MySQLはバージョン8.0以降からROW_NUMBER()をサポートしています

MySQL 8.0以降を使用している場合は、ROW_NUMBER()関数を確認してください。それ以外の場合は、ROW_NUMBER()関数をエミュレートします。

row_number()は、最初の行を1として、行の連番を返すランキング関数です。

古いバージョンの場合、

SELECT t.*, 
       @rowid := @rowid + 1 AS ROWID
  FROM TABLE t, 
       (SELECT @rowid := 0) dummy;

1

重要:MySQL 8+へのアップグレードを検討し、定義および文書化されたROW_NUMBER()関数を使用して、機能が制限されたMySQLの古いバージョンに関連付けられた古いハックを破棄してください

次に、これらのハックの1つを示します。

クエリ内変数をほとんど/すべて使用するここでの回答は、ドキュメントに記載されている(言い換え)という事実を無視しているようです:

SELECTリストの項目が上から順に評価されることに依存しないでください。1つのSELECT項目に変数を割り当てて、別の項目で使用しないでください

そのため、彼らは間違った答えを生み出すリスクがあります。

select
  (row number variable that uses partition variable),
  (assign partition variable)

これらがボトムアップで評価されると、行番号は機能しなくなります(パーティションなし)。

したがって、実行順序が保証されたものを使用する必要があります。ケースを入力:

SELECT
  t.*, 
  @r := CASE 
    WHEN col = @prevcol THEN @r + 1 
    WHEN (@prevcol := col) = null THEN null
    ELSE 1 END AS rn
FROM
  t, 
  (SELECT @r := 0, @prevcol := null) x
ORDER BY col

アウトラインldと同様に、prevcolの割り当て順序が重要です。prevcolは、現在の行の値を割り当てる前に現在の行の値と比較する必要があります(そうでない場合は、前の行のcol値ではなく、現在の行のcol値になります) 。

これがどのように組み合わされるかは次のとおりです。

  • 最初のWHENが評価されます。この行のcolが前の行のcolと同じ場合、@ rがインクリメントされ、CASEから返されます。この戻り値は@rに格納されます。割り当てが@rに割り当てられたものの新しい値を結果行に返すのは、MySQLの機能です。

  • 結果セットの最初の行では、@ prevcolはnull(サブクエリでnullに初期化されます)なので、この述語はfalseです。この最初の述語は、colが変更されるたびにfalseを返します(現在の行は前の行とは異なります)。これにより、2番目のWHENが評価されます。

  • 2番目のWHEN述語は常にfalseであり、@ prevcolに新しい値を割り当てるためだけに存在します。この行のcolは前の行のcolとは異なるため(同じである場合、最初のWHENが使用されていたため、これを知っています)、次回のテスト用に保持するために新しい値を割り当てる必要があります。割り当てが行われた後、割り当ての結果がnullと比較され、nullと等しいものはすべてfalseであるため、この述語は常にfalseです。しかし、少なくともそれを評価することは、この行からのcolの値を維持するという仕事をしたので、次の行のcol値に対して評価することができます

  • 2番目のWHENはfalseであるため、(col)でパーティション化している列が変更された場合、@ rに新しい値を与えるのはELSEであり、番号付けを1から再開します

これは、次のような状況になります。

SELECT
  t.*, 
  ROW_NUMBER() OVER(PARTITION BY pcol1, pcol2, ... pcolX ORDER BY ocol1, ocol2, ... ocolX) rn
FROM
  t

一般的な形式を持っています:

SELECT
  t.*, 
  @r := CASE 
    WHEN col1 = @pcol1 AND col2 = @pcol2 AND ... AND colX = @pcolX THEN @r + 1 
    WHEN (@pcol1 := pcol1) = null OR (@pcol2 := col2) = null OR ... OR (@pcolX := colX) = null THEN null
    ELSE 1 
  END AS rn
FROM
  t, 
  (SELECT @r := 0, @pcol1 := null, @pcol2 := null, ..., @pcolX := null) x
ORDER BY pcol1, pcol2, ..., pcolX, ocol1, ocol2, ..., ocolX

脚注:

  • pcol内のpは「パーティション」を意味し、ocol内のoは「順序」を意味します。一般的な形式では、視覚的な混乱を減らすために変数名から「prev」を削除しました

  • 括弧(@pcolX := colX) = nullは重要です。それらがないと、@ pcolXにnullが割り当てられ、処理が停止します。

  • 前の列の比較が機能するためには、結果セットもパーティション列で順序付けする必要があるという妥協です。したがって、1つの列に基づいて行番号を並べ替えることはできませんが、結果セットは別の列に並べ替えることができます。サブクエリでこれを解決できる可能性がありますが、LIMITを使用しない限りサブクエリの順序が無視され、これが影響する可能性があることもドキュメントに記載されていると思いますパフォーマンス

  • 私はメソッドが機能することをテストする以外にそれを掘り下げていませんが、2番目のWHENの述部が最適化されてしまうリスクがある場合(nullと比較してすべてがnull / falseなので、わざわざ割り当てを実行する必要があります)、実行されません、それも停止します。これは私の経験では発生しないようですが、合理的に発生する可能性がある場合は、コメントを受け入れて解決策を提案します

  • @pcolX変数を作成するサブクエリで、@ pcolXを作成するnullを実際の列のタイプにキャストするのが賢明な場合があります。 select @pcol1 := CAST(null as INT), @pcol2 := CAST(null as DATE)


これには正当な理由はありません。同じ変数に割り当て、同じ変数から読み取る他の回答と同じです。
philipxy

より詳細なフィルを提供できますか?
Caius Jard

このページの他のコメントを参照してください。「site:stackoverflow.com "philipxy" mysql変数(設定OR割り当てOR割り当てOR書き込み)読み取り」をグーグルする:私の回答&承認された回答がマニュアルを引用しているこの質問のコメントにリンクされたバグレポート主張に反して、それとは矛盾する何かをすることは問題ありません。手動の再変数と再割り当てをお読みください。
philipxy


あなたの懸念を理解しました
Caius Jard

0

これは最も堅牢なソリューションではありませんが、いくつかの異なる値のみを持つフィールドにパーティションランクを作成するだけの場合は、必要な数の変数を持つロジックでケースを使用するのは面倒ではありません。

このような何かが過去に私のために働いてきました:

SELECT t.*, 
   CASE WHEN <partition_field> = @rownum1 := @rownum1 + 1 
     WHEN <partition_field> = @rownum2 := @rownum2 + 1 
     ...
     END AS rank
FROM YOUR_TABLE t, 
   (SELECT @rownum1 := 0) r1, (SELECT @rownum2 := 0) r2
ORDER BY <rank_order_by_field>
;

それが理にかなっている/役立つことを願っています!


-1

これは、複数の列がある場合にRowNumberを作成するのに最適です。この場合は2列です。

SELECT @row_num := IF(@prev_value= concat(`Fk_Business_Unit_Code`,`NetIQ_Job_Code`), @row_num+1, 1) AS RowNumber, 
    `Fk_Business_Unit_Code`,   
    `NetIQ_Job_Code`,  
    `Supervisor_Name`,  
    @prev_value := concat(`Fk_Business_Unit_Code`,`NetIQ_Job_Code`)  
FROM (SELECT DISTINCT `Fk_Business_Unit_Code`,`NetIQ_Job_Code`,`Supervisor_Name`         
      FROM Employee    
      ORDER BY `Fk_Business_Unit_Code`, `NetIQ_Job_Code`, `Supervisor_Name` DESC) z,  
(SELECT @row_num := 1) x,  
(SELECT @prev_value := '') y  
ORDER BY `Fk_Business_Unit_Code`, `NetIQ_Job_Code`,`Supervisor_Name` DESC

-3
set @i = 1;  
INSERT INTO ARG_VALUE_LOOKUP(ARG_VALUE_LOOKUP_ID,ARGUMENT_NAME,VALUE,DESCRIPTION,UPDATE_TIMESTAMP,UPDATE_USER,VER_NBR,OBJ_ID) 
select @i:= @i+1 as ARG_VALUE_LOOKUP_ID,ARGUMENT_NAME,VALUE,DESCRIPTION,CURRENT_TIMESTAMP,'admin',1,UUID() 
FROM TEMP_ARG_VALUE_LOOKUP 
order by ARGUMENT_NAME;

1
回答をフォーマットしてみて、何をしようとしているかについて追加のコンテキストを提供してください。現時点では、フォーマットが不十分なテキストにすぎません。
Yannick Meeus、2015

2
これは、元の質問とは関係がないようです。ご不明な点がございましたら、別途お問い合わせください。
Jeroen Mostert、2015

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