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 / 73109
Accepted
George.Palacios
George.Palacios
Asked: 2014-08-05 05:05:42 +0800 CST2014-08-05 05:05:42 +0800 CST 2014-08-05 05:05:42 +0800 CST

Atualização na exibição em tabelas particionadas atualizando todos os índices

  • 772

Temos uma tabela, que é particionada por meio de um campo de data em anos separados.

Há uma visão sobre todas essas mesas (Call)

Esquema é o seguinte:

CREATE TABLE [dbo].[Call_2015](
    [calID] [uniqueidentifier] NOT NULL,
    [calPackageID] [int] NULL,
    [calClientID] [int] NULL,
    [calStartDate] [datetime] NOT NULL,
    [calEndDate] [datetime] NOT NULL,
    [calTimeIn] [char](5) NULL,
    [calTimeOut] [char](5) NULL,
    [calMinutes] [smallint] NULL,
    [calPreferredTimeIn] [char](5) NULL,
    [calPreferredTimeOut] [char](5) NULL,
    [calActualTimeIn] [char](5) NULL,
    [calActualTimeOut] [char](5) NULL,
    [calActualMinutes] [smallint] NULL,
    [calConfirmed] [smallint] NULL,
    [calCarerID] [int] NULL,
    [calRepCarerID] [int] NULL,
    [calOriginalCarerID] [int] NULL,
    [calContractID] [int] NULL,
    [calNeedID] [int] NULL,
    [calMedicationID] [int] NULL,
    [calFrequency] [smallint] NULL,
    [calFromDate] [datetime] NULL,
    [calWeekNo] [smallint] NULL,
    [calAlert] [smallint] NULL,
    [calNoLeave] [smallint] NULL,
    [calTimeCritical] [smallint] NULL,
    [calStatus] [smallint] NULL,
    [calClientAwayReasonID] [int] NULL,
    [calCarerAwayReasonID] [int] NULL,
    [calOutsideShift] [smallint] NULL,
    [calHistoryID] [int] NULL,
    [calInvoiceID] [int] NULL,
    [calWagesheetID] [int] NULL,
    [calReasonID] [int] NULL,
    [calCallConfirmID] [varchar](50) NULL,
    [calCreated] [datetime] NULL,
    [calUpdated] [datetime] NULL,
    [calVariation] [int] NULL,
    [calVariationUserID] [int] NULL,
    [calException] [smallint] NULL,
    [calRetained] [smallint] NULL,
    [calDoubleUpID] [uniqueidentifier] NULL,
    [calDoubleUpOrder] [smallint] NULL,
    [calNeedCount] [smallint] NULL,
    [calNoStay] [smallint] NULL,
    [calCoverCarerID] [int] NULL,
    [calPayAdjustment] [real] NULL,
    [calChargeAdjustment] [real] NULL,
    [calTeamID] [int] NULL,
    [calExpenses] [money] NULL,
    [calMileage] [real] NULL,
    [calOverrideStatus] [smallint] NULL,
    [calLocked] [smallint] NULL,
    [calDriver] [smallint] NULL,
    [calPostcode] [char](10) NULL,
    [calDayCentreID] [int] NULL,
    [calMustHaveCarer] [smallint] NULL,
    [calRoleID] [int] NULL,
    [calUnavailableCarerID] [int] NULL,
    [calClientInformed] [smallint] NULL,
    [calFamilyInformed] [smallint] NULL,
    [calMonthlyDay] [smallint] NULL,
    [calOriginalTimeIn] [char](5) NULL,
    [calLeadCarer] [smallint] NULL,
    [calCallTypeID] [int] NULL,
    [calActualStartDate] [datetime] NULL,
    [calActualEndDate] [datetime] NULL,
    [Table_Year] [int] NOT NULL,
 CONSTRAINT [PK_Call_2015] PRIMARY KEY CLUSTERED 
(
    [Table_Year] ASC,
    [calID] ASC,
    [calStartDate] ASC,
    [calEndDate] ASC
)WITH (PAD_INDEX  = OFF, STATISTICS_NORECOMPUTE  = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS  = ON, ALLOW_PAGE_LOCKS  = ON) ON [PRIMARY]
) ON [PRIMARY]

GO

SET ANSI_PADDING OFF
GO

ALTER TABLE [dbo].[Call_2015]  WITH CHECK ADD  CONSTRAINT [CK_Call_Year_2015] CHECK  (([Table_Year]=(2015)))
GO

ALTER TABLE [dbo].[Call_2015] CHECK CONSTRAINT [CK_Call_Year_2015]
GO

ALTER TABLE [dbo].[Call_2015]  WITH CHECK ADD  CONSTRAINT [CK_calStartDate_2015] CHECK  (([calStartDate]>=CONVERT([datetime],'01 Jan 2015 00:00:00',(0)) AND [calStartDate]<=CONVERT([datetime],'31 DEC 2015 23:59:59',(0))))
GO

ALTER TABLE [dbo].[Call_2015] CHECK CONSTRAINT [CK_calStartDate_2015]
GO

ALTER TABLE [dbo].[Call_2015] ADD  CONSTRAINT [DF_Call_2015_Table_Year]  DEFAULT ((2015)) FOR [Table_Year]
GO

A atualização da tabela é a seguinte:

UPDATE Call SET
        calStartDate = CASE 
            WHEN calFrequency = 14 THEN dbo.funDate(@MonthlyDay, MONTH(calStartDate), YEAR(calStartDate))
            WHEN calFrequency IN (15,16) THEN dbo.funMonthlyCallDate(calFrequency, @MonthlyDay, calStartDate)
            ELSE DateAdd(d, @StartDay-1, (calStartDate - datepart(dw,calStartDate)+1)) 
        END,
        calEndDate = CASE 
            WHEN calFrequency = 14 THEN dbo.funDate(@MonthlyDay + @EndDay - @StartDay, MONTH(calStartDate), YEAR(calStartDate))
            WHEN calFrequency IN (15,16) THEN DATEADD(D, @EndDay - @StartDay, dbo.funMonthlyCallDate(calFrequency, @MonthlyDay, calStartDate))
            ELSE DateAdd(d, @StartDay-1+@DayCount, (calStartDate - datepart(dw,calStartDate)+1)) 
        END,
        calTimeIn = @TimeIn,
        calTimeOut = @TimeOut,
        calMinutes = @Minutes,
        calMonthlyDay = @MonthlyDay,
        calClientInformed = Null, 
        calFamilyInformed = Null
    WHERE calPackageID = @PackageID
    AND calClientID = @ClientID
    AND calWeekNo = @WeekNo
    AND (DatePart(dw, calStartDate) = @OriginalDay OR calFrequency IN (14,15,16))
    AND calStartDate BETWEEN @StartDate AND @EndDate
    AND (calInvoiceID = 0 OR calInvoiceID Is Null OR @InvoicesFinalised = 1)
    AND (calWagesheetID = 0 OR calWagesheetID Is Null OR @WagesFinalised = 1)
    AND (calLocked = 0 OR calLocked Is Null)
    AND (Table_Year = YEAR(@StartDate) 
            OR Table_Year =YEAR(@EndDate))

O SP atualiza um lote de linhas dependentes da entrada em @StartDate e @EndDate (atualiza todas as linhas com uma calStartDate entre as duas)

O problema então vem com o plano de execução. Há enormes custos de E/S para a operação, e eu acertei como o SQL está lidando com a atualização.

Atualmente temos 20 dessas mesas; particionada por ano. Cada atualização está causando uma atualização dos índices de cada tabela, independentemente de a tabela ser realmente tocada pela operação de atualização ou não.

Plano de execução

Abaixo desta seção, ele atualiza, exatamente da mesma maneira, todas as tabelas na exibição.

Não consigo entender por que isso ocorre, pois especifiquei o Table_Year (no qual a tabela está particionada) no texto da consulta. O SQL não deveria atualizar apenas a tabela necessária?

sql-server index
  • 2 2 respostas
  • 1144 Views

2 respostas

  • Voted
  1. Aaron Bertrand
    2014-08-05T06:10:00+08:002014-08-05T06:10:00+08:00

    [Observação: também respondido em answers.SQLPerformance.com .]

    Na verdade, essas não são tabelas particionadas e, mesmo que fossem, a eliminação de partições não funcionaria realmente para atualizar índices, a menos que todos os índices também fossem alinhados por partição.

    Como você está usando o Express Edition e não pode realmente usar o particionamento, tenho uma abordagem diferente para recomendar: UPDATEs dinâmicos que afetam apenas a(s) tabela(s) representada(s) em @StartDate / @EndDate. Você terá que preencher a lista de parâmetros duas vezes; uma vez com seus tipos de dados - isso deve ser fácil, pois presumo que eles já estejam declarados em algum lugar.

    DECLARE 
      @StartDate DATE = '20140605',
      @EndDate   DATE = '20150201';
    
    
    DECLARE 
      @SQL NVARCHAR(MAX) = N'',
      @baseSQL NVARCHAR(MAX) = N'UPDATE dbo.[Call_$y$] SET
          calStartDate = CASE 
              WHEN calFrequency = 14 THEN dbo.funDate(@MonthlyDay, MONTH(calStartDate), YEAR(calStartDate))
              WHEN calFrequency IN (15,16) THEN dbo.funMonthlyCallDate(calFrequency, @MonthlyDay, calStartDate)
              ELSE DateAdd(d, @StartDay-1, (calStartDate - datepart(dw,calStartDate)+1)) 
          END,
          calEndDate = CASE 
              WHEN calFrequency = 14 THEN dbo.funDate(@MonthlyDay + @EndDay - @StartDay, MONTH(calStartDate), YEAR(calStartDate))
              WHEN calFrequency IN (15,16) THEN DATEADD(D, @EndDay - @StartDay, dbo.funMonthlyCallDate(calFrequency, @MonthlyDay, calStartDate))
              ELSE DateAdd(d, @StartDay-1+@DayCount, (calStartDate - datepart(dw,calStartDate)+1)) 
          END,
          calTimeIn = @TimeIn,
          calTimeOut = @TimeOut,
          calMinutes = @Minutes,
          calMonthlyDay = @MonthlyDay,
          calClientInformed = Null, 
          calFamilyInformed = Null
      WHERE calPackageID = @PackageID
      AND calClientID = @ClientID
      AND calWeekNo = @WeekNo
      AND (DatePart(dw, calStartDate) = @OriginalDay OR calFrequency IN (14,15,16))
      AND calStartDate BETWEEN @StartDate AND @EndDate
      AND (calInvoiceID = 0 OR calInvoiceID Is Null OR @InvoicesFinalised = 1)
      AND (calWagesheetID = 0 OR calWagesheetID Is Null OR @WagesFinalised = 1)
      AND (calLocked = 0 OR calLocked Is Null)
      AND (Table_Year = YEAR(@StartDate) 
              OR Table_Year =YEAR(@EndDate));',
      @params NVARCHAR(MAX) = N'@MonthlyDay INT,...,@StartDate DATETIME,@EndDate DATETIME';
      -- you'll need to update the params list 
      -- to reflect all of the params coming in
    
    ;WITH y AS 
    (
      SELECT y FROM 
      (
        -- all valid years to choose from:
        VALUES(2014),(2015),(2016)
      ) AS y(y)
    )
    SELECT @SQL += REPLACE(@baseSQL, N'$y$', CONVERT(CHAR(4),y))
      FROM y 
      WHERE y >= YEAR(@StartDate) 
        AND y <= YEAR(@EndDate);
    
    EXEC sp_executesql @SQL,
      @params,
      @MonthlyDay,...,@StartDate,@EndDate;
    -- you'll also need to update the list of params here
    

    Como você pode abranger no máximo dois anos (veja os comentários abaixo), você pode simplificar um pouco alterando o CTE para:

    ;WITH y AS 
    (
      SELECT y = YEAR(@StartDate)
      UNION SELECT YEAR(@EndDate)
    )
    SELECT @SQL += REPLACE(@baseSQL, N'$y$', CONVERT(CHAR(4),y))
      FROM y;
    

    Mas observe que isso ainda terminará com o mesmo número de consultas a serem executadas e sem corrigir algumas das outras coisas (como as cláusulas não sargáveis ​​em algumas das colunas), isso ainda produzirá exatamente o mesmo desempenho que meu original versão, você simplesmente não precisava digitar os anos reais. Tenha muito cuidado com essa "otimização" se for possível que StartDate e EndDate tenham mais de 365 dias de diferença.

    • 2
  2. Best Answer
    Paul White
    2014-08-06T00:03:32+08:002014-08-06T00:03:32+08:00

    Abaixo desta seção, ele atualiza, exatamente da mesma maneira, todas as tabelas na exibição. Não consigo ver por que isso ocorre, pois especifiquei o Table_Year(no qual a tabela está particionada) no texto da consulta. O SQL não deveria atualizar apenas a tabela necessária?

    A exibição atende a todos os requisitos de particionamento para arquivos Table_Yeare calStartDate. A última coluna é modificada pela UPDATEinstrução para que o otimizador de consulta tenha que produzir um plano capaz de mover linhas entre as partições.

    Na verdade, as linhas não poderiam se mover entre as partições neste caso porque há uma relação de 1:1 entre Table_Yearos valores anuais de calStartDate, mas as etapas envolvidas nesse raciocínio são muito opacas para o otimizador.

    O novo valor para calStartDateé baseado em uma expressão complexa que faz referência a variáveis. O plano de consulta será armazenado em cache e poderá ser reutilizado quando as variáveis ​​tiverem valores diferentes, que é apenas outro fator que significa que o plano deve ser muito geral.

    Todas essas considerações levam a um plano que não apresenta eliminação de partições estáticas. No entanto, apresenta eliminação de partição dinâmica :

    No lado da leitura, a cadeia de operadores Filter imediatamente abaixo da Concatenation são todos filtros de inicialização . Eles avaliam seu predicado antes que a subárvore seja executada. Se o predicado for avaliado como falso, a subárvore sob o Filtro não será executada.

    O efeito geral é que apenas as tabelas sob a exibição que podem conter linhas qualificadas (dependendo dos valores das variáveis ​​de tempo de execução) são acessadas. Observe que o plano de execução mostra apenas as linhas sendo lidas de uma das tabelas base e a propriedade Execuções reais para todas as outras tabelas base é zero; esses operadores não foram executados em tempo de execução.

    No fragmento do plano abaixo, os filtros de inicialização garantem que apenas os operadores verdes sejam executados; os vermelhos nunca começam:

    Fragmento de plano do lado da leitura

    No lado da escrita, o filtro normal (não 'inicialização') logo à direita de cada operador Clustered Index Update garante que apenas as alterações da tabela atual sejam transmitidas. No plano de exemplo, apenas uma atualização de índice clusterizado (e seus operadores de manutenção de índice não clusterizados associados) recebe qualquer linha:

    Fragmento de plano do lado da gravação

    • 2

relate perguntas

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

  • Quanto "Padding" coloco em meus índices?

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

  • O que significa "índice" em RDBMSs? [fechado]

  • Como criar um índice condicional no MySQL?

Sidebar

Stats

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

    conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host

    • 12 respostas
  • Marko Smith

    Como fazer a saída do sqlplus aparecer em uma linha?

    • 3 respostas
  • Marko Smith

    Selecione qual tem data máxima ou data mais recente

    • 3 respostas
  • Marko Smith

    Como faço para listar todos os esquemas no PostgreSQL?

    • 4 respostas
  • Marko Smith

    Listar todas as colunas de uma tabela especificada

    • 5 respostas
  • Marko Smith

    Como usar o sqlplus para se conectar a um banco de dados Oracle localizado em outro host sem modificar meu próprio tnsnames.ora

    • 4 respostas
  • Marko Smith

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

    • 4 respostas
  • Marko Smith

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

    • 10 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
  • Martin Hope
    Jin conectar ao servidor PostgreSQL: FATAL: nenhuma entrada pg_hba.conf para o host 2014-12-02 02:54:58 +0800 CST
  • Martin Hope
    Stéphane Como faço para listar todos os esquemas no PostgreSQL? 2013-04-16 11:19:16 +0800 CST
  • Martin Hope
    Mike Walsh Por que o log de transações continua crescendo ou fica sem espaço? 2012-12-05 18:11:22 +0800 CST
  • Martin Hope
    Stephane Rolland Listar todas as colunas de uma tabela especificada 2012-08-14 04:44:44 +0800 CST
  • Martin Hope
    haxney O MySQL pode realizar consultas razoavelmente em bilhões de linhas? 2012-07-03 11:36:13 +0800 CST
  • Martin Hope
    qazwsx Como posso monitorar o andamento de uma importação de um arquivo .sql grande? 2012-05-03 08:54:41 +0800 CST
  • Martin Hope
    markdorison Como você mysqldump tabela (s) específica (s)? 2011-12-17 12:39:37 +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

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