使い古されたねたではありますが、プログラミングクイズとしてもっとも有名な問題のひとつである「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