読者です 読者をやめる 読者になる 読者になる

私が歌川です

@utgwkk が書いている

SQL で DB 問い合わせをしないでプログラミングをして遊ぶ

今日の KMC の例会講座で、こういう発表をしたので、話したことをざっくりまとめておきます。SQLチューリング完全なので、 DB 問い合わせだけじゃなくていろいろなコードが書けるよみたいな話です。

使用環境は SQLite3 です。

SQL プログラミングの方法

文字列を出力したい

これはかなり単純で、

SELECT 'Hello, world!';

のようにしてやればよい。

文字列を結合したい

|| 演算子で文字列を結合できる。

SELECT 0 || ' is a natural number';

条件分岐をしたい

CASE WHEN を使えば実現できる。

SELECT '5 is ' ||
CASE WHEN 5 % 2 = 0
  THEN 'even'
  ELSE 'odd'
END

繰り返し処理をしたい

たとえば、C++ でいうところの

for (int i = 0; i < 100; ++i) {
  std::cout << i << std::endl;
}

のようなことをしたいときには、WITH 句を使って、次のように記述する。

WITH RECURSIVE
  cnt(x) AS (
     SELECT 0
     UNION ALL
     SELECT x+1 FROM cnt
     LIMIT 100
  )
SELECT x FROM cnt;

関数を定義したい

SQL の本来の意味での関数は(すくなくとも SQLite では)定義できないけど、ここでも WITH 句が使える。 次の例では、10 % 4 を計算して出力する。

WITH
args(n, k) AS (
  SELECT 10, 4
),

cmod(result) AS (
  SELECT (n % k)
  FROM args
)

SELECT n || ' % ' || k || ' = ' || result
FROM args, cmod;

cmod が関数(のようなもの)で、args がそれに渡される引数である。 そもそも WITH 句は、副問い合わせに別名をつけておくみたいな機能なので、 cmod(result)result は引数じゃなくてカラム名を表している。 そのため、SELECT result FROM cmod のようにして結果を取得する必要がある。

プログラムを実行したい

cat hoge.sql | sqlite3 のようなコマンドを叩けば実行できる。

また、手元に環境がない場合でも、(Wandbox)http://melpon.org/wandbox/ や (ideone)https://ideone.com/ といったオンラインの実行環境を使うことができる。

SQL プログラムの例

FizzBuzz

WITH RECURSIVE
  cnt(x) AS (
    SELECT 1
    UNION ALL SELECT x + 1
    FROM cnt
    LIMIT 100
  ),
  fizzbuzz(x) AS (
    SELECT
      CASE WHEN x % 3 = 0
      THEN 'Fizz'
      ELSE ''
      END
    ||
      CASE WHEN x % 5 = 0
      THEN 'Buzz'
      ELSE ''
      END
    ||
      CASE WHEN x % 5 != 0 AND x % 3 != 0
      THEN x
      ELSE ''
      END
    FROM cnt
  )
SELECT x FROM fizzbuzz;

自力で書いたのでもっとうまい方法があるかもしれない。行ベースなので勝手に改行してくれて便利。

その他の例

公式のドキュメント にいろいろな例があって、

などが実際に実装されている。

まとめ

SQLプログラミング言語として使えるし、実際そのように使っている例もある。 また、本来はデータベースに問い合わせるための言語であるので、データベースと連携したプログラミングが、何の外部モジュールもなくても実現できる。

欠点として、見通しが悪いとか、標準入力を受け取る方法が分からないとか、いろいろあるので、その辺りは今後の展望という感じ。やっていきましょう。