1年半くらい前に書かれたらしいけれど、ふと今日 「7の倍数」を表す正規表現 - Qiita を見つけて読んだ。
(取り敢えず今の俺の中で)正規表現といえばMySQL 8.0。
そして(取り敢えず今の俺の中で)forループ的に数値をテストするといえばCTE、CTEといえばMySQL 8.0。
やってみます。
さすがに元の正規表現は長くて直接クエリーに記述してるとめげるのでストアドファンクションにラップする。
mysql80 26> CREATE FUNCTION regexp_7(n BIGINT UNSIGNED) RETURNS INT DETERMINISTIC RETURN n RLIKE '\\A(((((([07]|(6[29]*3) <snip> )))))))))+\\z';
Query OK, 0 rows affected (0.03 sec)
バックスラッシュは二重にしなければならない、くらいで意外とすんなりストアドファンクションにできた。動くかどうかはわからない。
mysql80 26> WITH RECURSIVE seq AS(
-> SELECT 1 AS n
-> UNION ALL
-> SELECT n + 1 FROM seq WHERE n < 10
-> )
-> SELECT n FROM seq;
+------+
| n |
+------+
| 1 |
| 2 |
| 3 |
| 4 |
| 5 |
| 6 |
| 7 |
| 8 |
| 9 |
| 10 |
+------+
10 rows in set (0.00 sec)
そしてまあ簡単な再起CTE。
組み合わせるとこうなる。
組み合わせるとこうなる。
mysql80 26> WITH RECURSIVE seq AS( SELECT 1 AS n UNION ALL SELECT n + 1 FROM seq WHERE n < 10 ) SELECT n, regexp_7(n) AS r FROM seq;
+------+------+
| n | r |
+------+------+
| 1 | 0 |
| 2 | 0 |
| 3 | 0 |
| 4 | 0 |
| 5 | 0 |
| 6 | 0 |
| 7 | 1 |
| 8 | 0 |
| 9 | 0 |
| 10 | 0 |
+------+------+
10 rows in set (0.12 sec)
nが正規表現にマッチした時はrが1、マッチしなければ0。
取り敢えず1~10の範囲では問題なく(MySQLの正規表現エンジンが)動いている様子。
取り敢えず1~10の範囲では問題なく(MySQLの正規表現エンジンが)動いている様子。
mysql80 26> WITH RECURSIVE seq AS(
-> SELECT 1 AS n, 0 AS r
-> UNION ALL
-> SELECT n + 1, regexp_7(n + 1) FROM seq WHERE n < 49
-> )
-> SELECT n, r FROM seq WHERE r = 1;
+------+------+
| n | r |
+------+------+
| 7 | 1 |
| 14 | 1 |
| 21 | 1 |
| 28 | 1 |
| 35 | 1 |
| 42 | 1 |
| 49 | 1 |
+------+------+
7 rows in set (0.56 sec)
いい感じに見やすくなったのでnを1~49まで増やしてる。重い。
ついでにnと前回のn(LAG(n))の差を取れば常に7になるはずだと思ってWindow関数にも手を出してみる。
ついでにnと前回のn(LAG(n))の差を取れば常に7になるはずだと思ってWindow関数にも手を出してみる。
mysql80 26> WITH RECURSIVE seq AS(
-> SELECT 1 AS n, 0 AS r
-> UNION ALL
-> SELECT n + 1, regexp_7(n + 1) FROM seq WHERE n < 49
-> )
-> SELECT n, r, n - LAG(n) OVER (ORDER BY n) AS diff FROM seq WHERE r = 1;
+------+------+------+
| n | r | diff |
+------+------+------+
| 7 | 1 | NULL |
| 14 | 1 | 7 |
| 21 | 1 | 7 |
| 28 | 1 | 7 |
| 35 | 1 | 7 |
| 42 | 1 | 7 |
| 49 | 1 | 7 |
+------+------+------+
7 rows in set (0.62 sec)
じゃあここまでをもう一段CTEに閉じ込めて、diff <> 7のものを探してみる。
mysql80 26> WITH RECURSIVE seq AS( SELECT 1 AS n, 0 AS r UNION ALL SELECT n + 1, regexp_7(n + 1) FROM seq WHERE n < 1000 ),
-> ret AS( SELECT n, r, n - LAG(n) OVER (ORDER BY n) AS diff FROM seq WHERE r = 1)
-> SELECT * FROM ret WHERE diff <> 7;
Empty set (12.60 sec)
1~1000で12秒なら、1万件で2分くらいかしらん、と思ったら↓に当たった。
mysql80 26> SET cte_max_recursion_depth = 100000;
Query OK, 0 rows affected (0.00 sec)
mysql80 26> WITH RECURSIVE seq AS( SELECT 1 AS n, 0 AS r UNION ALL SELECT n + 1, regexp_7(n + 1) FROM seq WHERE n < 10000 ),
-> ret AS( SELECT n, r, n - LAG(n) OVER (ORDER BY n) AS diff FROM seq WHERE r = 1)
-> SELECT * FROM ret WHERE diff <> 7;
Empty set (2 min 19.87 sec)
うん、楽しい、と、思う。
0 件のコメント :
コメントを投稿