Eu quero atualizar uma tabela grande que tem 83.423.460 linhas e está crescendo.
A consulta abaixo leva 8 minutos para ser executada com sucesso:
UPDATE FPP_Invoice_Revenue
SET Till_Prev_Inv_Amt = Till_Prev_Inv_Amt_In_USD / 0.0285714286,
Cur_Inv_Amt = Cur_Inv_Amt_In_USD / 0.0285714286,
YTD_Inv_Amt = YTD_Inv_Amt_In_USD / 0.0285714286
WHERE SOW_Number = '20014378'
Existe um clustered index
. Pensei em desabilitar esse índice antes de atualizar e reconstruir novamente após a atualização, mas isso também não funcionou, pois a reconstrução está demorando muito.
Eu li em algum lugar que isso pode ser alcançado dividindo-se em pequenas partes, mas como posso dividir a consulta acima?
DDL:
CREATE TABLE [dbo].[FPP_Invoice_Revenue](
[Project_Code] [varchar](10) NOT NULL,
[Project_Desc] [varchar](50) NULL,
[SOW_Number] [varchar](10) NOT NULL,
[SOW_Desc] [varchar](50) NULL,
[Invoice_No] [varchar](50) NOT NULL,
[Inv_Month] [int] NOT NULL,
[Inv_Year] [int] NOT NULL,
[Billing_Date] [smalldatetime] NULL,
[Doc_Currency] [varchar](10) NULL,
[Vertical] [varchar](255) NULL,
[Till_Prev_Inv_Amt] [numeric](24, 10) NULL,
[Cur_Inv_Amt] [numeric](24, 10) NULL,
[YTD_Inv_Amt] [numeric](24, 10) NULL,
[Till_Prev_Inv_Amt_In_USD] [numeric](24, 10) NULL,
[Cur_Inv_Amt_In_USD] [numeric](24, 10) NULL,
[YTD_Inv_Amt_In_USD] [numeric](24, 10) NULL,
CONSTRAINT [PK_FPP_Invoice_Revenue] PRIMARY KEY CLUSTERED
(
[Project_Code] ASC,
[SOW_Number] ASC,
[Invoice_No] ASC,
[Inv_Month] ASC,
[Inv_Year] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF,
ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON, FILLFACTOR = 90) ON [PRIMARY]
) ON [PRIMARY]
GO
SET ANSI_PADDING OFF
GO
Plano de execução:
Adicionar um índice
SOW_Number
permitirá que o SQL Server identifique muito rapidamente as linhas que precisam ser atualizadas, sem exigir uma verificação de toda a tabela (supondo que um número relativamente pequeno de linhas corresponda àWHERE
cláusula, tornando o índice altamente seletivo ).Mesmo que você tenha
SOW_Number
definido como parte do índice clusterizado, ele não é a coluna inicial do índice, o que significa que o SQL Server deve verificar todo o índice procurando por linhas que atendam àWHERE
cláusula. Adicionar as três colunas listadas naINCLUDE
cláusula permitirá que o SQL Server não execute uma pesquisa no índice clusterizado para esses valores, o que, de outra forma, seria necessário fazer, pois são usados nos cálculos dentro daSET
cláusula.Eu normalmente não sugeriria adicionar um índice como este sem avaliar cuidadosamente a carga de trabalho; no entanto, estou assumindo que você não tem nenhum índice além do índice clusterizado que você identifica em seu script. Adicionar um único índice nesse caso provavelmente fará uma grande diferença no desempenho de sua atualização, sem afetar drasticamente outras partes de sua carga de trabalho.
Configurei uma mesa de teste no meu laptop para poder testar o desempenho de adicionar o índice sugerido versus não ter nenhum índice no
SOW_Number
.Primeiro, criei um novo banco de dados
TestDB
para este teste (estou usando o SQL Server no Linux RC3):Em seguida, inseri 10.000.000 de linhas para ter uma tabela suficientemente grande. Eu sei, isso é um pouco menor que sua tabela, mas fornece dados suficientes para fazer extropolações razoáveis:
Em seguida, crio o índice não clusterizado que sugeri acima:
Isso levou cerca de 1:20 para criar no meu laptop.
Agora, a consulta:
Os dados de teste são distribuídos uniformemente em 10
SOW_Number
valores;WHERE SOW_Number = '6'
indica que atualizaremos 1.000.000 de linhas:O plano de execução real para a consulta de atualização:
As estatísticas de execução para isso:
Como você pode ver acima, a consulta levou apenas 14 segundos para atualizar 1.000.000 de linhas. Se
SOW_Number
for mais seletivo, o tempo necessário será proporcionalmente menor.Como você identificou em sua pergunta, descartar o índice clusterizado não ajuda a aumentar o desempenho da consulta de atualização. Entenda que todos os dados em uma tabela que possui um índice clusterizado são de fato armazenados no índice clusterizado. Descartar e recriar o índice clusterizado fará com que todas as 83 milhões de linhas sejam gravadas no log enquanto os dados são movidos para dentro e para fora do índice clusterizado. Isso é muito E/S extra que não é necessário para que a atualização seja bem-sucedida.
Você
SOW_Number
definiu como umvarchar(10)
; se o conteúdo dessa coluna sempre tiver na verdade 10 caracteres, você pode considerar modificá-lo parachar(10)
em vez devarchar(10)
, pois isso é realmente mais eficiente. Considere isso para todas asvarchar
colunas que você tem. Além disso,Vertical
realmente precisa ter 255 caracteres? Presumivelmente,Inv_Month
eInv_Year
na verdade não são obrigados a armazenar até 2.147.483.647? Você provavelmente poderia converter essas colunassmallint
e economizar 4 bytes por linha. Tendo em mente que as colunas na chave de índice clusterizado são usadas (duplicadas) em cada índice não clusterizado, a economia de espaço com o dimensionamento correto dos dados pode ser substancial.Em vez de atualizar, e provavelmente ter que continuar atualizando essa tabela, por que não criar colunas computadas uma vez?
São cálculos simples e você pode evitar usar um gatilho no caminho.
Você pode decidir se deseja persistir e/ou indexá-los, e quaisquer consultas que atualmente usam cálculos que correspondam à definição de coluna computada devem usar as colunas computadas sem alterar suas consultas.
Então, algo assim também se beneficiaria:
É estranho para mim que não esteja usando uma varredura de índice, pois [SOW_Number] faz parte do PK. De acordo com um comentário, uma varredura de índice seria relatada como uma varredura de tabela, pois é um índice clusterizado.
Poderia adicionar um índice em [SOW_Number] como sugerido por Max (+1).
Colocar [SOW_Number] primeiro no PK pode ajudar, mas fazer isso na tabela com 83.423.460 linhas não é algo que eu gostaria de fazer.
Você não está alterando nenhum valor de índice, portanto, desabilitar esse índice não terá efeito
8 minutos não é tão ruim. Essa é uma consulta que você precisa executar com frequência? Nesse caso, uma visão pode ser mais eficaz.