LoginSignup
6
3

More than 5 years have passed since last update.

SQLでFizzBuzzを書いた

Last updated at Posted at 2017-01-05

使い古されたねたではありますが、プログラミングクイズとしてもっとも有名な問題のひとつである「FizzBuzz」をSQLで解いてみました。動作確認自体はPostgreSQLで行いましたが、可能な限り標準SQLに準拠しています。ただし剰余は標準SQLにはないため、処理依存の関数modを利用しています。もっとも多くの処理系でmodはサポートされていると思いますが。

-- 桁テーブルを作成&初期化する。
create table digits (digit integer);
insert into digits values (1), (2), (3), (4), (5), (6), (7), (8), (9), (0);

-- 連番ビューを作成&初期化する。
create view numbers as (
    select (d1.digit * 10 + d2.digit) as x 
    from digits as d1, digits as d2
    where d1.digit <> 0 or d2.digit <> 0
);

-- FizzBuzzを出力する。
select 
    x,
    case
        when mod(x, 15) = 0 then 'FizzBuzz'
        when mod(x,  3) = 0 then 'Fizz'
        when mod(x,  5) = 0 then 'Buzz'
        else                     cast(x as char(2))
    end as result
from numbers
order by x;

/**
 x  |  result
----+----------
  1 | 1
  2 | 2
  3 | Fizz
  4 | 4
  5 | Buzz
  6 | Fizz
  7 | 7
  8 | 8
  9 | Fizz
 10 | Buzz
 11 | 11
 12 | Fizz
 13 | 13
 14 | 14
 15 | FizzBuzz
 16 | 16
 17 | 17
 18 | Fizz
 19 | 19
 20 | Buzz
 21 | Fizz
 22 | 22
 23 | 23
 24 | Fizz
 25 | Buzz
 26 | 26
 27 | Fizz
 28 | 28
 29 | 29
 30 | FizzBuzz
 31 | 31
 32 | 32
 33 | Fizz
 34 | 34
 35 | Buzz
 36 | Fizz
 37 | 37
 38 | 38
 39 | Fizz
 40 | Buzz
 41 | 41
 42 | Fizz
 43 | 43
 44 | 44
 45 | FizzBuzz
 46 | 46
 47 | 47
 48 | Fizz
 49 | 49
 50 | Buzz
 51 | Fizz
 52 | 52
 53 | 53
 54 | Fizz
 55 | Buzz
 56 | 56
 57 | Fizz
 58 | 58
 59 | 59
 60 | FizzBuzz
 61 | 61
 62 | 62
 63 | Fizz
 64 | 64
 65 | Buzz
 66 | Fizz
 67 | 67
 68 | 68
 69 | Fizz
 70 | Buzz
 71 | 71
 72 | Fizz
 73 | 73
 74 | 74
 75 | FizzBuzz
 76 | 76
 77 | 77
 78 | Fizz
 79 | 79
 80 | Buzz
 81 | Fizz
 82 | 82
 83 | 83
 84 | Fizz
 85 | Buzz
 86 | 86
 87 | Fizz
 88 | 88
 89 | 89
 90 | FizzBuzz
 91 | 91
 92 | 92
 93 | Fizz
 94 | 94
 95 | Buzz
 96 | Fizz
 97 | 97
 98 | 98
 99 | Fizz
**/

個人的にもっとも工夫した点としては1から99までの連番をシーケンスではなく直積によって求めているところでしょうか(意外に有名なテクニック!)。つまり2桁の整数を「1桁の整数を2つくみあわせたもの」として解釈しています。あるいはRubyでいえば次の例のようなことをしているのと同じです。

numbers = []
(0..9).each do |d1|
  (0..9).each do |d2|
    numbers << d1 * 10 + d2
  end
end

もしくは直積を扱うArray#productを利用すると、次のようにも書けるはずです。

numbers = []
[*0..9].product([*0..9]) do |d1, d2|
  numbers << d1 * 10 + d2
end

2017-01-07追記:

1から99までの連番を生成するところですが、よくよく考えてみると「再帰クエリ」を利用しても書けますね……。なお結果は同じになるので省略しています。

with recursive numbers (x) as (
        select 1
    union all
        select x + 1 from numbers where x < 99
) 
select 
    x,
    case
        when mod(x, 15) = 0 then 'FizzBuzz'
        when mod(x,  3) = 0 then 'Fizz'
        when mod(x,  5) = 0 then 'Buzz'
        else                     cast(x as char(2))
    end as result
from numbers
order by x;

あるいは「連番を生成する部分」と「ロジックによりFizz/Buzz/FizzBuzzを切り替える部分」を同時に処理するなら、次のように書くこともできます。なお利用してるPostgresSQLの関係上、文字列の型をtextにしています。

with recursive fizzbuzz (x, result) as (
        select 1, '1'
    union all
        select
            x + 1,
            case
                when mod(x + 1, 15) = 0 then 'FizzBuzz'
                when mod(x + 1,  3) = 0 then 'Fizz'
                when mod(x + 1,  5) = 0 then 'Buzz'
                else                     cast(x + 1 as text)
            end
        from fizzbuzz
        where x < 99
) select * from fizzbuzz 
6
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
6
3