20210419のMySQLに関する記事は5件です。

初心者でもDockerでWordPressの環境構築ができた!

はじめに この記事はDockerを軽く勉強しただけの駆け出しエンジニアが、 Docker、WordPressの勉強のために環境構築をする過程・反省を記した備忘録です。 よって、詳しい説明や深い考察などはなく、ただこの経験を忘れたくないなと思い記録に残したものです。 業界でいうと未経験者なので、さほど難しい言葉もないと思います。同じく初学者である方々への励みにもなればなと思います。(Docker用語?は登場します。) ちなみにローカルで勉強用にWordPressを使うだけなら環境構築は Local by Flywheel と検索するとやり方が出てくると思いますが、ソフトウェアをインストールして軽く設定をしてもらうだけですぐにできます。 あくまで私はDockerの勉強も兼ねてやってみようと思った次第です。 経緯 新たにWordPressを学び始めるにあたって、Dockerも絶賛勉強中だったため せっかくならと得た知識をアウトプットする意味でも環境構築をDockerでやってみようと考えました。 ちなみに、何をもとにDockerを学んでいるかというと、 Udemyの「ゼロからはじめる Dockerによるアプリケーション実行環境構築」というコースです。これを現在40%ほど終わらせたところです。(決してステマなどではありません。) 一応リンクも貼っておきます。(ステマではありません。) まずは自力で頑張ってみる(失敗例) まずは作業用のディレクトリを作る(名前はなんでもよい) $ mkdir WordPress/WordPress-lecture 次に、nginxでサーバーを立ち上げるレッスンで使った、バインドマウントを設定してWordPressイメージのコンテナを立ち上げるコードを実行しようとした(macの場合のコード) WordPress-lecture $ docker run --name wordpress-lecture:ver1 \ -v /Users/(PCのユーザー名)/WordPress/WordPress-lecture:/usr/share/wordpress/html:ro -d -p 8081:80 wordpress Dockerコンテナのタグの指定はここではできないためエラー。:ver1の部分を消して再度実行 するとエラーは起きずコンテナは立ち上がったが肝心のサーバーがないため、もちろんブラウザで localhost:8081 にアクセスしてもページは表示されず。。。 コンテナにnginxのイメージを追加すればいいのか?それともnginxのサーバーを立ち上げてそこにWordPressのイメージを追加したほうがいいのか?Apacheじゃなきゃダメなのか?と考えて参考になるものがないかネットを漁ってみたが、学んだことをもう一度調べ直すのにもここまでそれなりの時間を要していたため、早くWordPressの勉強に取り掛かりたかったので結局最後は先輩方の知恵をお借りすることに。。。 完成されたコードを使いWordPressの環境を整える Qiitaで探すと何人かの方々が既に試していらっしゃったのでそのコードを拝借することに 参考にさせていただいた記事 ① https://qiita.com/qpSHiNqp/items/48815ed90bd474064708 ② https://qiita.com/t20190127/items/3abb7c17acdc8543c8ad ③ https://qiita.com/engineer_ikuzou/items/07ff7bd7c9eabf0b3d6b ・①の記事のdocker-compose.ymlの内容を丸パクリさせてもらい、自身のWordPress-lectureディレクトリ内にdocker-compose.ymlファイルを作成 ・自身の現在のディレクトリをコンテナにバインドマウントするための記述 -.:/app をvolumes欄に追加 ・ポート番号8080は別のレッスンで使用していたので8081に変更 最終的なファイルの内容がこちら docker-compose.yml version: '3.1' services: wordpress: image: wordpress restart: always ports: - 8081:80 environment: WORDPRESS_DB_HOST: db WORDPRESS_DB_USER: exampleuser WORDPRESS_DB_PASSWORD: examplepass WORDPRESS_DB_NAME: exampledb volumes: - .:/app - wordpress:/var/www/html - ./wp-content/themes:/var/www/html/wp-content/themes - ./wp-content/plugins:/var/www/html/wp-content/plugins db: image: mysql:5.7 restart: always environment: MYSQL_DATABASE: exampledb MYSQL_USER: exampleuser MYSQL_PASSWORD: examplepass MYSQL_RANDOM_ROOT_PASSWORD: '1' volumes: - db:/var/lib/mysql volumes: wordpress: db: 今まで丸パクリしたコードでも度々エラーが起きていた経験から、無事に成功するかドキドキしながらも、 docker-compose.ymlファイルを置いているWordPress-lectureディレクトリからコマンドを実行 WordPress-lecture $ docker-compose up -d (-dオプションはバックグラウンドで処理を実行させるためのもの) doneの文字が表示され無事に成功 localhost:8081にアクセスするとWordPressのデフォルト画面が表示された! ここに来るまでにかなりの時間がかかり苦労したため、無事に成功して感動。 エンジニアにとって苦労することが大事と言うけど、本当にその通りで苦労して手に入れた技術とか知識は身体に染み付くと思うし、うまくいった時に感動があるのでますます勉強が好きになるし意欲も高まっていきますね。 ※もし間違い等あれば指摘・訂正していただければと思います。 ということで以上です 終
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MySQLをローカル上にインストールできない。

MySQLをローカル上にインストールできない 初めにMySQLをインストール brew instal mysql 以下の様なエラーが発生 curl: (22) The requested URL returned error: 404 Error: Failed to download resource "openssl@1.1" Download failed: https://ghcr.io/v2/homebrew/core/openssl%401.1-1.1.1k.big_sur.bottle.tar.gz 調べるとアップデートで解決するかもしれないと言うことで ターミナルでHomebrewを先ずアップデート % brew update Already up-to-date. 再度インストール % mysql --version mysql Ver 8.0.23 for osx10.16 on x86_64 (Homebrew) 成功 ! 何かお気づきのことがありましたらアドバイスいただきたいです!よろしくお願いいたします!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

MySQL またserver startできなくなった件

mysql.server start  → ERROR! The server quit without updating PID file ↑上記記事で解決したはずのMySQLがまたstartできなくなった。 とりあえず、同じことを確認してみた。 PATHは問題なさそうだった…。 また再発したので、心折れそうになりましたが今回は結構早く解決したので記録します。 解決 /private/tmpの所有者をrootから自分に変えた 参考記事  https://github.com/Homebrew/homebrew-core/issues/64304 https://teratail.com/questions/204129 sudo chown -R '自分の名前' /private/tmp/ で変更。 無事、server start できました。 経緯  再インストールすると Warning: The post-install step did not complete successfully You can try again using: brew postinstall mysql@5.7 brew postinstall mysql@5.7しても変わらず…。 mysqlに拘らずpostinstall関係でググっていたら https://github.com/Homebrew/homebrew-core/issues/64304 を発見。 brew postinstall --debug ほげほげをすると内容が見れることを知り brew postinstall --debug mysql@5.7を実行 ==> Permission denied @ dir_s_mkdir - /private/tmp/homebrew なにやらパーミッションデニッドしている!! パーミッションを調べてみると drwxr-xr-x 12 root wheel tmp 所有者がrootなので sudo chown -R '自分の名前' /private/tmp/ を実行 drwxr-xr-x 12 自分の名前 wheel tmp そして mysql.server start 実行 Starting MySQL . SUCCESS! よかった。 同じエラー内容でも深掘りすると違う原因ですね。 明日になったらまた違うエラーでだめになってそうで怖いけど 頑張ります。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Mysqlで50音検索(あ行からわ行まで)

コピペ用です。 $query = "SELECT * FROM example WHERE name REGEXP '^(あ|い|う|え|お|ア|イ|ウ|エ|オ)+';"; $query = "SELECT * FROM example WHERE name REGEXP '^(か|き|く|け|こ|カ|キ|ク|ケ|コ)+';"; $query = "SELECT * FROM example WHERE name REGEXP '^(さ|し|す|せ|そ|サ|シ|ス|セ|ソ)+';"; $query = "SELECT * FROM example WHERE name REGEXP '^(た|ち|つ|て|と|タ|チ|ツ|テ|ト)+';"; $query = "SELECT * FROM example WHERE name REGEXP '^(な|に|ぬ|ね|の|ナ|二|ヌ|ネ|ノ)+';"; $query = "SELECT * FROM example WHERE name REGEXP '^(は|ひ|ふ|へ|ほ|ハ|ヒ|フ|ヘ|ホ)+';"; $query = "SELECT * FROM example WHERE name REGEXP '^(ま|み|む|め|も|マ|ミ|ム|メ|モ)+';"; $query = "SELECT * FROM example WHERE name REGEXP '^(や|ゆ|よ|ヤ|ユ|ヨ)+';"; $query = "SELECT * FROM example WHERE name REGEXP '^(ら|り|る|れ|ろ|ラ|リ|ル|レ|ロ)+';"; $query = "SELECT * FROM example WHERE name REGEXP '^(わ|を|ん|ワ|ヲ|ン)+';";
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

単一SQLクエリで数独の数値表をランダムに作る

はじめに SQLクエリで数独/ナンプレの数値表(マトリックス)をランダムに作ります。ここでいう数値表は、すべてのセルで数値の埋まった答えの表です。こういうやつですね。 SQLはOracleを使用して話をすすめていきますが、最後にMySQL版も記載しています。 クエリの考察 再帰クエリを使えば一見簡単そうに思えますが、SQLの再帰にはいろいろ制約や制限が多く一筋縄ではいきません。一番大きな制約は分岐した枝から他の枝や再帰全体に影響を与えることができないってとこです。つまり一度初期値で再帰を始めたら最後までやり遂げなければならないわけです。一説によると数独のパターンは6x10^21以上ってことなので、普通に再帰したら計算終わらないですよね。 従って考えられる戦略としては以下の3つ。 方法 内容 単純ループ型 1セルに配置可能な数値をひとつづつをランダムに割り当てて進み、進行不能となったらすべてをすててやり直す 初期値再帰型 ランダムに作成した適当な初期値を与えて空白セルを減らしたのち残りを再帰探索する。 ハイブリッド型 再帰分岐をせず分岐候補を保持しながら進み、進行不可となったらロールバックして分岐可能な位置に戻る。 それぞれの一長一短ありそうなので各方法でクエリを作りパフォーマンスを考察してみたいと思います。 単純ループ型 再帰クエリを使用しますが、再帰分岐はしません。分岐候補から一つだけランダムに選び進行し、進行不可となったらこれまでの結果を捨てて最初からやりなおします。最後まで到達したら終了です。 再帰分岐しない単純ループの利点は、進行中のある時点でクエリを終了させると再帰クエリ全体が終了することですね。まぁ一本道なので。ここでは81個のセルがすべて埋まった時点で再帰を終了させています。 また、LATERALの部分で数独のルールをチェックしています。ここで行、列、ボックスを確認してすでに配置されている数値を除外するわけですが、候補がない場合もループを続けなければならないため候補にNULLを混入させています。これにより行き詰まった場合にNULLが取得され、再帰のSELECTリスト部分において進行する場合と結果を捨ててやり直す場合の分岐ができます。 Oracle版 (12c以降) WITH singles(n, s) AS ( SELECT LEVEL, TO_CHAR(LEVEL) FROM DUAL CONNECT BY LEVEL <= 9 ), recur (cnt, rpt, lvl, diag) AS ( SELECT 0, 1, 0, RPAD('-', 81, '-') FROM DUAL UNION ALL SELECT cnt + 1, DECODE(num, NULL, rpt + 1, rpt), DECODE(num, NULL, 0, lvl + 1), DECODE(num, NULL, RPAD('-', 81, '-'), REGEXP_REPLACE(diag, '-', num, 1, 1)) FROM recur r, LATERAL( SELECT num FROM (SELECT num, ROW_NUMBER() OVER (ORDER BY v desc) rn FROM (SELECT singles.s num, DBMS_RANDOM.VALUE + 1 v FROM singles WHERE INSTR( -- row SUBSTR(diag, TRUNC(lvl/9)*9 + 1, 9) || -- col SUBSTR(diag, MOD(lvl, 9) + 1, 1) || SUBSTR(diag, MOD(lvl, 9) + 10, 1) || SUBSTR(diag, MOD(lvl, 9) + 19, 1) || SUBSTR(diag, MOD(lvl, 9) + 28, 1) || SUBSTR(diag, MOD(lvl, 9) + 37, 1) || SUBSTR(diag, MOD(lvl, 9) + 46, 1) || SUBSTR(diag, MOD(lvl, 9) + 55, 1) || SUBSTR(diag, MOD(lvl, 9) + 64, 1) || SUBSTR(diag, MOD(lvl, 9) + 73, 1) || -- box SUBSTR(diag, TRUNC(lvl/27)*27 + MOD(TRUNC(lvl/3),3)*3 + 1, 3) || SUBSTR(diag, TRUNC(lvl/27)*27 + MOD(TRUNC(lvl/3),3)*3 + 10, 3) || SUBSTR(diag, TRUNC(lvl/27)*27 + MOD(TRUNC(lvl/3),3)*3 + 19, 3), s) = 0 UNION ALL SELECT NULL, 0 FROM DUAL) -- rollback ) WHERE rn = 1 ) WHERE lvl < 81 and rpt > 0 ) SELECT s sudoku FROM ( SELECT n, REGEXP_REPLACE(SUBSTR(diag, 9 * (n - 1) + 1, 9), '(\d{3})(\d{3})(\d{3})', '\1|\2|\3') s FROM (SELECT * FROM recur WHERE lvl = 81), singles UNION ALL SELECT 3.5, '---+---+---' FROM DUAL UNION ALL SELECT 6.5, '---+---+---' FROM DUAL) ORDER BY n; SUDOKU ----------- 854|793|612 629|518|734 137|642|598 ---+---+--- 215|967|483 946|385|271 783|421|965 ---+---+--- 492|836|157 371|259|846 568|174|329 Elapsed: 00:00:02.60 クエリのパフォーマンスですが、各ターンで開始して最後まで到達できるかは完全に運に左右されます。つまりやり直しの回数次第となります。運が良ければ0.5秒、運が悪ければ15秒と大きな幅があります。まぁあまりいい方法ではないようですね。 初期値再帰型 次に再帰分岐を試してみます。前述の通りすべてを分岐させることは不可能です。60該パターンとか計算がおわりません。なので、初期配置としてランダムに選択した固定数値を先においておきます。初期配置の数値が多いほど再帰する回数が減ってパフォーマンスが向上しますが、多くしすぎると数独ルールに適合しない答えのない初期配置を作ってしまう可能性が高まります。要はどのくらいでバランスをとるかですね。 まずは、斜めに3ブロックの初期配置を作成します(下図1,5,9)。なぜこの3ブロックかというと、これらは互いに他のブロックセルの影響を受けないので1~9の数値を完全にランダムに配置できるからです。3ブロックだけだとまだまだ再帰探索に時間がかかってしまうので、さらに反対側対角線の2ブロック(下図3と7)を追加します。しかしこの2ブロックを確定してしまうと答えのない初期配置になる可能性が非常に高まります。 解決方法として、右上のブロックだけを確定して開始するか、右上と左下のブロックの組み合わせをいくつか作ってそれをすべて再帰探索するかです。後者のほうが計算量を調整して幾分早く結果を取得できるため、ここでは後者のブロックの組み合わせを採用します。 Oracle版 (11g以降) WITH singles(n, s) AS ( SELECT LEVEL, TO_CHAR(LEVEL) FROM DUAL CONNECT BY LEVEL <= 9 ), first_blks(b1, b5, b9) AS ( SELECT LISTAGG(s) WITHIN GROUP (ORDER BY (DBMS_RANDOM.VALUE * 1)), LISTAGG(s) WITHIN GROUP (ORDER BY (DBMS_RANDOM.VALUE * 2)), LISTAGG(s) WITHIN GROUP (ORDER BY (DBMS_RANDOM.VALUE * 4)) FROM singles ), corners(grp, cnt, srow, scol, str) as ( SELECT * FROM (SELECT 3, 0, b1, b9, '' FROM first_blks UNION SELECT 7, 0, b9, b1, '' FROM first_blks) UNION ALL SELECT grp, cnt + 1, srow, scol, str || singles.s FROM corners, singles WHERE cnt < 9 AND NVL(INSTR(str, singles.s), 0) = 0 AND INSTR(SUBSTR(srow, TRUNC(cnt/3)*3 + 1, 3), singles.s) = 0 AND INSTR(SUBSTR(scol, MOD(cnt, 3) + 1, 1), singles.s) = 0 AND INSTR(SUBSTR(scol, MOD(cnt, 3) + 4, 1), singles.s) = 0 AND INSTR(SUBSTR(scol, MOD(cnt, 3) + 7, 1), singles.s) = 0 ), targets (s) AS ( SELECT SUBSTR(b1, 1, 3) || '---' || SUBSTR(b3, 1, 3) || SUBSTR(b1, 4, 3) || '---' || SUBSTR(b3, 4, 3) || SUBSTR(b1, 7, 3) || '---' || SUBSTR(b3, 7, 3) || '---' || substr(b5, 1, 3) || '---' || '---' || substr(b5, 4, 3) || '---' || '---' || substr(b5, 7, 3) || '---' || SUBSTR(b7, 1, 3) || '---' || SUBSTR(b9, 1, 3) || SUBSTR(b7, 4, 3) || '---' || SUBSTR(b9, 4, 3) || SUBSTR(b7, 7, 3) || '---' || SUBSTR(b9, 7, 3) s FROM first_blks, (SELECT str b3 FROM ( SELECT str, ROW_NUMBER() OVER (ORDER BY DBMS_RANDOM.VALUE) rn FROM corners WHERE cnt = 9 and grp = 3) WHERE rn <= 5), (SELECT str b7 FROM ( SELECT str, ROW_NUMBER() OVER (ORDER BY DBMS_RANDOM.VALUE) rn FROM corners WHERE cnt = 9 and grp = 7) WHERE rn <= 5) ), recur (lvl, diag) AS ( SELECT 0, s FROM targets UNION ALL SELECT lvl + 1, REGEXP_REPLACE(diag, '.', singles.s, lvl + 1, 1) FROM recur, singles WHERE lvl < 81 AND (SUBSTR(diag, lvl + 1, 1) = singles.s OR (SUBSTR(diag, lvl + 1, 1) = '-' AND INSTR(-- row SUBSTR(diag, TRUNC(lvl/9)*9 + 1, 9) || -- col SUBSTR(diag, MOD(lvl, 9) + 1, 1) || SUBSTR(diag, MOD(lvl, 9) + 10, 1) || SUBSTR(diag, MOD(lvl, 9) + 19, 1) || SUBSTR(diag, MOD(lvl, 9) + 28, 1) || SUBSTR(diag, MOD(lvl, 9) + 37, 1) || SUBSTR(diag, MOD(lvl, 9) + 46, 1) || SUBSTR(diag, MOD(lvl, 9) + 55, 1) || SUBSTR(diag, MOD(lvl, 9) + 64, 1) || SUBSTR(diag, MOD(lvl, 9) + 73, 1) || -- box SUBSTR(diag, TRUNC(lvl/27)*27 + MOD(TRUNC(lvl/3),3)*3 + 1, 3) || SUBSTR(diag, TRUNC(lvl/27)*27 + MOD(TRUNC(lvl/3),3)*3 + 10, 3) || SUBSTR(diag, TRUNC(lvl/27)*27 + MOD(TRUNC(lvl/3),3)*3 + 19, 3), singles.s) = 0 )) ) SELECT s sudoku FROM ( SELECT n, REGEXP_REPLACE(substr(diag, 9*(n - 1)+1, 9), '(\d{3})(\d{3})(\d{3})', '\1|\2|\3') s FROM (SELECT * FROM recur WHERE lvl = 81 and rownum = 1), singles UNION ALL SELECT 3.5, '---+---+---' FROM DUAL UNION ALL SELECT 6.5, '---+---+---' FROM DUAL) ORDER BY n; SUDOKU ----------- 593|268|174 768|431|529 241|975|368 ---+---+--- 815|642|793 926|713|485 437|589|216 ---+---+--- 652|397|841 179|854|632 384|126|957 Elapsed: 00:00:01.46 少しクエリが長くなってしまいましたが、ほぼ一秒前後長くても二秒程度で結果が取得できるようになりました。ただし、初期値によっては結果を取得できない可能性も完全にゼロではないので注意が必要です。 ハイブリッド型 最後にハイブリッド型です。なにがハイブリッドかというと、再帰分岐はせず単純ループさせながらクエリの内容で枝分かれを実現して、最初に到達した数値表でクエリを終了する、というところですね。 繰り返しになりますが、再帰クエリには分岐の機能がありながらなぜそれを使わずに自分で分岐させるのかというと、それはもう「他に分岐があっても最初の答えを得た時点でクエリを終了させたいから」につきます。自分の知ってる限りSQLの再帰はこれができません。まぁ当然といえば当然ですけどね。 ということで、再帰クエリのパラメータとして分岐可能リストを保持し、探索が行き詰まった場合に他の候補があるセルまでロールバックして、リストから次の候補を取得して探索を再開します。以下のクエリでは、stkに分岐可能リストをカンマ区切りリストで保持しています。Cにはターゲットのセルで確定した数値が入っていますが、これがNULLである場合セルを一つ戻して候補リストから次の候補を取得します。 Oracle版 (12c以降) WITH singles(n, s) AS ( SELECT LEVEL, TO_CHAR(LEVEL) FROM DUAL CONNECT BY LEVEL <= 9 ), recur (cnt, lvl, stk, c, diag) AS ( SELECT 0, 0, CAST(',' AS VARCHAR(500)), '*', RPAD('-', 81, '-') FROM DUAL UNION ALL SELECT cnt + 1, CASE WHEN c IS NULL THEN lvl - 1 WHEN nums IS NULL THEN lvl ELSE lvl + 1 END, CASE WHEN nums IS NULL THEN SUBSTR(stk, 2) ELSE SUBSTR(nums, 2) || ',' || stk END, CASE WHEN nums IS NULL THEN NULLIF(SUBSTR(stk, 1, 1), ',') ELSE SUBSTR(nums, 1, 1) END, CASE WHEN c IS NULL THEN SUBSTR(diag, 1, (lvl - 1) - 1) || NVL(NULLIF(SUBSTR(stk, 1, 1), ','), '*') || SUBSTR(diag, (lvl - 1) + 1) WHEN nums IS NULL THEN SUBSTR(diag, 1, (lvl + 0) - 1) || NVL(NULLIF(SUBSTR(stk, 1, 1), ','), '*') || SUBSTR(diag, (lvl + 0) + 1) ELSE SUBSTR(diag, 1, (lvl + 1) - 1) || SUBSTR(nums, 1, 1) || SUBSTR(diag, (lvl + 1) + 1) END FROM recur r, LATERAL( SELECT nums FROM (-- aggregate function is not permitted, use analytic function instead SELECT LISTAGG(s) WITHIN GROUP (ORDER BY DBMS_RANDOM.VALUE) OVER () nums, ROW_NUMBER() OVER (ORDER BY NULL) rn FROM (SELECT s FROM singles UNION ALL SELECT NULL FROM DUAL) WHERE s IS NULL OR (c IS NOT NULL AND (SUBSTR(diag, lvl + 1, 1) = s OR SUBSTR(diag, lvl + 1, 1) in ('-', '*') AND INSTR( -- row SUBSTR(diag, TRUNC(lvl/9)*9 + 1, 9) || -- col SUBSTR(diag, MOD(lvl, 9) + 1, 1) || SUBSTR(diag, MOD(lvl, 9) + 10, 1) || SUBSTR(diag, MOD(lvl, 9) + 19, 1) || SUBSTR(diag, MOD(lvl, 9) + 28, 1) || SUBSTR(diag, MOD(lvl, 9) + 37, 1) || SUBSTR(diag, MOD(lvl, 9) + 46, 1) || SUBSTR(diag, MOD(lvl, 9) + 55, 1) || SUBSTR(diag, MOD(lvl, 9) + 64, 1) || SUBSTR(diag, MOD(lvl, 9) + 73, 1) || -- box SUBSTR(diag, TRUNC(lvl/27)*27 + MOD(TRUNC(lvl/3),3)*3 + 1, 3) || SUBSTR(diag, TRUNC(lvl/27)*27 + MOD(TRUNC(lvl/3),3)*3 + 10, 3) || SUBSTR(diag, TRUNC(lvl/27)*27 + MOD(TRUNC(lvl/3),3)*3 + 19, 3) , s) = 0) ) ) WHERE rn = 1 ) WHERE lvl < 81 ) CYCLE diag, stk, c SET cycle TO 1 DEFAULT 0 SELECT s sudoku FROM ( SELECT n, REGEXP_REPLACE(substr(diag, 9*(n - 1)+1, 9), '(\d{3})(\d{3})(\d{3})', '\1|\2|\3') s FROM (SELECT * FROM recur WHERE lvl = 81), singles UNION ALL SELECT 3.5, '---+---+---' FROM DUAL UNION ALL SELECT 6.5, '---+---+---' FROM DUAL) ORDER BY n; SUDOKU ----------- 567|813|249 389|724|651 124|659|783 ---+---+--- 631|245|897 798|361|425 452|987|136 ---+---+--- 273|198|564 815|436|972 946|572|318 Elapsed: 00:00:00.08 なんとなんと、結果を得るのにかかる時間は0.1秒以下です。パフォーマンスはある程度運に左右されますが、完全に無視できる範囲内です。しかも結果が取得できないリスクもありません。他の2つのクエリを完全に凌駕してしまいました。 MySQL版 (8.0.14以降) LATERALを使用しているので、8.0.14以降が対象となります。 WITH RECURSIVE singles(n, s) AS ( SELECT 1, '1' UNION ALL SELECT N + 1, CAST(n + 1 AS CHAR(1)) FROM singles WHERE n < 9 ), recur (cnt, lvl, stk, c, diag) AS ( SELECT 0, 0, CAST(',' AS CHAR(500)), '*', RPAD('-', 81, '-') UNION ALL SELECT cnt + 1, CASE WHEN c IS NULL THEN lvl - 1 WHEN nums IS NULL THEN lvl ELSE lvl + 1 END, CASE WHEN nums IS NULL THEN SUBSTR(stk, 2) ELSE CONCAT(SUBSTR(nums, 2), ',', stk) END, CASE WHEN nums IS NULL THEN NULLIF(SUBSTR(stk, 1, 1), ',') ELSE SUBSTR(nums, 1, 1) END, CASE WHEN c IS NULL THEN CONCAT(SUBSTR(diag, 1, lvl - 2), IFNULL(NULLIF(SUBSTR(stk, 1, 1), ','), '*'), SUBSTR(diag, lvl)) WHEN nums IS NULL THEN CONCAT(SUBSTR(diag, 1, lvl - 1), IFNULL(NULLIF(SUBSTR(stk, 1, 1), ','), '*'), SUBSTR(diag, lvl + 1)) ELSE CONCAT(SUBSTR(diag, 1, lvl), SUBSTR(nums, 1, 1), SUBSTR(diag, lvl + 2)) END FROM recur r, LATERAL(SELECT GROUP_CONCAT(s ORDER BY RAND() SEPARATOR '') nums FROM (SELECT s FROM singles UNION ALL SELECT NULL) i WHERE s IS NULL OR (c IS NOT NULL AND (SUBSTR(diag, lvl + 1, 1) = s OR SUBSTR(diag, lvl + 1, 1) in ('-', '*') AND INSTR(CONCAT( -- row SUBSTR(diag, TRUNCATE(lvl/9,0)*9 + 1, 9), -- col SUBSTR(diag, MOD(lvl, 9) + 1, 1), SUBSTR(diag, MOD(lvl, 9) + 10, 1), SUBSTR(diag, MOD(lvl, 9) + 19, 1), SUBSTR(diag, MOD(lvl, 9) + 28, 1), SUBSTR(diag, MOD(lvl, 9) + 37, 1), SUBSTR(diag, MOD(lvl, 9) + 46, 1), SUBSTR(diag, MOD(lvl, 9) + 55, 1), SUBSTR(diag, MOD(lvl, 9) + 64, 1), SUBSTR(diag, MOD(lvl, 9) + 73, 1), -- box SUBSTR(diag, TRUNCATE(lvl/27,0)*27 + MOD(TRUNCATE(lvl/3,0),3)*3 + 1, 3), SUBSTR(diag, TRUNCATE(lvl/27,0)*27 + MOD(TRUNCATE(lvl/3,0),3)*3 + 10, 3), SUBSTR(diag, TRUNCATE(lvl/27,0)*27 + MOD(TRUNCATE(lvl/3,0),3)*3 + 19, 3)), s) = 0) ) ) l WHERE lvl < 81 ) SELECT s sudoku FROM ( SELECT n, CONCAT(SUBSTR(diag, 9*(n - 1) + 1, 3), '|', SUBSTR(diag, 9*(n - 1) + 4, 3), '|', SUBSTR(diag, 9*(n - 1) + 7, 3)) s FROM (SELECT * FROM recur WHERE lvl = 81) r, singles UNION ALL SELECT 3.5, '---+---+---' UNION ALL SELECT 6.5, '---+---+---') t ORDER BY n; +-------------+ | sudoku | +-------------+ | 327|169|584 | | 481|753|962 | | 659|482|731 | | ---+---+--- | | 948|516|327 | | 135|827|496 | | 276|394|815 | | ---+---+--- | | 763|248|159 | | 594|631|278 | | 812|975|643 | +-------------+ 11 rows in set (0.035 sec) おわりに 久しぶりに再帰クエリに取り組んでみましたが、いい練習になりました。再帰クエリに関していえば、クエリを強制終了できる機能があるといろいろできる幅が広がるんですが、まぁSQLには必要ないんでしょうね。たぶん。 実はこのクエリ、「SQLで数独の問題を作る」という目標において、問題を作るにはまず元になる数値表が必要だよなぁ、ってことで書いたものです。そんなわけで次回は、一意に定まる数独の問題を作成するクエリについて書く予定です。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む