有没有更有效的方法来获取从 1 到 49 的数字列表,其中包含单词的列,FIZZ当数字可以被 3 整除,BUZZ当数字可以被 5 整除,FIZZBUZZ当数字可以被整除3和5?
FIZZ
BUZZ
FIZZBUZZ
我的尝试是(注意,这会清空你的过程缓存,所以不要在 PRODUCTION BOX 上运行):
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!*/;
我已经修改了我尝试运行每组语句 100 次的尝试,然后显示 SQL Server 通过sys.dm_exec_query_stats.
sys.dm_exec_query_stats
结果:
Runs total_time average time VARIANT1 100 42533 425 VARIANT2 100 138677 1386
使用 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
本机程序:
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;
测试:
SET NOCOUNT ON; PRINT SYSUTCDATETIME(); GO DECLARE @T AS dbo.FizzBuzzTableType; INSERT @T (n, FizzBuzz) EXECUTE dbo.FizzBuzz; GO 100 PRINT SYSUTCDATETIME();
典型结果:
-- 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
这会将过程输出写入内存中的表变量,否则我们只是在测试在 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());