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)
ペアの最高の単一行を取得できます。
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)
ペアの最高の単一行を取得できます。
回答:
各(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を持つ場合、複数の行を返します。それが問題である場合、いくつかの後処理が必要になる場合があります。)
SELECT t1.id FROM test t1 LEFT JOIN test t2 ON t1.id>t2.id WHERE t2.id IS NULL;
ためにn*n/2 + n/2
IS NULL比較が必要ではありませんか?表示されない最適化は発生しますか?私は別のスレッドでビルに同様の質問をしようとしましたが、彼はそれを無視したようです。
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)
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に実装する必要があるもののロジックをカバーしています。
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;
私はいつもこのパターンに従います。このテーブルを考えると:
+------+------+
| 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
お役に立てば幸いです。
<
、>
、<=
、>=
アルファベット順にハンドルCHARとVARCHARデータ型。私は期待しています、まさにあなたが探しているものです。
row_numbers <= 2
です。この回答に非常に感謝しています。モスティ、完璧です。
SELECT
@i:=@i+1 AS iterator,
t.*
FROM
tablename AS t,
(SELECT @i:=0) AS foo
この記事を確認してください。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つのフィールドに対してこれをテストしました。お役に立てれば。このクエリを分解して説明している記事をご覧ください。
limit 18446744073709551615
、強制order by
句に追加してください。
concat_ws
空の文字列''
は危険です:concat_ws('',12,3) = concat_ws('',1,23)
。セパレータを使用するか、'_'
@ Kenneth Xuソリューションを使用することをお勧めします。
MySQL 8.0.0
以上から、ウィンドウ関数をネイティブに使用できます。
ウィンドウ関数。
MySQLは、クエリの各行に対して、その行に関連する行を使用して計算を実行するウィンドウ関数をサポートするようになりました。これらには、RANK()、LAG()、NTILE()などの関数が含まれます。さらに、いくつかの既存の集約関数をウィンドウ関数として使用できるようになりました。たとえば、SUM()やAVG()などです。
パーティション内の現在の行の番号を返します。行番号の範囲は、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;
私はまた、彼のクエリコードに小さな変更を加えたモスティモスタチョのソリューションに投票します。
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を使用しない唯一の違いは、代わりにネストされた選択に依存しています。
私は関数を定義します:
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;
これで、ビューに含めることができないサブクエリはありません。
mysqlのrow_numberのクエリ
set @row_number=0;
select (@row_number := @row_number +1) as num,id,name from sbs
行番号機能を模倣することはできません。期待どおりの結果が得られるかもしれませんが、ある段階でがっかりするでしょう。mysqlのドキュメントには次のように記載されています。
SELECTなどの他のステートメントでは、期待どおりの結果が得られる場合がありますが、これは保証されていません。次のステートメントでは、MySQLが最初に@aを評価し、次に2番目に代入を行うと考えるかもしれません。SELECT @a、@a:= @ a + 1、...; ただし、ユーザー変数を含む式の評価の順序は定義されていません。
よろしく、Georgi。
MariaDB 10.2は、RANK()、ROW_NUMBER()などの「ウィンドウ関数」を実装しています。
https://mariadb.com/kb/en/mariadb/window-functions/
今月のPercona Liveでの講演に基づいて、それらはかなり最適化されています。
構文は質問のコードと同じです。
「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
この簡単な例では、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
少し遅れますが、答えを探す人にも役立つかもしれません...
行/行番号の例-任意の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
また少し遅くなりましたが、今日は同じニーズがあったため、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の明確なデモンストレーションに感謝します。
ORDER BY
サブクエリでは無視できることに注意してください(mariadb.com/kb/en/mariadb/…を参照)。これに対する推奨される解決策はLIMIT 18446744073709551615
、ソートを強制するサブクエリに追加することです。ただし、これはパフォーマンスの問題を引き起こす可能性があり、巨大なテーブルを本当に
これも解決策になる可能性があります:
SET @row_number = 0;
SELECT
(@row_number:=@row_number + 1) AS num, firstName, lastName
FROM
employees
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;
重要: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)
これは最も堅牢なソリューションではありませんが、いくつかの異なる値のみを持つフィールドにパーティションランクを作成するだけの場合は、必要な数の変数を持つロジックでケースを使用するのは面倒ではありません。
このような何かが過去に私のために働いてきました:
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>
;
それが理にかなっている/役立つことを願っています!
これは、複数の列がある場合に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
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;
greatest-n-per-group
して、同様の質問に案内します。