AskOverflow.Dev

AskOverflow.Dev Logo AskOverflow.Dev Logo

AskOverflow.Dev Navigation

  • Início
  • system&network
  • Ubuntu
  • Unix
  • DBA
  • Computer
  • Coding
  • LangChain

Mobile menu

Close
  • Início
  • system&network
    • Recentes
    • Highest score
    • tags
  • Ubuntu
    • Recentes
    • Highest score
    • tags
  • Unix
    • Recentes
    • tags
  • DBA
    • Recentes
    • tags
  • Computer
    • Recentes
    • tags
  • Coding
    • Recentes
    • tags
Início / dba / Perguntas / 7350
Accepted
GWR
GWR
Asked: 2011-10-29 05:05:32 +0800 CST2011-10-29 05:05:32 +0800 CST 2011-10-29 05:05:32 +0800 CST

Eficiente INSERT INTO a Table com Índice Clusterizado

  • 772

Eu tenho uma instrução SQL que insere linhas em uma tabela com um índice clusterizado na coluna TRACKING_NUMBER.

POR EXEMPLO:

INSERT INTO TABL_NAME (TRACKING_NUMBER, COLB, COLC) 
SELECT TRACKING_NUMBER, COL_B, COL_C 
FROM STAGING_TABLE

Minha pergunta é - ajuda usar uma cláusula ORDER BY na instrução SELECT para a coluna de índice clusterizado ou qualquer ganho obtido seria negado pela classificação extra necessária para a cláusula ORDER BY?

sql-server performance
  • 3 3 respostas
  • 73489 Views

3 respostas

  • Voted
  1. Best Answer
    Martin Smith
    2011-10-29T09:46:04+08:002011-10-29T09:46:04+08:00

    Como as outras respostas já indicam, o SQL Server pode ou não garantir explicitamente que as linhas sejam classificadas na ordem do índice clusterizado antes do insert.

    Isso depende se o operador de índice clusterizado no plano tem ou não a DMLRequestSortpropriedade definida (que, por sua vez, depende do número estimado de linhas inseridas).

    Se você achar que o SQL Server está subestimando isso por qualquer motivo, você pode se beneficiar da adição de um explícito ORDER BYà SELECTconsulta para minimizar as divisões de página e a fragmentação resultante da INSERToperação

    Exemplo:

    use tempdb;
    
    GO
    
    CREATE TABLE T(N INT PRIMARY KEY,Filler char(2000))
    
    CREATE TABLE T2(N INT PRIMARY KEY,Filler char(2000))
    
    GO
    
    DECLARE @T TABLE (U UNIQUEIDENTIFIER PRIMARY KEY DEFAULT NEWID(),N int)
    
    INSERT INTO @T(N)
    SELECT number 
    FROM master..spt_values
    WHERE type = 'P' AND number BETWEEN 0 AND 499
    
    /*Estimated row count wrong as inserting from table variable*/
    INSERT INTO T(N)
    SELECT T1.N*1000 + T2.N
    FROM @T T1, @T T2
    
    /*Same operation using explicit sort*/    
    INSERT INTO T2(N)
    SELECT T1.N*1000 + T2.N
    FROM @T T1, @T T2
    ORDER BY T1.N*1000 + T2.N
    
    
    SELECT avg_fragmentation_in_percent,
           fragment_count,
           page_count,
           avg_page_space_used_in_percent,
           record_count
    FROM   sys.dm_db_index_physical_stats(2, OBJECT_ID('T'), NULL, NULL, 'DETAILED')
    ;  
    
    
    SELECT avg_fragmentation_in_percent,
           fragment_count,
           page_count,
           avg_page_space_used_in_percent,
           record_count
    FROM   sys.dm_db_index_physical_stats(2, OBJECT_ID('T2'), NULL, NULL, 'DETAILED')
    ;  
    

    Mostra que Té massivamente fragmentada

    avg_fragmentation_in_percent fragment_count       page_count           avg_page_space_used_in_percent record_count
    ---------------------------- -------------------- -------------------- ------------------------------ --------------------
    99.3116118225536             92535                92535                67.1668272794663               250000
    99.5                         200                  200                  74.2868173956017               92535
    0                            1                    1                    32.0978502594514               200
    

    Mas para a T2fragmentação é mínima

    avg_fragmentation_in_percent fragment_count       page_count           avg_page_space_used_in_percent record_count
    ---------------------------- -------------------- -------------------- ------------------------------ --------------------
    0.376                        262                  62500                99.456387447492                250000
    2.1551724137931              232                  232                  43.2438349394613               62500
    0                            1                    1                    37.2374598468001               232
    

    Por outro lado, às vezes você pode querer forçar o SQL Server a subestimar a contagem de linhas quando você sabe que os dados já estão pré-classificados e deseja evitar uma classificação desnecessária. Um exemplo notável é ao inserir um grande número de linhas em uma tabela com uma newsequentialidchave de índice clusterizado. Nas versões do SQL Server anteriores ao Denali, o SQL Server adiciona uma operação de classificação desnecessária e potencialmente cara . Isso pode ser evitado por

    DECLARE @var INT =2147483647
    
    INSERT INTO Foo
    SELECT TOP (@var) *
    FROM Bar
    

    O SQL Server estimará que 100 linhas serão inseridas, independentemente do tamanho Barque estiver abaixo do limite no qual uma classificação é adicionada ao plano. No entanto, como apontado nos comentários abaixo, isso significa que a inserção infelizmente não poderá aproveitar o registro mínimo.

    • 20
  2. Mark Storey-Smith
    2011-10-29T05:50:05+08:002011-10-29T05:50:05+08:00

    Se o otimizador decidir que seria mais eficiente classificar os dados antes da inserção, ele o fará em algum lugar a montante do operador de inserção. Se você introduzir uma classificação como parte de sua consulta, o otimizador deverá perceber que os dados já estão classificados e deixar de fazê-lo novamente. Observe que o plano de execução escolhido pode variar de execução para execução, dependendo do número de linhas inseridas em sua tabela de preparo.

    Se você puder capturar os planos de execução do processo com e sem a classificação explícita, anexe-os à sua pergunta para comentário.

    Editar: 28/10/2011 17:00

    A resposta do @Gonsalu parece mostrar que uma operação de classificação sempre ocorre, esse não é o caso. Scripts de demonstração necessários!

    Como os scripts estavam ficando muito grandes, eu os movi para Gist . Para facilitar a experimentação, os scripts usam o modo SQLCMD. Os testes são executados em 2K5SP3, dual core, 8GB.

    Os testes de inserção abrangem três cenários:

    1. Índice clusterizado de dados de preparo na mesma ordem do destino.
    2. Índice clusterizado de dados de preparo em ordem inversa.
    3. Dados de preparo agrupados por col2 que contém um INT aleatório.

    Primeira execução, inserindo 25 linhas.

    1ª corrida, 25 linhas

    Todos os três planos de execução são os mesmos, nenhuma classificação ocorre em nenhum lugar do plano e a varredura de índice clusterizado é "ordered=false".

    Segunda execução, inserindo 26 linhas.

    2ª corrida, 26 linhas

    Desta vez os planos são diferentes.

    • O primeiro mostra a varredura de índice clusterizado como ordenado=falso. Nenhuma classificação ocorreu, pois os dados de origem estão classificados adequadamente.
    • No segundo, o índice clusterizado varre como order=true, para trás. Portanto, não temos uma operação de classificação, mas a necessidade de classificar os dados é reconhecida pelo otimizador e ele verifica na ordem inversa.
    • O terceiro mostra um operador de classificação.

    Portanto, há um ponto de inflexão em que o otimizador considera necessária uma classificação. Como mostra @MartinSmith, isso parece ser baseado nas linhas estimadas a serem inseridas. No meu equipamento de teste 25 não requer uma classificação, 26 sim (2K5SP3, dual core, 8GB)

    O script SQLCMD inclui variáveis ​​que permitem alterar o tamanho das linhas na tabela (alterando a densidade da página) e o número de linhas em dbo.MyTable antes das inserções adicionais. Dos meus testes, nenhum deles tem qualquer efeito sobre o ponto de inflexão.

    Se algum leitor estiver tão inclinado, por favor, execute os scripts e adicione seu ponto de inflexão como um comentário. Interessado em saber se varia entre as plataformas de teste e/ou versões.

    Editar: 28/10/2011 20:15

    Testes repetidos no mesmo equipamento, mas com 2K8R2. Desta vez, o ponto de inflexão é de 251 linhas. Novamente, variar a densidade da página e as contagens de linhas existentes não tem efeito.

    • 13
  3. gonsalu
    2011-10-29T06:30:56+08:002011-10-29T06:30:56+08:00

    A ORDER BYcláusula na SELECTdeclaração é redundante.

    É redundante porque as linhas que serão inseridas, caso precisem ser classificadas , serão classificadas de qualquer maneira.

    Vamos criar um caso de teste.

    CREATE TABLE #Test (
        id INTEGER NOT NULL
    );
    
    CREATE UNIQUE CLUSTERED INDEX CL_Test_ID ON #Test (id);
    
    CREATE TABLE #Sequence (
        number INTEGER NOT NULL
    );
    
    INSERT INTO #Sequence
    SELECT number FROM master..spt_values WHERE name IS NULL;
    

    Vamos habilitar a exibição de texto dos planos de consulta reais, para que possamos ver quais tarefas são executadas pelo processador de consultas.

    SET STATISTICS PROFILE ON;
    GO
    

    Agora, vamos INSERTinserir 2K linhas na tabela sem uma ORDER BYcláusula.

    INSERT INTO #Test
    SELECT number
      FROM #Sequence
    

    O plano de execução real para esta consulta é o seguinte.

    INSERT INTO #Test  SELECT number    FROM #Sequence
      |--Clustered Index Insert(OBJECT:([tempdb].[dbo].[#Test]), SET:([tempdb].[dbo].[#Test].[id] = [tempdb].[dbo].[#Sequence].[number]))
           |--Top(ROWCOUNT est 0)
                |--Sort(ORDER BY:([tempdb].[dbo].[#Sequence].[number] ASC))
                     |--Table Scan(OBJECT:([tempdb].[dbo].[#Sequence]))
    

    Como você pode ver, há um operador Sort antes que o INSERT real ocorra.

    Agora, vamos limpar a tabela e INSERT2k linhas na tabela com a ORDER BYcláusula.

    TRUNCATE TABLE #Test;
    GO
    
    INSERT INTO #Test
    SELECT number
      FROM #Sequence
     ORDER BY number
    

    O plano de execução real para esta consulta é o seguinte.

    INSERT INTO #Test  SELECT number    FROM #Sequence   ORDER BY number
      |--Clustered Index Insert(OBJECT:([tempdb].[dbo].[#Test]), SET:([tempdb].[dbo].[#Test].[id] = [tempdb].[dbo].[#Sequence].[number]))
           |--Top(ROWCOUNT est 0)
                |--Sort(ORDER BY:([tempdb].[dbo].[#Sequence].[number] ASC))
                     |--Table Scan(OBJECT:([tempdb].[dbo].[#Sequence]))
    

    Observe que é o mesmo plano de execução usado para a INSERTinstrução sem a ORDER BYcláusula.

    Agora, a Sortoperação nem sempre é necessária, como Mark Smith mostrou em outra resposta (se o número de linhas a serem inseridas for baixo), mas a ORDER BYcláusula ainda é redundante nesse caso, pois mesmo com um explicit ORDER BY, nenhuma Sortoperação é gerada pelo processador de consultas.

    Você pode otimizar uma INSERTinstrução em uma tabela com um índice clusterizado usando um minimamente logado INSERT, mas isso está fora do escopo desta pergunta.

    Atualizado em 2011-11-02: Como Mark Smith mostrou , INSERTs em uma tabela com um índice clusterizado nem sempre precisa ser classificado - a ORDER BYcláusula também é redundante nesse caso.

    • 9

relate perguntas

  • Quais são as principais causas de deadlocks e podem ser evitadas?

  • Como determinar se um Índice é necessário ou necessário

  • Onde posso encontrar o log lento do mysql?

  • Como posso otimizar um mysqldump de um banco de dados grande?

Sidebar

Stats

  • Perguntas 205573
  • respostas 270741
  • best respostas 135370
  • utilizador 68524
  • Highest score
  • respostas
  • Marko Smith

    Como você mysqldump tabela (s) específica (s)?

    • 4 respostas
  • Marko Smith

    Como você mostra o SQL em execução em um banco de dados Oracle?

    • 2 respostas
  • Marko Smith

    Como selecionar a primeira linha de cada grupo?

    • 6 respostas
  • Marko Smith

    Listar os privilégios do banco de dados usando o psql

    • 10 respostas
  • Marko Smith

    Posso ver Consultas Históricas executadas em um banco de dados SQL Server?

    • 6 respostas
  • Marko Smith

    Como uso currval() no PostgreSQL para obter o último id inserido?

    • 10 respostas
  • Marko Smith

    Como executar o psql no Mac OS X?

    • 11 respostas
  • Marko Smith

    Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Como faço para listar todos os bancos de dados e tabelas usando o psql?

    • 7 respostas
  • Marko Smith

    Passando parâmetros de array para um procedimento armazenado

    • 12 respostas
  • Martin Hope
    Manuel Leduc Restrição exclusiva de várias colunas do PostgreSQL e valores NULL 2011-12-28 01:10:21 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +0800 CST
  • Martin Hope
    Stuart Blackler Quando uma chave primária deve ser declarada sem cluster? 2011-11-11 13:31:59 +0800 CST
  • Martin Hope
    pedrosanta Listar os privilégios do banco de dados usando o psql 2011-08-04 11:01:21 +0800 CST
  • Martin Hope
    Jonas Como posso cronometrar consultas SQL usando psql? 2011-06-04 02:22:54 +0800 CST
  • Martin Hope
    Jonas Como inserir valores em uma tabela de uma consulta de seleção no PostgreSQL? 2011-05-28 00:33:05 +0800 CST
  • Martin Hope
    Jonas Como faço para listar todos os bancos de dados e tabelas usando o psql? 2011-02-18 00:45:49 +0800 CST
  • Martin Hope
    BrunoLM Guid vs INT - Qual é melhor como chave primária? 2011-01-05 23:46:34 +0800 CST
  • Martin Hope
    bernd_k Quando devo usar uma restrição exclusiva em vez de um índice exclusivo? 2011-01-05 02:32:27 +0800 CST
  • Martin Hope
    Patrick Como posso otimizar um mysqldump de um banco de dados grande? 2011-01-04 13:13:48 +0800 CST

Hot tag

sql-server mysql postgresql sql-server-2014 sql-server-2016 oracle sql-server-2008 database-design query-performance sql-server-2017

Explore

  • Início
  • Perguntas
    • Recentes
    • Highest score
  • tag
  • help

Footer

AskOverflow.Dev

About Us

  • About Us
  • Contact Us

Legal Stuff

  • Privacy Policy

Language

  • Pt
  • Server
  • Unix

© 2023 AskOverflow.DEV All Rights Reserve