将有多个脚本,所以请耐心等待。请注意,我在整个过程中都使用Pascal表示法。
创建原始架构
DROP SCHEMA IF EXISTS PhrancisFizzBuzz CASCADE;
CREATE SCHEMA PhrancisFizzBuzz;
-- this table will hold the list of numbers to check for FizzBuzz
CREATE TABLE PhrancisFizzBuzz.Number(
n INT NOT NULL
);
-- the following tables will hold multiplier values based on table PhrancisFizzBuzz.Number
CREATE TABLE PhrancisFizzBuzz.Fizz(
Number INT,
Word VARCHAR(4) NOT NULL DEFAULT 'Fizz'
);
CREATE TABLE PhrancisFizzBuzz.Buzz(
Number INT,
Word VARCHAR(4) NOT NULL DEFAULT 'Buzz'
);
CREATE TABLE PhrancisFizzBuzz.Woof(
Number INT,
Word VARCHAR(4) NOT NULL DEFAULT 'Woof'
);
CREATE TABLE PhrancisFizzBuzz.Ping(
Number INT,
Word VARCHAR(4) NOT NULL DEFAULT 'Ping'
);
CREATE TABLE PhrancisFizzBuzz.Plop(
Number INT,
Word VARCHAR(4) NOT NULL DEFAULT 'Plop'
);
填充FizzBuzz表
现在我将开始创建过程做工作。
CREATE OR REPLACE FUNCTION PhrancisFizzBuzz.fncPopulateFizzBuzz(
prmMaxNumber INT, -- for example 100
prmFizz INT,
prmBuzz INT,
prmWoof INT,
prmPing INT,
prmPlop INT
) RETURNS VOID
AS -- begin literal string with escape $$
$$
BEGIN
SET SEARCH_PATH = PhrancisFizzBuzz;
-- initialize and populate list of initial numbers
TRUNCATE TABLE Number;
INSERT INTO Number (n)
SELECT * FROM GENERATE_SERIES(1, prmMaxNumber);
-- populate the FizzBuzz tables with multiples
-- based on user parameter choice
TRUNCATE TABLE Fizz;
INSERT INTO Fizz
SELECT n
FROM Number
WHERE COALESCE(prmFizz,0) <> 0 AND n % prmFizz = 0;
INSERT INTO Buzz
SELECT n
FROM Number
WHERE COALESCE(prmBuzz,0) <> 0 AND n % prmBuzz = 0;
INSERT INTO Woof
SELECT n
FROM Number
WHERE COALESCE(prmWoof,0) <> 0 AND n % prmWoof = 0;
INSERT INTO Ping
SELECT n
FROM Number
WHERE COALESCE(prmPing,0) <> 0 AND n % prmPing = 0;
INSERT INTO Plop
SELECT n
FROM Number
WHERE COALESCE(prmPlop,0) <> 0 AND n % prmPlop = 0;
END;
$$ -- end literal string
LANGUAGE plpgsql;
用于测试:
SET SEARCH_PATH = PhrancisFizzBuzz;
SELECT fncPopulateFizzBuzz(100,3,5,0,0,0);
SELECT * FROM Number
LEFT JOIN Fizz ON Number.n = Fizz.Number
LEFT JOIN Buzz ON Number.n = Buzz.Number
ORDER BY Number.n;
结果样本:
Total query runtime: 17 ms.
100 rows retrieved.
到带有
JOIN
的表中的FizzBuzz 将结果集显示到每个单词一行的表中:
CREATE OR REPLACE FUNCTION PhrancisFizzBuzz.fcnTableFizzBuzz(
prmMaxNumber INT, -- for example 100
prmFizz INT,
prmBuzz INT,
prmWoof INT,
prmPing INT,
prmPlop INT
) RETURNS VOID
AS
$$
BEGIN
SET SEARCH_PATH = PhrancisFizzBuzz;
-- call populate function with input parameters
PERFORM fncPopulateFizzBuzz(
prmMaxNumber,
prmFizz,
prmBuzz,
prmWoof,
prmPing,
prmPlop
);
DROP TABLE IF EXISTS tmpFizzBuzzResults;
CREATE TEMP TABLE tmpFizzBuzzResults AS
SELECT num.n AS Number,
Fizz.Word AS Fizz,
Buzz.Word AS Buzz,
Woof.Word AS Woof,
Ping.Word AS Ping,
Plop.Word AS Plop
FROM Number AS num
LEFT JOIN Fizz ON Fizz.Number = num.n
LEFT JOIN Buzz ON Buzz.Number = num.n
LEFT JOIN Woof ON Woof.Number = num.n
LEFT JOIN Ping ON Ping.Number = num.n
LEFT JOIN Plop ON Plop.Number = num.n
;
END;
$$
LANGUAGE plpgsql;
用于测试:
SET SEARCH_PATH = PhrancisFizzBuzz;
SELECT fcnTableFizzBuzz(100,3,5,7,11,13);
SELECT * FROM tmpFizzBuzzResults ORDER BY Number ASC;
输出样本:
Total query runtime: 78 ms.
100 rows retrieved.
我确实尝试在
RETURN TABLE
中使用该功能,但无法正常工作。欢迎对此提出任何建议。 最后但并非最不重要的是,这会生成经典的FizzBuzz:
CREATE OR REPLACE FUNCTION PhrancisFizzBuzz.fcnClassicFizzBuzz(
prmMaxNumber INT,
prmFizz INT,
prmBuzz INT,
prmWoof INT,
prmPing INT,
prmPlop INT
)
RETURNS VOID
AS
$BODY$
BEGIN
-- populate tables based on user input
-- using previously created function
SET SEARCH_PATH = PhrancisFizzBuzz;
PERFORM fncPopulateFizzBuzz(
prmMaxNumber,
prmFizz,
prmBuzz,
prmWoof,
prmPing,
prmPlop
);
DROP TABLE IF EXISTS tmpFizzBuzzResults;
CREATE TEMP TABLE tmpFizzBuzzResults AS
WITH cteFizzBuzzConcat AS(
SELECT Number.n AS Number,
CONCAT(Fizz.Word, Buzz.Word, Woof.Word, Ping.Word, Plop.Word) AS Word
FROM Number
LEFT JOIN Fizz ON Number.n = Fizz.Number
LEFT JOIN Buzz ON Number.n = Buzz.Number
LEFT JOIN Woof ON Number.n = Woof.Number
LEFT JOIN Ping ON Number.n = Ping.Number
LEFT JOIN Plop ON Number.n = Plop.Number
)
SELECT Number
, (CASE
WHEN Word <> '' THEN Word
-- WHEN Word IS NOT NULL THEN Word
ELSE CAST(Number AS VARCHAR)
END) AS Value
FROM cteFizzBuzzConcat;
END;
$BODY$ LANGUAGE plpgsql;
-- For testing:
SET SEARCH_PATH = PhrancisFizzBuzz;
SELECT fcnClassicFizzBuzz(100,3,5,7,11,13);
SELECT Value FROM tmpFizzBuzzResults ORDER BY Number ASC;
执行:
Total query runtime: 53 ms.
100 rows retrieved.
输出示例:
Update
我运行了超过100,000个数字的最后一个函数,并在3.5秒
#1 楼
表的扩散数据库的架构不应设计成需要创建更多的表来容纳更多的数据。同样的原则在这里适用。您应该只有一个
Fizz
表,而不是单独的表Buzz
,Woof
,Ping
,Plop
和NoiseDefs
。经典的无参数FizzBuzz应该更简单... WITH NoiseDefs(multiple, noise) AS (
VALUES (3, 'Fizz')
, (5, 'Buzz')
), CombinedNoises AS (
SELECT n
, string_agg(noise, '' ORDER BY multiple) AS noise
FROM generate_series(1, 100) AS n
LEFT OUTER JOIN NoiseDefs
ON n % multiple = 0
GROUP BY n
)
SELECT n
, coalesce(noise, CAST(n AS TEXT)) AS noiseOrNum
FROM CombinedNoises
ORDER BY n;
请注意
VALUES
和string_agg()
的使用。 /> 在PostgreSQL中,使用
TEXT
而不是VARCHAR(n)
没什么意义-如果字符串长度超过限制,这只会引起问题。您还可能允许噪声包含四个以上的字符。他们每个人都在幕后操纵临时表,并且调用者需要了解该行为才能使用这些功能。换句话说,VARCHAR(n)
不返回任何内容是很不正确的,您必须使用TEXT
分别检索结果。 (当您说我确实尝试使用该功能在SELECT fcnClassicFizzBuzz(100, 3, 5, 7, 11, 13);
中工作,但无法使它正常工作时,我想您可能已经意识到了这个问题。) SELECT Value FROM tmpFizzBuzzResults ORDER BY Number ASC;
,RETURN TABLE
和fcnClassicFizzBuzz()
表,但是它们自身不会清除。因此,它会在那些表中留下垃圾,这是副作用。重复调用Fizz
将导致越来越不正确的输出。用户唯一的办法就是发出Buzz
-您甚至都没有提供撤消Woof
的便利功能。噪声也应由用户指定。CREATE OR REPLACE FUNCTION FlexibleFizzBuzz(
max INTEGER,
n1 INTEGER, noise1 TEXT,
n2 INTEGER, noise2 TEXT,
n3 INTEGER, noise3 TEXT,
n4 INTEGER, noise4 TEXT,
n5 INTEGER, noise5 TEXT
) RETURNS TABLE(n INTEGER, noiseOrNum TEXT) AS $BODY$
BEGIN
RETURN QUERY
WITH NoiseDefs(multiple, noise) AS (
VALUES (n1, noise1)
, (n2, noise2)
, (n3, noise3)
, (n4, noise4)
, (n5, noise5)
), CombinedNoises AS (
SELECT num
, string_agg(noise, '' ORDER BY multiple) AS noise
FROM generate_series(1, max) AS num
LEFT OUTER JOIN NoiseDefs
ON num % multiple = 0
GROUP BY num
)
SELECT num
, coalesce(noise, CAST(num AS TEXT))
FROM CombinedNoises
ORDER BY num;
END;
$BODY$ LANGUAGE plpgsql;
如果可能,请完全避免使用临时表。无需担心的清理工作和更少的I / O浪费。要缓存结果,请调用方执行:
CREATE TEMPORARY TABLE FizzBuzzResults10000 AS
SELECT *
FROM FlexibleFizzBuzz(10000, 3, 'Fizz', 5, 'Buzz', 7, 'Woof', 11, 'Ping', 13, 'Plop');
此实现的运行时间约为原始代码的一半。重载
提供便利功能会很好,因此可以省略某些参数。例如:
CREATE OR REPLACE FUNCTION FlexibleFizzBuzz(
max INTEGER,
n1 INTEGER, noise1 TEXT,
n2 INTEGER, noise2 TEXT
) RETURNS TABLE(n INTEGER, noiseOrNum TEXT) AS $BODY$
BEGIN
RETURN QUERY SELECT * FROM FlexibleFizzBuzz(
max,
n1, noise1,
n2, noise2,
NULL, NULL,
NULL, NULL,
NULL, NULL);
END;
$BODY$ LANGUAGE plpgsql;
我不相信PL / pgSQL函数可以接受可变数量的参数。
从上面的实现可以推断,我不喜欢您使用的前缀,例如
Ping
,Plop
,fcnClassicFizzBuzz()
和TRUNCATE Fizz; TRUNCATE Buzz; TRUNCATE Woof; TRUNCATE Ping; TRUNCATE Plop;
。相反,我会将fncPopulateFizzBuzz()
用于类似表的对象,例如返回行的表,视图和函数。对于标量,请使用fnc…
。我认为这是一个优势,例如您可以用视图替换临时表。 (PostgreSQL标识符实际上不区分大小写,除非您对它们进行prm…
,但是在源代码中具有该约定就足够了。)评论
\ $ \ begingroup \ $
遇到一些功能错误,但应该能够轻松修复。再次感谢
\ $ \ endgroup \ $
– ran
2014年7月25日下午6:36
\ $ \ begingroup \ $
此解决方案适用于“我的机器认证的产品”(PostgreSQL 9.0)。不过,请随时纠正发现的任何问题。
\ $ \ endgroup \ $
– 200_success
2014年7月25日下午6:48
#2 楼
我认为您的脚本可以使用更多的垂直空白;也许是最麻烦的事情,但我发现了: >BEGIN
SET SEARCH_PATH = PhrancisFizzBuzz;
-- call populate function with input parameters
PERFORM fncPopulateFizzBuzz(
prmMaxNumber,
prmFizz,
prmBuzz,
prmWoof,
prmPing,
prmPlop
);
DROP TABLE IF EXISTS tmpFizzBuzzResults;
CREATE TEMP TABLE tmpFizzBuzzResults AS
...
我也希望缩进
BEGIN...END
块。我不明白,这是一个Tab吗?
BEGIN
SET SEARCH_PATH = PhrancisFizzBuzz;
-- call populate function with input parameters
PERFORM fncPopulateFizzBuzz(
prmMaxNumber,
prmFizz,
prmBuzz,
prmWoof,
prmPing,
prmPlop
);
DROP TABLE IF EXISTS tmpFizzBuzzResults;
CREATE TEMP TABLE tmpFizzBuzzResults AS
...
在这里您需要一个空格: >在
cteFizzBuzzConcat
中;您在tmpFizzBuzzResults
中有一个类似的查询,但别名不同:来源相同。您存储了很多
VARCHAR(4)
非值,我希望将它们视为NULL
值。对于100000个数字,您将存储400000 x4个无用的空字符;您应该在将它们串联时对它们进行空提示。还有一件事-我在这里引用臭名昭著的Monkey:
唯一明智的评论是:不要使用SQL进行FizzBuzz
;)
评论
\ $ \ begingroup \ $
哦,来吧,运行100K记录需要3.5秒,这是支持SQL执行FizzBuzz的一个论点;)感谢@ Mat'sMug的评论
\ $ \ endgroup \ $
– ran
2014年7月23日下午4:28
\ $ \ begingroup \ $
我同意,但是我个人不喜欢BEGIN之后的新行,这使我想起人们在if语句中的Curly之后添加新行。
\ $ \ endgroup \ $
–马拉奇♦
2014年7月23日下午13:20
评论
大约要花时间... +1就是纯粹的努力最后,有人加紧做两个以上的单词。接下来,我们需要有一个素数规则,当N作为一个因子或数字出现时适用的规则,当一个规则不只一次应用于一个数字时重复单词,等等。至少您的设计可能具有足够的可扩展性!
注意:concat()是PostgreSQL 9.1中引入的。
@DavidK不久之后,我们将创建基础拟声词(与主流数学家使用的那可怕的基础10相对)。