諸事情でWindow関数を触る機会があって、ちょっと前にyokuさんに教えてもらったWindow関数ネタを思い出したので1つ。
Oracle11gにはMySQLでおなじみ?のLIMIT,OFFSETがない。
そこで(いろいろやり方はあるけど)window関数を使ってあらわしたりする。
例えば id(number), value(varchar2) のテーブルならこんな感じ。
SELECT * FROM
(
SELECT row_number() over order by id asc) as row_number,
value
FROM t1
) a
WHERE a.row_number between 500 and 600;
MySQL8からwindow関数が追加され、row_number()とかもつかえるのでこのクエリをそのままMySQLで使えるか検討してみる。 環境はMySQL8.0.13。データは前回のブログで使った100万行くらい入ってるシンプルなテーブルのやつ
mysql [d1]> SHOW CREATE TABLE t1\G
*************************** 1. row ***************************
Table: t1
Create Table: CREATE TABLE `t1` (
`id` int(11) NOT NULL,
`value` varchar(256) COLLATE utf8mb4_bin DEFAULT NULL,
PRIMARY KEY (`id`)
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_bin
1 row in set (0.00 sec)
mysql [d1]> SELECT COUNT(1) FROM t1;
+----------+
| COUNT(1) |
+----------+
| 1011000 |
+----------+
1 row in set (0.15 sec)
mysql [d1]> SELECT * FROM
-> (
-> SELECT row_number() over (order by id) as row_no,
-> value
-> FROM t1
-> ) a
-> WHERE a.row_no between 500 and 600;
+--------+------------+
| row_no | value |
+--------+------------+
| 500 | 1zwujbeNGW |
| 501 | qz8df1RHcc |
| 502 | XKH92CeXGa |
| 503 | zWc7JQeOH0 |
| 504 | AkwB5KClKD |
| 505 | rP1ZUAOc80 |
~~
| 595 | oDdB7ZR1jD |
| 596 | kqUuhKOELK |
| 597 | o4mAu0y2KG |
| 598 | 5e0EbD9OXW |
| 599 | yUyRUrOy9d |
| 600 | 5J4wVaIKOJ |
+--------+------------+
101 rows in set (0.91 sec)
お、ちゃんと出力された。SQL的には(当たり前だけど)問題ないっぽい。 これでOracleのテーブルをMySQLに移行するときのSQLの書き換える量がへりますね。(めでたしめでたし。
ただ、0.91 secはやっぱ遅い気がする。
mysql [d1]> SELECT id, value FROM t1 ORDER BY id LIMIT 101 OFFSET 499; +-----+------------+ | id | value | +-----+------------+ | 500 | 1zwujbeNGW | | 501 | qz8df1RHcc | | 502 | XKH92CeXGa | | 503 | zWc7JQeOH0 | | 504 | AkwB5KClKD | | 505 | rP1ZUAOc80 | | 506 | WJSjD0NWJb | | 507 | 3j6lvvGYvH | ~~ | 598 | 5e0EbD9OXW | | 599 | yUyRUrOy9d | | 600 | 5J4wVaIKOJ | +-----+------------+ 101 rows in set (0.00 sec)
。。。ですよね〜。
ということでOFFSETを深くしてprofilingをとってみる。
mysql [d1]> SELECT * FROM
-> (
-> SELECT row_number() over (order by id) as row_no,
-> value
-> FROM t1
-> ) a
-> WHERE a.row_no between 1000000 and 1000010;
+---------+------------+
| row_no | value |
+---------+------------+
| 1000000 | 5UGdDjX6sI |
| 1000001 | 1BmxMSJMNO |
| 1000002 | fRA4G96KoZ |
| 1000003 | 0PEhFBERn3 |
| 1000004 | x9N5k3eez5 |
| 1000005 | KeIyt1RHX5 |
| 1000006 | BaDGNWrvPj |
| 1000007 | hnpbYAQtKW |
| 1000008 | XI1a2RAy0G |
| 1000009 | ZmnI8XobRb |
| 1000010 | N6XXZnw3mo |
+---------+------------+
11 rows in set (1.01 sec)
mysql [d1]> show profile;
+----------------------------+----------+
| Status | Duration |
+----------------------------+----------+
| starting | 0.000550 |
| checking permissions | 0.000024 |
| Opening tables | 0.000390 |
| init | 0.000034 |
| System lock | 0.000066 |
| optimizing | 0.000010 |
| optimizing | 0.000007 |
| statistics | 0.000054 |
| preparing | 0.000033 |
| Creating tmp table | 0.000058 |
| statistics | 0.000023 |
| preparing | 0.000015 |
| executing | 0.000032 |
| Sending data | 0.000015 |
| executing | 0.000004 |
| Sending data | 0.000008 |
| Creating sort index | 0.973423 |
| end | 0.000040 |
| query end | 0.000014 |
| waiting for handler commit | 0.000030 |
| query end | 0.000017 |
| removing tmp table | 0.028293 |
| query end | 0.004465 |
| removing tmp table | 0.000023 |
| query end | 0.000015 |
| closing tables | 0.000041 |
| freeing items | 0.000094 |
| cleaning up | 0.000094 |
+----------------------------+----------+
28 rows in set, 1 warning (0.00 sec)
Warning (Code 1287): 'SHOW PROFILE' is deprecated and will be removed in a future release. Please use Performance Schema instead
mysql [d1]> SELECT id, value FROM t1 ORDER BY id LIMIT 11 OFFSET 999999;
+---------+------------+
| id | value |
+---------+------------+
| 1000000 | 5UGdDjX6sI |
| 1000001 | 1BmxMSJMNO |
| 1000002 | fRA4G96KoZ |
| 1000003 | 0PEhFBERn3 |
| 1000004 | x9N5k3eez5 |
| 1000005 | KeIyt1RHX5 |
| 1000006 | BaDGNWrvPj |
| 1000007 | hnpbYAQtKW |
| 1000008 | XI1a2RAy0G |
| 1000009 | ZmnI8XobRb |
+---------+------------+
10 rows in set (0.22 sec)
mysql [d1]> SELECT id, value FROM t1 ORDER BY id LIMIT 11 OFFSET 999999;
+---------+------------+
| id | value |
+---------+------------+
| 1000000 | 5UGdDjX6sI |
| 1000001 | 1BmxMSJMNO |
| 1000002 | fRA4G96KoZ |
| 1000003 | 0PEhFBERn3 |
| 1000004 | x9N5k3eez5 |
| 1000005 | KeIyt1RHX5 |
| 1000006 | BaDGNWrvPj |
| 1000007 | hnpbYAQtKW |
| 1000008 | XI1a2RAy0G |
| 1000009 | ZmnI8XobRb |
| 1000010 | N6XXZnw3mo |
+---------+------------+
11 rows in set (0.19 sec)
mysql [d1]> show profile;
+----------------------------+----------+
| Status | Duration |
+----------------------------+----------+
| starting | 0.000265 |
| checking permissions | 0.000019 |
| Opening tables | 0.000135 |
| init | 0.000022 |
| System lock | 0.000065 |
| optimizing | 0.000011 |
| statistics | 0.000046 |
| preparing | 0.000031 |
| Sorting result | 0.000009 |
| executing | 0.000008 |
| Sending data | 0.189200 |
| end | 0.000031 |
| query end | 0.000009 |
| waiting for handler commit | 0.000016 |
| query end | 0.000014 |
| closing tables | 0.000020 |
| freeing items | 0.000038 |
| cleaning up | 0.000048 |
+----------------------------+----------+
18 rows in set, 1 warning (0.00 sec)
Warning (Code 1287): 'SHOW PROFILE' is deprecated and will be removed in a future release. Please use Performance Schema instead
window関数の利用するするほうはCreating tmp tableしてCreating sort indexしてるがこのsort indexが重いらしい。 件数がすくないとたいしたことないかもしれないけど無難にlimitに書き換えてあげたほうがよいかもしれない。
(書いてから気づいたけどbetweenじゃなくて普通にid > ? and id <= ?ってすればよかった)