Existe um método mais eficiente de obter uma lista de números de 1 a 49 com uma coluna contendo as palavras FIZZ
quando o número pode ser dividido igualmente por 3, BUZZ
quando o número pode ser dividido igualmente por 5 e FIZZBUZZ
quando o número pode ser dividido igualmente por 3 e 5?
Minhas tentativas são (CUIDADO, isso irá esvaziar seu cache de procedimento, então NÃO EXECUTAR EM UMA CAIXA DE PRODUÇÃO):
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!*/;
Modifiquei minhas tentativas de executar cada conjunto de instruções 100 vezes cada e, em seguida, mostrar os tempos registrados pelo SQL Server por meio de sys.dm_exec_query_stats
.
Os resultados:
Runs total_time average time
VARIANT1 100 42533 425
VARIANT2 100 138677 1386
Usando uma tabela com otimização de memória do SQL Server 2014 e um procedimento compilado nativamente:
Procedimento nativo:
Teste:
Resultados típicos:
Isso grava a saída do procedimento em uma variável de tabela na memória, porque, caso contrário, estamos apenas testando a velocidade de exibição dos resultados no SSMS.
Um milhão de linhas
O procedimento nativo acima leva cerca de 12 segundos para ser executado em 1.000.000 de números. Existem todos os tipos de maneiras mais rápidas de fazer a mesma coisa no T-SQL. Um que escrevi antes segue. Ele é executado em cerca de 500 ms no meu laptop em um milhão de linhas quando o plano paralelo pretendido é alcançado:
Este funciona da mesma forma na minha máquina que o seu primeiro (0ms). Não tenho certeza se escalaria mais rápido ou não.
A melhor versão que criei é executada em 30 ms na minha máquina:
De acordo com sqlfiddle.com, isso leva 7 ms:
Não usa tabelas, procedimentos armazenados ou CTEs.
Eu tenho uma versão razoável do procedimento armazenado compilado nativamente trabalhando para 1 milhão de linhas em ~ 500-800ms. Esta é uma conversão T-SQL que fiz do algoritmo bit a bit daqui com uma pequena ajuda do blog de Adam Machanic sobre operações bit a bit aqui .
Estou (espero) seguindo as mesmas regras do procedimento de 500ms / 1 milhão de linhas de @PaulWhite, ou seja, gerando os resultados, mas não os exibindo / não os passando como parte do tempo. Deve haver índices de hash nas tabelas na memória para velocidade e tamanhos de balde de 4.194.304 ou 8.388.608 pareceram ser o ponto ideal para mim, embora obviamente isso forneça altas contagens de baldes vazios.
Eu encontrei e joguei com este único sub select sem CTE. max_elapsed_time nas estatísticas de consulta mostra 1036
Não tomo crédito pelo código como está escrito, só queria ver quanto tempo levaria
UM BILHÃO DE LINHAS!
A resposta é: cerca de 10 minutos.
Below is a T-SQL solution that writes the first million numbers to a temp table. It takes about 84 ms on my machine. The key bottlenecks are waiting on the NESTING_TRANSACTION_FULL latch and
CXPACKET
, both of which I don't know how to address other than changingMAXDOP
. I wanted a query plan that can take advantage of parallel nested loops and demand based parallelism, which is what I managed to get:The code is a bit long. In short, I join together two derived tables of 246 rows and 271 rows for a total of 66666 rows. Those rows are joined to a 15 row derived table which takes advantage of the fact that the FIZZBUZZ pattern is repeated for every 15 rows. The final ten rows are added in with a
UNION ALL
.PostgreSQLName
PostgreSQL provides
generate_series
, a Table-Value Function (Set-Returning Function) which makes this substantially simpler. I'm assuming you don't want anything output whatsoever when the number neither 3, nor 5 goes into it.