SQL で逆ポーランド記法の電卓

まだ未完成ですけど、もうほとんど完成してるのでさらしておきますね。
完成しました!
文字列でスタックを実現しています。

WITH
  Input(id, str) AS (
    -- 1 + 3 / -8
    SELECT 1, '1 3 + -8 /'
    -- 2*3*4*5+99
    UNION ALL SELECT 2, '2 3 * 4 * 5 * 99 +'
    -- 4 * (9 - 4) / (2 * 6 - 2) + 8
    UNION ALL SELECT 3, '9 4 - 4 * 2 6 * 2 - / 8 +'
    -- 1 + ((123 * 3 - 69) / 100)
    UNION ALL SELECT 4, '1 123 3 * 69 - 100 / +'
    -- 2.45/8.5*9.27+(5*0.0023)
    UNION ALL SELECT 5, '2.45 8.5 / 9.27 * 5 0.0023 * +'
  )
, Input_(id, str) AS (
    SELECT id, str + ' ' FROM Input
  )
, Calc(id, stack, input_str) AS (
    -- スタックの初期状態
    SELECT
        id
      , CAST(' ' + RTRIM(
          LEFT(str, CHARINDEX(' ', str, 1)) -- 最初の要素をスタックに積む
        ) AS varchar(max))
      , SUBSTRING(str, CHARINDEX(' ', str, 1) + 1, LEN(str))    -- 入力から消費した要素を取り除く
    FROM
        Input_
    UNION ALL
    -- 再帰SQLを使って入力の先頭から一要素毎計算していく
    SELECT
        id
        -- 入力の先頭要素を元にスタックを更新
      , CASE
        WHEN RTRIM(
               LEFT(input_str, CHARINDEX(' ', input_str, 1))
             ) IN ('+', '-', '*', '/')
        -- 演算子ならスタックから2つの要素を取り出して、演算して、また戻す
          THEN ' '
                 + CASE LEFT(
                          stack,
                          LEN(stack)
                              -- topの要素の長さ
                            - LEN(REVERSE(LEFT(REVERSE(stack), CHARINDEX(' ', REVERSE(stack), 1))))
                              -- topの次の要素の長さ
                            - LEN(REVERSE(SUBSTRING(
                                REVERSE(stack),
                                CHARINDEX(' ', REVERSE(stack), 1) + 1,
                                CHARINDEX(
                                  ' ',
                                  REVERSE(stack),
                                  CHARINDEX(' ', REVERSE(stack), 1) + 1
                                ) - CHARINDEX(' ', REVERSE(stack), 1)
                                  - 1
                              )))
                        )
                   WHEN '' THEN ''
                           -- 2つの要素を取り出した後のスタックが空じゃないならスタックの内容と空白を一つスタックに戻す
                           ELSE RTRIM(LTRIM(LEFT(
                                  stack,
                                  LEN(stack)
                                    - LEN(REVERSE(LEFT(REVERSE(stack), CHARINDEX(' ', REVERSE(stack), 1))))
                                    - LEN(REVERSE(SUBSTRING(
                                        REVERSE(stack),
                                        CHARINDEX(' ', REVERSE(stack), 1) + 1,
                                        CHARINDEX(
                                          ' ',
                                          REVERSE(stack),
                                          CHARINDEX(' ', REVERSE(stack), 1) + 1
                                        ) - CHARINDEX(' ', REVERSE(stack), 1)
                                          - 1
                                      )))
                                ))) + ' '
                   END
                 + CAST(
                     -- topから2番目の要素とtopの要素を取り出して演算後、スタックに戻す
                     CASE RTRIM(
                            LEFT(input_str, CHARINDEX(' ', input_str, 1))
                          )
                     WHEN '+' THEN CAST(REVERSE(SUBSTRING(
                                     REVERSE(stack),
                                     CHARINDEX(' ', REVERSE(stack), 1) + 1,
                                     CHARINDEX(
                                       ' ',
                                       REVERSE(stack),
                                       CHARINDEX(' ', REVERSE(stack), 1) + 1
                                     ) - CHARINDEX(' ', REVERSE(stack), 1) - 1
                                   )) AS real)
                                     + CAST(REVERSE(LEFT(REVERSE(stack), CHARINDEX(' ', REVERSE(stack), 1))) AS real)
                     WHEN '-' THEN CAST(REVERSE(SUBSTRING(
                                     REVERSE(stack),
                                     CHARINDEX(' ', REVERSE(stack), 1) + 1,
                                     CHARINDEX(
                                       ' ',
                                       REVERSE(stack),
                                       CHARINDEX(' ', REVERSE(stack), 1) + 1
                                     ) - CHARINDEX(' ', REVERSE(stack), 1) - 1
                                   )) AS real)
                                     - CAST(REVERSE(LEFT(REVERSE(stack), CHARINDEX(' ', REVERSE(stack), 1))) AS real)
                     WHEN '*' THEN CAST(REVERSE(SUBSTRING(
                                     REVERSE(stack),
                                     CHARINDEX(' ', REVERSE(stack), 1) + 1,
                                     CHARINDEX(
                                       ' ',
                                       REVERSE(stack),
                                       CHARINDEX(' ', REVERSE(stack), 1) + 1
                                     ) - CHARINDEX(' ', REVERSE(stack), 1) - 1
                                   )) AS real)
                                     * CAST(REVERSE(LEFT(REVERSE(stack), CHARINDEX(' ', REVERSE(stack), 1))) AS real)
                     WHEN '/' THEN CAST(REVERSE(SUBSTRING(
                                     REVERSE(stack),
                                     CHARINDEX(' ', REVERSE(stack), 1) + 1,
                                     CHARINDEX(
                                       ' ',
                                       REVERSE(stack),
                                       CHARINDEX(' ', REVERSE(stack), 1) + 1
                                     ) - CHARINDEX(' ', REVERSE(stack), 1) - 1
                                   )) AS real)
                                     / CAST(REVERSE(LEFT(REVERSE(stack), CHARINDEX(' ', REVERSE(stack), 1))) AS real)
                     END
                   AS varchar(max))
          ELSE stack
                 + ' '
                 + RTRIM(
                     SUBSTRING(input_str, 1, CHARINDEX(' ', input_str, 1))
                   )
        END
      , SUBSTRING(input_str, CHARINDEX(' ', input_str, 1) + 1, LEN(input_str))
    FROM
        Calc
    WHERE
        input_str <> ''
  )
, Result(id, val) AS (
    SELECT
        id
      , stack
    FROM
        Calc
    WHERE
        input_str = ''
  )
SELECT
    id
  , val
FROM
    Result
ORDER BY
    id

実行結果

id val
1 -0.5
2 219
3 10
4 4
5 2.68344


最後の SELECT を、

SELECT * FROM Calc ORDER BY id, LEN(input_str) DESC

に置き換えると計算の途中結果を確認することができる。

id stack input_str
1 1 3 + -8 /
1 1 3 + -8 /
1 4 -8 /
1 4 -8 /
1 -0.5  
2 2 3 * 4 * 5 * 99 +
2 2 3 * 4 * 5 * 99 +
2 6 4 * 5 * 99 +
2 6 4 * 5 * 99 +
2 24 5 * 99 +
2 24 5 * 99 +
2 120 99 +
2 120 99 +
2 219  
3 9 4 - 4 * 2 6 * 2 - / 8 +
3 9 4 - 4 * 2 6 * 2 - / 8 +
3 5 4 * 2 6 * 2 - / 8 +
3 5 4 * 2 6 * 2 - / 8 +
3 20 2 6 * 2 - / 8 +
3 20 2 6 * 2 - / 8 +
3 20 2 6 * 2 - / 8 +
3 20 12 2 - / 8 +
3 20 12 2 - / 8 +
3 20 10 / 8 +
3 2 8 +
3 2 8 +
3 10  
4 1 123 3 * 69 - 100 / +
4 1 123 3 * 69 - 100 / +
4 1 123 3 * 69 - 100 / +
4 1 369 69 - 100 / +
4 1 369 69 - 100 / +
4 1 300 100 / +
4 1 300 100 / +
4 1 3 +
4 4  
5 2.45 8.5 / 9.27 * 5 0.0023 * +
5 2.45 8.5 / 9.27 * 5 0.0023 * +
5 0.288235 9.27 * 5 0.0023 * +
5 0.288235 9.27 * 5 0.0023 * +
5 2.67194 5 0.0023 * +
5 2.67194 5 0.0023 * +
5 2.67194 5 0.0023 * +
5 2.67194 0.0115 +
5 2.68344  

こんな入力はどうでしょ?

-- 1 + (2 + (3 + (4 + (5 + (6 + (7 + (8 + (9 + 10))))))))
SELECT 1, '1 2 3 4 5 6 7 8 9 10 + + + + + + + + +'

途中結果を確認すると、

id stack input_str
1 1 2 3 4 5 6 7 8 9 10 + + + + + + + + +
1 1 2 3 4 5 6 7 8 9 10 + + + + + + + + +
1 1 2 3 4 5 6 7 8 9 10 + + + + + + + + +
1 1 2 3 4 5 6 7 8 9 10 + + + + + + + + +
1 1 2 3 4 5 6 7 8 9 10 + + + + + + + + +
1 1 2 3 4 5 6 7 8 9 10 + + + + + + + + +
1 1 2 3 4 5 6 7 8 9 10 + + + + + + + + +
1 1 2 3 4 5 6 7 8 9 10 + + + + + + + + +
1 1 2 3 4 5 6 7 8 9 10 + + + + + + + + +
1 1 2 3 4 5 6 7 8 9 10 + + + + + + + + +
1 1 2 3 4 5 6 7 8 19 + + + + + + + +
1 1 2 3 4 5 6 7 27 + + + + + + +
1 1 2 3 4 5 6 34 + + + + + +
1 1 2 3 4 5 40 + + + + +
1 1 2 3 4 45 + + + +
1 1 2 3 49 + + +
1 1 2 52 + +
1 1 54 +
1 55  

楽しい!


追記:
最初の実装以降に身につけたテクニックを使って書き直した。
短く読みやすくなってる・・・はず!

WITH
  Input(id, str) AS (
    -- 1 + 3 / -8
    SELECT 1, '1 3 + -8 /'
    -- 2*3*4*5+99
    UNION ALL SELECT 2, '2 3 * 4 * 5 * 99 +'
    -- 4 * (9 - 4) / (2 * 6 - 2) + 8
    UNION ALL SELECT 3, '9 4 - 4 * 2 6 * 2 - / 8 +'
    -- 1 + ((123 * 3 - 69) / 100)
    UNION ALL SELECT 4, '1 123 3 * 69 - 100 / +'
    -- 2.45/8.5*9.27+(5*0.0023)
    UNION ALL SELECT 5, '2.45 8.5 / 9.27 * 5 0.0023 * +'
  )
, Input_(id, str) AS (
    SELECT id, str + ' ' FROM Input
  )
, Calc(id, stack, top1, top2, input_str) AS (
    -- スタックの初期状態
    SELECT
        id
      , CAST(
          LEFT(str, CHARINDEX(' ', str))    -- 最初の要素をスタックに積む
        AS varchar(max))
      , CAST(LEFT(str, CHARINDEX(' ', str) - 1) AS real)    -- スタックに積んだものと同じもの
      , CAST(NULL AS real)                                  -- 最初は使わないのでなんでもいい
      , SUBSTRING(str, CHARINDEX(' ', str) + 1, LEN(str))   -- 入力から消費した要素を取り除く
    FROM
        Input_
    UNION ALL
    -- 再帰SQLを使って入力の先頭から一要素毎計算していく
    SELECT
        id
        -- 入力の先頭要素を元にスタックを更新
      , CASE
        WHEN LEFT(input_str, 2) IN ('+', '-', '*', '/')
          THEN CAST(
                 -- 演算結果に
                 CASE LEFT(input_str, 1)
                 WHEN '+' THEN top2 + top1
                 WHEN '-' THEN top2 - top1
                 WHEN '*' THEN top2 * top1
                 WHEN '/' THEN top2 / top1
                 END
               AS varchar(max))
                   -- スタックから先頭要素を2つ取り除いたものを連結したものが新しいスタック
                 + CASE LEN(stack)
                          - LEN(CAST(top1 AS varchar(max)))
                          - LEN(CAST(top2 AS varchar(max))) - 1
                   WHEN 0 THEN ' '
                          ELSE SUBSTRING(
                                 stack,
                                 LEN(CAST(top1 AS varchar(max)))
                                   + LEN(CAST(top2 AS varchar(max))) + 2,
                                 len(stack)
                               )
                   END
          ELSE LEFT(input_str, CHARINDEX(' ', input_str)) + stack
        END
        -- 次のtop1は入力が演算子なら演算結果、そうでなければ入力の先頭
      , CAST(CASE LEFT(input_str, CHARINDEX(' ', input_str))
             WHEN '+' THEN top2 + top1
             WHEN '-' THEN top2 - top1
             WHEN '*' THEN top2 * top1
             WHEN '/' THEN top2 / top1
                      ELSE LEFT(input_str, CHARINDEX(' ', input_str))
             END AS real)
        -- 次のtop2は入力が演算子ならスタックの3番目の要素、そうでなければtop1
      , CAST(CASE
             WHEN LEFT(input_str, CHARINDEX(' ', input_str)) IN ('+', '-', '*', '/')
                    AND CHARINDEX(' ', stack, CHARINDEX(' ', stack, CHARINDEX(' ', stack) + 1) + 1) <> 0
               THEN SUBSTRING(
                      stack,
                      LEN(CAST(top1 AS varchar(max))) + LEN(CAST(top2 AS varchar(max))) + 2,
                      CHARINDEX(' ', stack, CHARINDEX(' ', stack, CHARINDEX(' ', stack) + 1) + 1)
                        - CHARINDEX(' ', stack, CHARINDEX(' ', stack) + 1)
                    )
               ELSE top1
             END AS real)
      , SUBSTRING(input_str, CHARINDEX(' ', input_str) + 1, LEN(input_str))
    FROM
        Calc
    WHERE
        input_str <> ''
  )
, Result(id, val) AS (
    SELECT
        id
      , stack
    FROM
        Calc
    WHERE
        input_str = ''
  )
SELECT
    id
  , val
FROM
    Result
ORDER BY
    id

途中結果も載せときますね。

id stack top1 top2 input_str
1 1 1 NULL 3 + -8 /
1 3 1 3 1 + -8 /
1 4 4 3 -8 /
1 -8 4 -8 4 /
1 -0.5 -0.5 -8  
2 2 2 NULL 3 * 4 * 5 * 99 +
2 3 2 3 2 * 4 * 5 * 99 +
2 6 6 3 4 * 5 * 99 +
2 4 6 4 6 * 5 * 99 +
2 24 24 4 5 * 99 +
2 5 24 5 24 * 99 +
2 120 120 5 99 +
2 99 120 99 120 +
2 219 219 99  
3 9 9 NULL 4 - 4 * 2 6 * 2 - / 8 +
3 4 9 4 9 - 4 * 2 6 * 2 - / 8 +
3 5 5 4 4 * 2 6 * 2 - / 8 +
3 4 5 4 5 * 2 6 * 2 - / 8 +
3 20 20 4 2 6 * 2 - / 8 +
3 2 20 2 20 6 * 2 - / 8 +
3 6 2 20 6 2 * 2 - / 8 +
3 12 20 12 20 2 - / 8 +
3 2 12 20 2 12 - / 8 +
3 10 20 10 20 / 8 +
3 2 2 10 8 +
3 8 2 8 2 +
3 10 10 8  
4 1 1 NULL 123 3 * 69 - 100 / +
4 123 1 123 1 3 * 69 - 100 / +
4 3 123 1 3 123 * 69 - 100 / +
4 369 1 369 1 69 - 100 / +
4 69 369 1 69 369 - 100 / +
4 300 1 300 1 100 / +
4 100 300 1 100 300 / +
4 3 1 3 1 +
4 4 4 3  
5 2.45 2.45 NULL 8.5 / 9.27 * 5 0.0023 * +
5 8.5 2.45 8.5 2.45 / 9.27 * 5 0.0023 * +
5 0.288235 0.2882353 8.5 9.27 * 5 0.0023 * +
5 9.27 0.288235 9.27 0.2882353 * 5 0.0023 * +
5 2.67194 2.671942 9.27 5 0.0023 * +
5 5 2.67194 5 2.671942 0.0023 * +
5 0.0023 5 2.67194 0.0023 5 * +
5 0.0115 2.67194 0.0115 2.67194 +
5 2.68344 2.68344 0.0115  

Brainf*ck in SQL

Brainf*ck はチューリング完全らしいですよ。
Brainf*ck 自体に興味のある方は、Brainf*ckBrainfuck - Wikipedia へどうぞ。

WITH
  -- 入力
  Input(id, bf_program, stdin) AS (
    -- Hello, World!と標準出力に出力するプログラム
    SELECT 0, '
>+++++++++[<++++++++>-]<.>+++++++[<++++>-]<+.+++++++..+++.[-]>++++++++[<++
++>-]<.>+++++++++++[<+++++>-]<.>++++++++[<+++>-]<.+++.------.--------.[-]>
++++++++[<++++>-]<+.[-]++++++++++.', ''

    -- hogeと標準出力に出力するプログラム
    UNION ALL SELECT 1, '
++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++
++++++++++++++++++++++++++++++++
++++++++.
+++++++.
--------.
--.', ''

    -- hogeと標準出力に出力するプログラム(ループ版)
    UNION ALL SELECT 2, '
++++++++++[>++++++++++<-]>
++++.+++++++.--------.--.', ''

    -- echo・・・だけど無限ループなのでコメントアウト
    --UNION ALL SELECT 3, '+[>,.<]', 'hoge'
    
    -- 1 + 2を計算し、標準出力に出力するプログラム
    UNION ALL SELECT 4, '
+>++><<
[->>>+<<<]
>>>[-<+<<+>>>]<<
[->>+<<]
>>[-<+<+>>]<
++++++++++++++++++++++++++++++++++++++++++++++++.', ''

    -- 4 * 2を計算し、標準出力に出力するプログラム
    UNION ALL SELECT 5, '
++++>++><<
[-
  >[->>+<<]
  >>[-<+<+>>]
  <<<
]>>
++++++++++++++++++++++++++++++++++++++++++++++++.', ''

    -- 条件分岐のサンプル
    UNION ALL SELECT 6, '
+++++     # {0} = ?
>+++<     # {1} = ?
>[                    # while( {1} ) do
  <[->>+>+<<<]>       #   {2:3} = !{0}
  >>[-<<<+>>>]<<      #   {0}   = !{3}
  >[[-]<<->>]<        #   if({2}) then decr {0}
  -                   #   decr {1}
]<                    # end
++++++++++++++++++++++++++++++++++++++++++++++++.', ''
  )
, BracketTableSrc(id, open_bracket_pos, close_bracket_pos, stack, src, crnt) AS (
    -- 括弧の対応表の元データ
    -- スタックに開き括弧のインデックスをpushし、
    -- 閉じカッコを見つけ次第そのインデックスとスタックに積まれているインデックスを取り出して、
    -- open_bracket_posとclose_bracket_posに格納する。
    -- 後戻りが発生しないので、文字列消費型(srcがどんどん減っていく)で構成。
    SELECT
        id
      , 0
      , 0
      , CAST('' AS varchar(max))
      , bf_program
      , 1
    FROM
        Input
    UNION ALL
    SELECT
        id
        -- スタックのtopにあるインデックスを設定
      , CASE LEFT(src, 1)
        WHEN ']' THEN CAST(LEFT(stack, CHARINDEX(' ', stack, 1)) AS int)
                 ELSE 0
        END
        -- 閉じカッコのインデックスを設定
      , CASE LEFT(src, 1)
        WHEN ']' THEN crnt
                 ELSE 0
        END
        -- 開き括弧ならスタックにインデックスをpushし、
        -- 閉じカッコならスタックからpopする
      , CASE LEFT(src, 1)
        WHEN '[' THEN CAST(crnt AS varchar(4)) + ' ' + stack
        WHEN ']' THEN SUBSTRING(stack, CHARINDEX(' ', stack, 1) + 1, LEN(stack))
                 ELSE stack
        END
        -- 消費した文字列を取り除いたものが次のsrcになる
      , SUBSTRING(src, 2, LEN(src))
      , crnt + 1
    FROM
        BracketTableSrc
    WHERE
        src <> ''
  )
, BracketTable(id, open_bracket_pos, close_bracket_pos) AS (
    -- open_bracket_posとclose_bracket_posがどちらも格納されているデータのみ抜き出す
    -- このテーブルをひくことで対応括弧のインデックスが取得できる
    SELECT
        id
      , open_bracket_pos
      , close_bracket_pos
    FROM
        BracketTableSrc
    WHERE
        open_bracket_pos <> 0
          AND close_bracket_pos <> 0
  )
, Eval(id, array, ptr, input_pgm, crnt, stdout, stdin) AS (
    -- Brainf*ckの評価エンジン
    -- ループが存在するので、文字列消費型ではなく、
    -- オリジナル文字列とインデックスによる方法(input_pgmは変わらず、crntが変わる)を使用している。
    SELECT
        id
        -- Brainf*ckが使用する配列は5000に制限
        -- SQL Serverに限らず、一行のサイズが制限されていることは多いので一応。
        -- 初期状態は全て(数値の)0
      , CAST(REPLICATE(CHAR(0), 5000) AS char(5000))
      , 1   -- ポインタの初期状態はSQLの文字列のインデックスが1からなので1
      , bf_program
      , 1
      , CAST('' AS nvarchar(max))   -- 標準出力は最初空
      , stdin                       -- 標準出力はInputのものをそのまま使う
    FROM
        Input
    UNION ALL
    SELECT
        id
      , CAST(
          -- 現在見ている文字が+、-、,のいずれかなら配列を操作する
          CASE SUBSTRING(input_pgm, crnt, 1)
          WHEN '+' THEN STUFF(array, ptr, 1, CHAR(ASCII(SUBSTRING(array, ptr, 1)) + 1))
          WHEN '-' THEN STUFF(array, ptr, 1, CHAR(ASCII(SUBSTRING(array, ptr, 1)) - 1))
          -- 標準入力にも一応対応。標準入力を一番最初に与えておかなければいけないのはSQLの限界ですね
          WHEN ',' THEN STUFF(array, ptr, 1, LEFT(stdin, 1))
                   ELSE array
          END
        AS char(5000))
        -- 現在見ている文字が>か<ならポインタを操作する
      , CASE SUBSTRING(input_pgm, crnt, 1)
        WHEN '>' THEN ptr + 1
        WHEN '<' THEN ptr - 1
                 ELSE ptr
        END
      , input_pgm
        -- 現在見ている文字が[か]ならインデックスを操作する
      , CASE SUBSTRING(input_pgm, crnt, 1)
        WHEN '[' THEN CASE ASCII(SUBSTRING(array, ptr, 1))
                      WHEN 0 THEN (SELECT close_bracket_pos FROM BracketTable
                                   WHERE Eval.id = BracketTable.id AND open_bracket_pos = crnt) + 1
                             ELSE crnt + 1
                      END
        WHEN ']' THEN (SELECT open_bracket_pos FROM BracketTable
                       WHERE Eval.id = BracketTable.id AND close_bracket_pos = crnt)
                 ELSE crnt + 1
        END
        -- 現在見ている文字が.なら標準出力に追記する
      , CASE SUBSTRING(input_pgm, crnt, 1)
        WHEN '.' THEN stdout + SUBSTRING(array, ptr, 1)
                 ELSE stdout
        END
        -- 現在見ている文字が,なら標準入力から消費する
      , CASE SUBSTRING(input_pgm, crnt, 1)
        WHEN ',' THEN SUBSTRING(stdin, 2, LEN(stdin))
                 ELSE stdin
        END
    FROM
        Eval
    WHERE
        crnt <= LEN(input_pgm)
  )
, Result(id, stdout) AS (
    SELECT
        id
      , stdout
    FROM
        Eval P
    WHERE
        crnt = (SELECT MAX(crnt) FROM Eval C WHERE P.id = C.id)
  )
SELECT * FROM Result ORDER BY id
OPTION (MAXRECURSION 32767)

実装には以下の書籍を参考にしました。超感謝!
カッコのネストのロジックはそのままです。

Rubyで作る奇妙なプログラミング言語 ~Esoteric Language~

Rubyで作る奇妙なプログラミング言語 ~Esoteric Language~


Brainf*ck のプログラムは、Brainf*ck をそのまま使わせていただきました。


他に特筆すべきは、スタックの構造を「文字列の先頭が top になるように」変更したところですかね。
気付いてしまえば大したものじゃないんですけど、文字列の末尾を top にしていた逆ポーランド記法の電卓では、REVERSE 関数使いまくりで訳分からんコードになっていました。
それがこちらでは超すっきり記述できています。

実行結果

id stdout
0 Hello World!
1 hoge
2 hoge
4 3
5 8
6 2

何が言いたい?

えー、Brainf*ck はチューリング完全なので、Brainf*ck が実装できる SQL、少なくとも SQL Server もまたチューリング完全であることが証明されました!
誰だ! SQLチューリング完全じゃないなんて言ってるのは!!