Database
 sql >> Base de Dados >  >> RDS >> Database

Gerar um conjunto ou sequência sem loops – parte 2


No meu post anterior, falei sobre maneiras de gerar uma sequência de números contíguos de 1 a 1.000. Agora eu gostaria de falar sobre os próximos níveis de escala:conjuntos geradores de 50.000 e 1.000.000 de números.

Gerando um conjunto de 50.000 números


Ao iniciar esta série, eu estava genuinamente curioso sobre como as diferentes abordagens seriam dimensionadas para conjuntos maiores de números. Na extremidade inferior, fiquei um pouco desanimado ao descobrir que minha abordagem favorita - usando sys.all_objects – não foi o método mais eficiente. Mas como essas diferentes técnicas escalariam para 50.000 linhas?
    Tabela de números

    Como já criamos uma tabela Numbers com 1.000.000 de linhas, essa consulta permanece praticamente idêntica:
    SELECT TOP (50000) n FROM dbo.Numbers ORDER BY n;

    Plano:


    spt_values

    Como existem apenas cerca de 2.500 linhas em spt_values , precisamos ser um pouco mais criativos se quisermos usá-lo como fonte do nosso gerador de conjuntos. Uma maneira de simular uma tabela maior é CROSS JOIN isso contra si mesmo. Se fizéssemos isso bruto, acabaríamos com ~ 2.500 linhas ao quadrado (mais de 6 milhões). Precisando de apenas 50.000 linhas, precisamos de cerca de 224 linhas ao quadrado. Então podemos fazer isso:
    ;WITH x AS ( SELECT TOP (224) número FROM [master]..spt_values)SELECT TOP (50000) n =ROW_NUMBER() OVER (ORDER BY x.number) FROM x CROSS JOIN x AS yORDER BY n; 

    Observe que isso é equivalente, mas mais conciso do que esta variação:
    SELECT TOP (50000) n =ROW_NUMBER() OVER (ORDER BY x.number) FROM (SELECT TOP (224) number FROM [master]..spt_values) AS xCROSS JOIN(SELECT TOP (224) number FROM [master] ]..spt_values) AS yORDER BY n;

    Em ambos os casos, o plano fica assim:


    sys.all_objects

    Como spt_values , sys.all_objects não satisfaz totalmente nosso requisito de 50.000 linhas por conta própria, portanto, precisaremos executar um CROSS JOIN semelhante .
    ;;WITH x AS ( SELECT TOP (224) [object_id] FROM sys.all_objects)SELECT TOP (50000) n =ROW_NUMBER() OVER (ORDER BY x.[object_id]) FROM x CROSS JOIN x AS y ORDER POR n;

    Plano:


    CTEs empilhados

    Precisamos apenas fazer um pequeno ajuste em nossos CTEs empilhados para obter exatamente 50.000 linhas:
    ;WITH e1(n) AS( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1) , -- 10e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10e3(n) AS (SELECT 1 FROM e2 CROSS JOIN e2 AS b), -- 100*100e4(n) AS (SELECT 1 FROM e3 CROSS JOIN (SELECT TOP 5 n FROM e1) AS b) -- 5*10000 SELECT n =ROW_NUMBER() OVER (ORDER BY n) FROM e4 ORDER BY n;

    Plano:


    CTEs recursivos

    Uma alteração ainda menos substancial é necessária para obter 50.000 linhas de nossa CTE recursiva:altere o WHERE cláusula para 50.000 e altere o MAXRECURSION opção para zero.
    ;COM n(n) AS( SELECT 1 UNION ALL SELECT n+1 FROM n WHERE n <50000)SELECT n FROM n ORDER nOPTION (MAXRECURSION 0);

    Plano:



    Nesse caso, há um ícone de aviso na classificação - como se vê, no meu sistema, a classificação precisava ser derramada no tempdb. Você pode não ver um vazamento em seu sistema, mas isso deve ser um aviso sobre os recursos necessários para essa técnica.

    Desempenho


    Assim como no último conjunto de testes, compararemos cada técnica, incluindo a tabela Numbers com cache frio e quente e compactada e descompactada:


    Tempo de execução, em milissegundos, para gerar 50.000 números contíguos

    Para obter um visual melhor, vamos remover o CTE recursivo, que foi um cão total neste teste e que distorce os resultados:


    Tempo de execução, em milissegundos, para gerar 50.000 números contíguos (excluindo recursivos CTE)

    Em 1.000 linhas, a diferença entre compactado e descompactado foi marginal, pois a consulta precisava apenas ler 8 e 9 páginas, respectivamente. Com 50.000 linhas, a lacuna aumenta um pouco:74 páginas versus 113. No entanto, o custo geral de descompactar os dados parece superar a economia de E/S. Assim, com 50.000 linhas, uma tabela de números não compactada parece ser o método mais eficiente de derivar um conjunto contíguo – embora, reconhecidamente, a vantagem seja marginal.

Gerando um conjunto de 1.000.000 números


Embora eu não possa imaginar muitos casos de uso em que você precisaria de um conjunto contíguo de números tão grande, eu queria incluí-lo para ser completo e porque fiz algumas observações interessantes nessa escala.
    Tabela de números

    Sem surpresas aqui, nossa consulta agora é:
    SELECT TOP 1000000 n FROM dbo.Numbers ORDER BY n;

    O TOP não é estritamente necessário, mas isso é apenas porque sabemos que nossa tabela Numbers e nossa saída desejada têm o mesmo número de linhas. O plano ainda é bastante semelhante aos testes anteriores:


    spt_values

    Para obter um CROSS JOIN que rende 1.000.000 de linhas, precisamos tomar 1.000 linhas ao quadrado:
    ;WITH x AS ( SELECT TOP (1000) número FROM [master]..spt_values)SELECT n =ROW_NUMBER() OVER (ORDER BY x.number) FROM x CROSS JOIN x AS y ORDER BY n;

    Plano:


    sys.all_objects

    Novamente, precisamos do produto vetorial de 1.000 linhas:
    ;WITH x AS ( SELECT TOP (1000) [object_id] FROM sys.all_objects)SELECT n =ROW_NUMBER() OVER (ORDER BY x.[object_id]) FROM x CROSS JOIN x AS y ORDER BY n; 
    Plano:


    CTEs empilhados

    Para o CTE empilhado, precisamos apenas de uma combinação ligeiramente diferente de CROSS JOIN s para chegar a 1.000.000 de linhas:
    ;WITH e1(n) AS( SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1 UNION ALL SELECT 1) , -- 10e2(n) AS (SELECT 1 FROM e1 CROSS JOIN e1 AS b), -- 10*10e3(n) AS (SELECT 1 FROM e1 CROSS JOIN e2 AS b), -- 10*100e4(n) AS (SELECT 1 FROM e3 CROSS JOIN e3 AS b) -- 1000*1000 SELECT n =ROW_NUMBER() OVER (ORDER BY n) FROM e4 ORDER BY n;

    Plano:



    Nesse tamanho de linha, você pode ver que a solução CTE empilhada fica paralela. Então eu também executei uma versão com MAXDOP 1 para obter uma forma de plano semelhante à anterior e ver se o paralelismo realmente ajuda:


    CTE recursiva

    O CTE recursivo novamente tem apenas uma pequena alteração; apenas o WHERE cláusula precisa ser alterada:
    ;COM n(n) AS( SELECT 1 UNION ALL SELECT n+1 FROM n WHERE n <1000000)SELECT n FROM n ORDER nOPTION (MAXRECURSION 0);

    Plano:


    Desempenho


    Mais uma vez vemos que o desempenho do CTE recursivo é abismal:


    Tempo de execução, em milissegundos, para gerar 1.000.000 números contíguos

    Removendo esse valor discrepante do gráfico, obtemos uma imagem melhor sobre o desempenho:


    Tempo de execução, em milissegundos, para gerar 1.000.000 números contíguos (excluindo recursivos CTE)

    Enquanto novamente vemos a tabela de números não compactada (pelo menos com um cache quente) como a vencedora, a diferença mesmo nessa escala não é tão notável.

Continua…


Agora que exploramos minuciosamente um punhado de abordagens para gerar uma sequência de números, passaremos para as datas. Na postagem final desta série, veremos a construção de um intervalo de datas como um conjunto, incluindo o uso de uma tabela de calendário e alguns casos de uso em que isso pode ser útil.

[ Parte 1 | Parte 2 | Parte 3]



Apêndice:Contagens de linhas


Você pode não estar tentando gerar um número exato de linhas; você pode querer apenas uma maneira direta de gerar muitas linhas. A seguir está uma lista de combinações de visualizações de catálogo que lhe darão várias contagens de linhas se você simplesmente SELECT sem um WHERE cláusula. Observe que esses números dependerão de você estar em um RTM ou em um service pack (já que alguns objetos do sistema são adicionados ou modificados) e também se você tem um banco de dados vazio.
Fonte Contagens de linhas
SQL Server 2008 R2 SQL Server 2012 SQL Server 2014
master..spt_values
2.508
2.515 2.519
master..spt_values ​​CROSS JOIN master..spt_values
6.290.064
6.325.225 6.345.361
sys.all_objects
1.990
2.089 2.165
sys.all_columns
5.157
7.276 8.560
sys.all_objects CROSS JOIN sys.all_objects
3.960.100
4.363.921 4.687.225
sys.all_objects CROSS JOIN sys.all_columns
10.262.430
15.199.564 18.532.400
sys.all_columns CROSS JOIN sys.all_columns
26.594.649
52.940.176 73.273.600

Tabela 1:contagens de linhas para várias consultas de exibição de catálogo