Recentemente, recebi a tarefa de imprimir todos os números primos (1 a 100). Eu falhei drasticamente lá. Meu código:
Create Procedure PrintPrimeNumbers
@startnum int,
@endnum int
AS
BEGIN
Declare @a INT;
Declare @i INT = 1
(
Select a = @startnum / 2;
WHILE @i<@a
BEGIN
@startnum%(@a-@i)
i=i+1;
)
END
Embora eu tenha terminado sem concluí-lo, pergunto-me se é possível executar esses programas no Banco de Dados (SQL Server 2008 R2).
Se sim, como isso pode acabar.
sql-server
sql-server-2008-r2
t-sql
ispostback
fonte
fonte
Respostas:
De longe, a maneira mais rápida e fácil de imprimir "todos os números primos (1-100)" é abraçar completamente o fato de que os números primos são um conjunto de valores conhecido, finito e imutável ("conhecido" e "finito" dentro de um faixa específica, é claro). Nesta pequena escala, por que desperdiçar CPU a cada vez para calcular um monte de valores conhecidos há muito tempo e ocupar quase nenhuma memória para armazenar?
Obviamente, se você precisar calcular os números primos entre 1 e 100, o seguinte é bastante eficiente:
Esta consulta apenas testa números ímpares, pois os números pares não serão primos de qualquer maneira. Também é específico para o intervalo de 1 a 100.
Agora, se você precisar de um intervalo dinâmico (semelhante ao mostrado no código de exemplo da pergunta), a seguir é apresentada uma adaptação da consulta acima que ainda é bastante eficiente (calculou o intervalo de 1 - 100.000 - 9592 entradas - em menos de 1 segundo):
Meu teste (usando
SET STATISTICS TIME, IO ON;
) mostra que esta consulta tem um desempenho melhor do que as outras duas respostas fornecidas (até agora):GAMA: 1-100
GAMA: 1 - 10.000
GAMA: 1 - 100.000
GAMA: 99.900 - 100.000
NOTA : Para executar este teste, eu tive que corrigir um erro no código de Dan -
@startnum
não era fatorado na consulta, portanto, sempre começava às1
. Troquei aDividend.num <= @endnum
linha porDividend.num BETWEEN @startnum AND @endnum
.GAMA: 1 - 100.000 (reteste parcial)
Depois de corrigir a consulta de Dan para o teste 99.900 - 100.000, notei que não havia mais leituras lógicas listadas. Portanto, retestei esse intervalo com essa correção ainda aplicada e descobri que as leituras lógicas haviam desaparecido novamente e os tempos eram um pouco melhores (e sim, o mesmo número de linhas foi retornado).
fonte
ROW_NUMBER() OVER (ORDER BY (SELECT 1))
? NãoROW_NUMBER() OVER ()
seria equivalente?OVER ()
, você receberá o seguinte erro:The function 'ROW_NUMBER' must have an OVER clause with ORDER BY.
. E, comORDER BY
, não pode ser uma constante, daí a subconsulta retornar uma constante.DECLARE @RangeStart INT = 999900, @RangeEnd INT = 1000000;
lo funciona, mas assim que definidoDECLARE @RangeStart INT = 9999999900, @RangeEnd INT = 10000000000;
, dizMsg 8115, Level 16, State 2, Line 1 Arithmetic overflow error converting expression to data type int. Msg 1014, Level 15, State 1, Line 5 A TOP or FETCH clause contains an invalid value.
?INT
. O valor máximo queINT
pode reter é 2.147.483.647, que é menor que o valor inicial de 9.999.999.900. Você recebe esse erro mesmo se executar apenas oDECLARE
. Você pode tentar alterar os tipos de dados variáveisBIGINT
e ver como isso acontece. É possível que outras pequenas alterações sejam necessárias para apoiar isso. Para intervalos de tipos de dados, consulte: int, bigint, smallint e tinyint .Uma maneira simples, mas não muito eficiente, de retornar os números primos no intervalo de 2 a 100 (1 não é primo) seria
Você também pode potencialmente materializar os números 2-100 em uma tabela e implementar a Peneira de Eratóstenes por meio de atualizações ou exclusões repetidas.
fonte
Sim, é viável, mas não acho que o T-SQL seja a ferramenta certa para o trabalho. Abaixo está um exemplo de uma abordagem baseada em conjunto no T-SQL para esse problema.
fonte
Podemos escrever o código abaixo e ele funciona:
Acima, criei um Procedimento armazenado para obter números primos.
Para conhecer os resultados, execute o procedimento armazenado:
fonte
fonte