是否有更有效的方法来获得从 1 到 49 的数字列表,其中一列包含单词,FIZZ当数字可以被 3 整除BUZZ时,当数字可以被 5 整除FIZZBUZZ时,当数字可以被均分时由两个3和5?
我的尝试是(注意,这会清空您的过程缓存,所以不要在生产箱上运行):
DBCC FREEPROCCACHE
GO
/*VARIANT1*/
;WITH t AS (
SELECT RowNum = ROW_NUMBER() OVER (ORDER BY o.object_id)
FROM sys.objects o
)
SELECT t.RowNum
, CASE WHEN ((t.RowNum % 3) + (t.RowNum % 5)) = 0 THEN 'FIZZBUZZ'
ELSE
CASE WHEN t.RowNum % 3 = 0 THEN 'FIZZ'
ELSE
CASE WHEN t.RowNum % 5 = 0 THEN 'BUZZ'
ELSE ''
END
END
END
FROM t
WHERE t.RowNum < 50;
GO 100
/*VARIANT2*/
DECLARE @t TABLE
(
Num INT NOT NULL PRIMARY KEY CLUSTERED
);
INSERT INTO @t (Num)
SELECT ROW_NUMBER() OVER (ORDER BY o.object_id)
FROM sys.objects o;
SELECT t.Num
, CASE WHEN ((t.Num % 3) + (t.Num % 5)) = 0 THEN 'FIZZBUZZ'
ELSE
CASE WHEN t.Num % 3 = 0 THEN 'FIZZ'
ELSE
CASE WHEN t.Num % 5 = 0 THEN 'BUZZ'
ELSE ''
END
END
END
FROM @t t
WHERE t.Num < 50;
GO 100
SELECT CASE WHEN dest.text LIKE '%/*VARIANT1*/%' THEN 'VARIANT1' ELSE 'VARIANT2' END
, MAX(deqs.execution_count)
, SUM(deqs.total_worker_time)
, AvgWorkerTime = SUM(deqs.total_worker_time) / MAX(deqs.execution_count)
FROM sys.dm_exec_query_stats deqs
CROSS APPLY sys.dm_exec_sql_text(deqs.sql_handle) dest
WHERE (dest.text LIKE '%/*VARIANT1*/%'
OR dest.text LIKE '%/*VARIANT2*/%')
AND dest.text NOT LIKE '%/*NOT_ME!*/%'
GROUP BY CASE WHEN dest.text LIKE '%/*VARIANT1*/%' THEN 'VARIANT1' ELSE 'VARIANT2' END
ORDER BY CASE WHEN dest.text LIKE '%/*VARIANT1*/%' THEN 'VARIANT1' ELSE 'VARIANT2' END
/*NOT_ME!*/;
Run Code Online (Sandbox Code Playgroud)
我修改了每组语句运行 100 次的尝试,然后显示 SQL Server 通过sys.dm_exec_query_stats.
结果:
Runs total_time average time
VARIANT1 100 42533 425
VARIANT2 100 138677 1386
Run Code Online (Sandbox Code Playgroud)
Pau*_*ite 15
使用 SQL Server 2014 内存优化表和本机编译过程:
-- Setup
CREATE DATABASE InMem;
GO
ALTER DATABASE InMem
ADD FILEGROUP FG1
CONTAINS MEMORY_OPTIMIZED_DATA;
GO
ALTER DATABASE InMem
ADD FILE
(
NAME = 'FN1',
-- Change to suit your system
FILENAME = 'C:\Program Files\Microsoft SQL Server\MSSQL12.SQL2014\MSSQL\DATA\FN1.mod'
)
TO FILEGROUP FG1;
GO
USE InMem;
GO
CREATE TYPE dbo.FizzBuzzTableType AS TABLE
(
n integer NOT NULL INDEX i,
FizzBuzz varchar(8) NOT NULL
) WITH (MEMORY_OPTIMIZED = ON);
GO
Run Code Online (Sandbox Code Playgroud)
本地程序:
CREATE PROCEDURE dbo.FizzBuzz
WITH
NATIVE_COMPILATION,
SCHEMABINDING,
EXECUTE AS OWNER
AS
BEGIN ATOMIC
WITH
(
TRANSACTION ISOLATION LEVEL = SNAPSHOT,
LANGUAGE = N'english'
)
DECLARE @n AS dbo.FizzBuzzTableType;
DECLARE @i integer = 1;
WHILE @i < 50
BEGIN
IF @i % 15 = 0
BEGIN
INSERT @n (n, FizzBuzz)
VALUES (@i, 'FizzBuzz')
END
ELSE
BEGIN
IF @i % 3 = 0
BEGIN
INSERT @n (n, FizzBuzz)
VALUES (@i, 'Fizz')
END
ELSE
BEGIN
IF @i % 5 = 0
BEGIN
INSERT @n (n, FizzBuzz)
VALUES (@i, 'Buzz')
END
ELSE
BEGIN
INSERT @n (n, FizzBuzz)
VALUES (@i, CONVERT(varchar(8), @i));
END;
END;
END;
SET @i += 1;
END;
SELECT
N.n,
N.FizzBuzz
FROM @n AS N
ORDER BY
N.n;
END;
Run Code Online (Sandbox Code Playgroud)
测试:
SET NOCOUNT ON;
PRINT SYSUTCDATETIME();
GO
DECLARE @T AS dbo.FizzBuzzTableType;
INSERT @T (n, FizzBuzz)
EXECUTE dbo.FizzBuzz;
GO 100
PRINT SYSUTCDATETIME();
Run Code Online (Sandbox Code Playgroud)
典型结果:
-- 95ms for 100 iterations, < 1ms each
2014-12-31 10:07:13.7993355
Beginning execution loop
Batch execution completed 100 times.
2014-12-31 10:07:13.8943409
Run Code Online (Sandbox Code Playgroud)
这会将过程输出写入内存表变量,否则我们只是测试在 SSMS 中显示结果的速度。
上述本机程序在 1,000,000 个号码上运行大约需要12 秒。在 T-SQL 中有各种更快的方法来做同样的事情。我之前写过的一篇如下。当实现预期的并行计划时,它在我的笔记本电脑上运行大约500 毫秒,以百万行运行:
IF OBJECT_ID(N'tempdb..#Result', N'U') IS NOT NULL
DROP TABLE #Result;
IF OBJECT_ID(N'tempdb..#Thousand', N'U') IS NOT NULL
DROP TABLE #Thousand;
SET NOCOUNT ON;
DECLARE @start datetime2(7) = SYSUTCDATETIME();
CREATE TABLE #Thousand
(
n integer NOT NULL,
CONSTRAINT PK_#Thousand
PRIMARY KEY CLUSTERED (n)
);
-- Add 1,000 rows numbered 0-999 to #Thousand
WITH
L1 (n) AS
(
SELECT V.n
FROM
(
VALUES (0), (1), (2), (3), (4),
(5), (6), (7), (8), (9)
) AS V (n)
),
Thousand AS
(
SELECT n =
CONVERT
(
integer,
ROW_NUMBER() OVER (
ORDER BY (SELECT NULL))
- 1
)
FROM L1
CROSS JOIN L1 AS L2
CROSS JOIN L1 AS L3
)
INSERT #Thousand (n)
SELECT n
FROM Thousand;
-- To hold the Fizz Buzz output
CREATE TABLE #Result
(
n integer NOT NULL,
result varchar(8) NOT NULL
);
INSERT #Result
SELECT
Million.n,
Million.result
FROM
(
-- Modulo operation to encourage few outer rows parallelism
SELECT n
FROM #Thousand
WHERE n % 1 = 0
) AS T1
-- Outer Apply to keep the Compute Scalar parallel
OUTER APPLY
(
SELECT
F2.n,
F2.result
FROM #Thousand AS T2
CROSS APPLY
(
-- Row numbers 1 to 1,000,000
SELECT (T1.n * 1000) + T2.n + 1
) AS F1 (n)
CROSS APPLY
(
-- The Fizz Buzz bit
SELECT
F1.n,
result =
CASE
WHEN F1.n % 15 = 0 THEN 'FizzBuzz'
WHEN F1.n % 3 = 0 THEN 'Buzz'
WHEN F1.n % 5 = 0 THEN 'Fizz'
ELSE CONVERT(varchar(8), F1.n)
END
) AS F2
) AS Million
OPTION (MAXDOP 4, QUERYTRACEON 9481);
PRINT DATEDIFF(MILLISECOND, @start, SYSUTCDATETIME());
Run Code Online (Sandbox Code Playgroud)
JNK*_*JNK 11
这个在我的机器上与你的第一个(0ms)运行相同。我不确定它是否会更快地扩展。
;WITH t AS (
SELECT RowNum = ROW_NUMBER() OVER (ORDER BY o.object_id)
FROM sys.objects o
)
SELECT t.RowNum
, Cxa.Fizz + CxB.Buzz
FROM t
CROSS APPLY (SELECT CASE WHEN t.RowNum % 3 = 0 THEN 'FIZZ' ELSE '' END) CxA(Fizz)
CROSS APPLY (SELECT CASE WHEN t.RowNum % 5 = 0 THEN 'BUZZ' ELSE '' END) CxB(Buzz)
WHERE t.RowNum < 50;
Run Code Online (Sandbox Code Playgroud)
Rob*_*vis 10
我想出的最佳版本在我的机器上运行 30 毫秒:
WITH t AS (
SELECT 1 As RowNum
Union ALL
Select RowNum + 1
From t
Where RowNum < 49
)
SELECT t.RowNum
, SubString('FIZZ', (t.RowNum % 3)*10, 5) + SubString('BUZZ', (t.RowNum % 5)*10, 5)
FROM t;
Run Code Online (Sandbox Code Playgroud)
小智 6
根据 sqlfiddle.com 这需要 7 毫秒:
select coalesce(fizz + buzz, fizz, buzz, cast(n as varchar)) as FizzBuzz
from (
select n0 + 3 * n3 + 9 * n9 + 27 * n27 + 81 * n81 as n
from
(select 0 as n0 union all select 1 union all select 2 as n0) as n0,
(select 0 as n3 union all select 1 union all select 2 as n3) as n3,
(select 0 as n9 union all select 1 union all select 2 as n9) as n9,
(select 0 as n27 union all select 1 union all select 2 as n27) as n27,
(select 0 as n81 union all select 1 as n81) as n81
) as stupidalias1
left outer join
(select 3 as fizzstep, 'Fizz' as fizz) as stupidalias2 on n % fizzstep = 0
left outer join
(select 5 as buzzstep, 'Buzz' as buzz) as stupidalias3 on n % buzzstep = 0
where n between 1 and 100
order by n;
Run Code Online (Sandbox Code Playgroud)
不使用任何表、存储过程或 CTE。
我得到了一个合理版本的本地编译存储过程,可以在大约 500-800 毫秒内处理 100 万行。这是T-SQL转换我做的按位algorithim从这里从亚当Machanic对位运算博客有点帮助在这里。
我(希望)遵循与@PaulWhite 的 500ms/100 万行 proc 相同的规则,即生成结果但不显示它们/不将它们作为计时的一部分传递。它必须是内存表上的哈希索引,以实现速度和 4,194,304 或 8,388,608 的存储桶大小,这对我来说似乎是最佳选择,尽管显然这会产生很高的空存储桶计数。
USE hekatondb
GO
--NB: SQLCMD script, Enable via: Query menu > SQLCMD Mode
:setvar bucketCount 4194304
--:setvar bucketCount 8388608
IF OBJECT_ID('dbo.usp_hekatonFizzBuzz') IS NOT NULL
DROP PROC dbo.usp_hekatonFizzBuzz
GO
IF OBJECT_ID('dbo.FizzBuzz') IS NOT NULL
DROP TABLE dbo.FizzBuzz
GO
IF OBJECT_ID('dbo.FizzBuzz') IS NULL
CREATE TABLE dbo.FizzBuzz (
Number INT NOT NULL,
Result VARCHAR(8) NULL,
CONSTRAINT PK_FizzBuzz PRIMARY KEY NONCLUSTERED HASH ( Number ) WITH ( BUCKET_COUNT = $(bucketCount) )
) WITH ( MEMORY_OPTIMIZED = ON, DURABILITY = SCHEMA_ONLY )
GO
CREATE PROC dbo.usp_hekatonFizzBuzz
@limit INT
WITH
NATIVE_COMPILATION,
SCHEMABINDING,
EXECUTE AS OWNER
AS
BEGIN ATOMIC
WITH
(
TRANSACTION ISOLATION LEVEL = SNAPSHOT,
LANGUAGE = N'english'
)
DECLARE @acc INT = 810092048 -- 11 00 00 01 00 10 01 00 00 01 10 00 01 00 00
DECLARE @i INT = 1
DECLARE @c INT
WHILE @i <= @limit
BEGIN
SELECT
@c = @acc & 3,
@acc = ( @acc / 4 ) | ( @c * 268435456 )
INSERT dbo.FizzBuzz ( Number, Result )
SELECT @i, SUBSTRING( ' Fizz Buzz FizzBuzz', @c * 8, @c * 4 )
SET @i += 1
END
END
GO
DELETE dbo.FizzBuzz
DECLARE @startDate DATETIME2 = SYSUTCDATETIME();
EXEC dbo.usp_hekatonFizzBuzz 1000000
SELECT DATEDIFF( millisecond, @startDate, SYSUTCDATETIME() ) diff
GO 10
Run Code Online (Sandbox Code Playgroud)
我发现并使用了这个没有 CTE 的单子选择。查询统计信息中的 max_elapsed_time 显示 1036
SELECT num,
CASE WHEN mod3 + mod5 = 0 THEN 'FizzBuzz'
WHEN mod5 = 0 THEN 'Buzz'
WHEN mod3 = 0 THEN 'Fizz'
ELSE CONVERT(VARCHAR(8), num)
END
FROM
(
SELECT number as num,
number % 3 AS mod3,
number % 5 AS mod5
FROM master.dbo.spt_values
WHERE name IS NULL
AND number BETWEEN 1 AND 101
) AS numbers;
Run Code Online (Sandbox Code Playgroud)
下面是一个 T-SQL 解决方案,它将前一百万个数字写入临时表。在我的机器上大约需要 84 毫秒。关键瓶颈是等待NESTING_TRANSACTION_FULL锁存器 和CXPACKET,这两个瓶颈除了更改之外我不知道如何解决MAXDOP。我想要一个可以利用并行嵌套循环和基于需求的并行性的查询计划,这是我设法得到的:
代码有点长。简而言之,我将两个 246 行和 271 行的派生表连接在一起,总共 66666 行。这些行连接到一个 15 行派生表,该表利用了每 15 行重复一次 FIZZBUZZ 模式这一事实。最后十行添加了UNION ALL.
DROP TABLE IF EXISTS #t;
SELECT res.fizzbuzz INTO #t
FROM
(
VALUES
(0),
(15),
(30),
(45),
(60),
(75),
(90),
(105),
(120),
(135),
(150),
(165),
(180),
(195),
(210),
(225),
(240),
(255),
(270),
(285),
(300),
(315),
(330),
(345),
(360),
(375),
(390),
(405),
(420),
(435),
(450),
(465),
(480),
(495),
(510),
(525),
(540),
(555),
(570),
(585),
(600),
(615),
(630),
(645),
(660),
(675),
(690),
(705),
(720),
(735),
(750),
(765),
(780),
(795),
(810),
(825),
(840),
(855),
(870),
(885),
(900),
(915),
(930),
(945),
(960),
(975),
(990),
(1005),
(1020),
(1035),
(1050),
(1065),
(1080),
(1095),
(1110),
(1125),
(1140),
(1155),
(1170),
(1185),
(1200),
(1215),
(1230),
(1245),
(1260),
(1275),
(1290),
(1305),
(1320),
(1335),
(1350),
(1365),
(1380),
(1395),
(1410),
(1425),
(1440),
(1455),
(1470),
(1485),
(1500),
(1515),
(1530),
(1545),
(1560),
(1575),
(1590),
(1605),
(1620),
(1635),
(1650),
(1665),
(1680),
(1695),
(1710),
(1725),
(1740),
(1755),
(1770),
(1785),
(1800),
(1815),
(1830),
(1845),
(1860),
(1875),
(1890),
(1905),
(1920),
(1935),
(1950),
(1965),
(1980),
(1995),
(2010),
(2025),
(2040),
(2055),
(2070),
(2085),
(2100),
(2115),
(2130),
(2145),
(2160),
(2175),
(2190),
(2205),
(2220),
(2235),
(2250),
(2265),
(2280),
(2295),
(2310),
(2325),
(2340),
(2355),
(2370),
(2385),
(2400),
(2415),
(2430),
(2445),
(2460),
(2475),
(2490),
(2505),
(2520),
(2535),
(2550),
(2565),
(2580),
(2595),
(2610),
(2625),
(2640),
(2655),
(2670),
(2685),
(2700),
(2715),
(2730),
(2745),
(2760),
(2775),
(2790),
(2805),
(2820),
(2835),
(2850),
(2865),
(2880),
(2895),
(2910),
(2925),
(2940),
(2955),
(2970),
(2985),
(3000),
(3015),
(3030),
(3045),
(3060),
(3075),
(3090),
(3105),
(3120),
(3135),
(3150),
(3165),
(3180),
(3195),
(3210),
(3225),
(3240),
(3255),
(3270),
(3285),
(3300),
(3315),
(3330),
(3345),
(3360),
(3375),
(3390),
(3405),
(3420),
(3435),
(3450),
(3465),
(3480),
(3495),
(3510),
(3525),
(3540),
(3555),
(3570),
(3585),
(3600),
(3615),
(3630),
(3645),
(3660),
(3675)
) v246 (n)
CROSS JOIN
(
VALUES
(0),
(15),
(30),
(45),
(60),
(75),
(90),
(105),
(120),
(135),
(150),
(165),
(180),
(195),
(210),
(225),
(240),
(255),
(270),
(285),
(300),
(315),
(330),
(345),
(360),
(375),
(390),
(405),
(420),
(435),
(450),
(465),
(480),
(495),
(510),
(525),
(540),
(555),
(570),
(585),
(600),
(615),
(630),
(645),
(660),
(675),
(690),
(705),
(720),
(735),
(750),
(765),
(780),
(795),
(810),
(825),
(840),
(855),
(870),
(885),
(900),
(915),
(930),
(945),
(960),
(975),
(990),
(1005),
(1020),
(1035),
(1050),
(1065),
(1080),
(1095),
(1110),
(1125),
(1140),
(1155),
(1170),
(1185),
(1200),
(1215),
(1230),
(1245),
(1260),
(1275),
(1290),
(1305),
(1320),
(1335),
(1350),
(1365),
(1380),
(1395),
(1410),
(1425),
(1440),
(1455),
(1470),
(1485),
(1500),
(1515),
(1530),
(1545),
(1560),
(1575),
(1590),
(1605),
(1620),
(1635),
(1650),
(1665),
(1680),
(1695),
(1710),
(1725),
(1740),
(1755),
(1770),
(1785),
(1800),
(1815),
(1830),
(1845),
(1860),
(1875),
(1890),
(1905),
(1920),
(1935),
(1950),
(1965),
(1980),
(1995),
(2010),
(2025),
(2040),
(2055),
(2070),
(2085),
(2100),
(2115),
(2130),
(2145),
(2160),
(2175),
(2190),
(2205),
(2220),
(2235),
(2250),
(2265),
(2280),
(2295),
(2310),
(2325),
(2340),
(2355),
(2370),
(2385),
(2400),
(2415),
(2430),
(2445),
(2460),
(2475),
(2490),
(2505),
(2520),
(2535),
(2550),
(2565),
(2580),
(2595),
(2610),
(2625),
(2640),
(2655),
(2670),
(2685),
(2700),
(2715),
(2730),
(2745),
(2760),
(2775),
(2790),
(2805),
(2820),
(2835),
(2850),
(2865),
(2880),
(2895),
(2910),
(2925),
(2940),
(2955),
(2970),
(2985),
(3000),
(3015),
(3030),
(3045),
(3060),
(3075),
(3090),
(3105),
(3120),
(3135),
(3150),
(3165),
(3180),
(3195),
(3210),
(3225),
(3240),
(3255),
(3270),
(3285),
(3300),
(3315),
(3330),
(3345),
(3360),
(3375),
(3390),
(3405),
(3420),
(3435),
(3450),
(3465),
(3480),
(3495),
(3510),
(3525),
(3540),
(3555),
(3570),
(3585),
(3600),
(3615),
(3630),
(3645),
(3660),
(3675),
(3690),
(3705),
(3720),
(3735),
(3750),
(3765),
(3780),
(3795),
(3810),
(3825),
(3840),
(3855),
(3870),
(3885),
(3900),
(3915),
(3930),
(3945),
(3960),
(3975),
(3990),
(4005),
(4020),
(4035),
(4050)
) v271 (n)
CROSS APPLY
(
VALUES
(CAST(v246.n * 271 + v271.n + 1 AS CHAR(8))),
(CAST(v246.n * 271 + v271.n + 2 AS CHAR(8))),
(CAST('FIZZ' AS CHAR(8))),
(CAST(v246.n * 271 + v271.n + 4 AS CHAR(8))),
(CAST('BUZZ' AS CHAR(8))),
(CAST('FIZZ' AS CHAR(8))),
(CAST(v246.n * 271 + v271.n + 7 AS CHAR(8))),
(CAST(v246.n * 271 + v271.n + 8 AS CHAR(8))),
(CAST('FIZZ' AS CHAR(8))),
(CAST('BUZZ' AS CHAR(8))),
(CAST(v246.n * 271 + v271.n + 11 AS CHAR(8))),
(CAST('FIZZ' AS CHAR(8))),
(CAST(v246.n * 271 + v271.n + 13 AS CHAR(8))),
(CAST(v246.n * 271 + v271.n + 14 AS CHAR(8))),
(CAST('FIZZBUZZ' AS CHAR(8)))
) res (fizzbuzz)
UNION ALL
SELECT v.fizzbuzz
FROM (
VALUES
('999991'),
('999992'),
('FIZZ'),
('999994'),
('BUZZ'),
('FIZZ'),
('999997'),
('999998'),
('FIZZ'),
('BUZZ')
) v (fizzbuzz)
OPTION (MAXDOP 6, NO_PERFORMANCE_SPOOL);
Run Code Online (Sandbox Code Playgroud)