As operações de agregação na exibição ignoram o índice [fechado]

8

O cenário

Era uma vez um banco de dados de armazenamento temporário em uma pequena empresa que participava de um processo de ETL, atuando como um catálogo de recebimento para os vários formatos de arquivos de várias fontes de terceiros. O E foi tratado através de pacotes DTS, com poucas estruturas de controle para auditoria ou controle, mas foi considerado "Bom o suficiente" e, para todos os efeitos, foi.

Os dados fornecidos pela parte E destinavam-se ao consumo por um aplicativo singular, desenvolvido e gerenciado por um punhado de programadores jovens e capazes. Embora não possuam experiência ou conhecimento das técnicas de data warehousing da época, eles estabeleceram e criaram seus próprios processos T e L a partir do código do aplicativo. À vista, esses novatos engenheiros de software inventaram o que as pessoas de fora poderiam chamar de "roda menos do que o ideal", mas com "Bom o suficiente" como um nível de serviço sempre presente, eles foram capazes de fornecer uma estrutura operacional.

Por um tempo, tudo ficou bom no domínio fortemente acoplado, com o catálogo Staging se deliciando com os dados de uma dúzia de terceiros, sendo alimentado pelo aplicativo. À medida que a aplicação crescia, também aumentavam seus apetites, mas com os hábeis desenvolvedores de cavaleiros brancos vigiando o sistema, esses apetites foram tratados rapidamente e, em muitos casos, até bem.

Mas a idade de ouro não poderia durar para sempre, é claro. Com a prosperidade concedida pelo aplicativo bem-sucedido, os negócios cresceram e cresceram. À medida que crescia, o ambiente e o aplicativo de armazenamento temporário eram forçados a crescer com ele. Apesar de toda a vigilância, o mero punhado de desenvolvedores de heróis não conseguia manter a manutenção do sistema agora expansivo, e os consumidores tinham direito a seus dados. Não era mais uma questão do que eles precisavam ou até queriam, mas a população achava que eles mereciam, exigindo ainda mais.

Armado com pouco mais do que cofres cheios de ganhos, os negócios chegaram ao mercado, contratando desenvolvedores e administradores para ajudar a apoiar o sistema sempre crescente. Mercenários de todos os etos afluíam à empresa, mas com esse surto de crescimento veio pouco o caminho das orientações especializadas disponíveis. Novos desenvolvedores e administradores lutaram para entender os meandros da suíte caseira, até que as frustrações resultaram em guerra total. Cada departamento começou a tentar resolver todos os problemas sozinho, trabalhando mais um contra o outro do que trabalhando um com o outro. Um único projeto ou iniciativa seria implementado de várias maneiras diferentes, cada uma ligeiramente diferente da seguinte. A tensão de tudo isso provou ser demais para alguns dos cavaleiros brancos e, quando caíram, o império desmoronou. Logo, o sistema estava em frangalhos,

Apesar da transformação desses campos de promessa em código de espaguete sangrento, a empresa resistiu. Afinal, era "Bom o suficiente".

O desafio

Mais algumas mudanças de regime e contratações depois, me encontro no emprego da empresa. Faz muitos anos desde as grandes guerras, mas o dano causado ainda é muito visível. Eu consegui resolver alguns dos pontos fracos da parte E do sistema e adicionar algumas tabelas de controle sob o pretexto de atualizar os pacotes DTS para o SSIS, que agora estão sendo usados ​​por alguns profissionais reais de data warehousing, pois eles criam um ambiente normal. e substituição T e L documentada.

O primeiro obstáculo foi importar os dados dos arquivos de terceiros de uma maneira que não truncasse os valores ou alterasse os tipos de dados nativos, mas também incluísse algumas teclas de controle para recarregamentos e expurgos. Tudo estava bem, mas os aplicativos precisavam acessar essas novas tabelas de maneira transparente e transparente. Um pacote DTS pode preencher uma tabela, que é então lida diretamente pelo aplicativo. As atualizações do SSIS precisam ser feitas paralelamente por motivos de controle de qualidade, mas esses novos pacotes incluem várias chaves de controle e também utilizam um esquema de particionamento, sem mencionar que as mudanças reais de metadados por si só podem ser significativas o suficiente para garantir uma nova tabela de qualquer maneira. nova tabela foi usada para os novos pacotes SSIS.

Com as importações de dados confiáveis ​​agora trabalhando e sendo usadas pela equipe de armazenamento, o verdadeiro desafio vem ao servir os novos dados para os aplicativos que acessam diretamente o ambiente de armazenamento temporário, com um impacto mínimo (também conhecido como "Não") no código do aplicativo. Para isso, optou por vistas de uso, renomear uma tabela, como dbo.DailyTransactiona dbo.DailyTranscation_LEGACYe reutilizar o dbo.DailyTransactionnome do objeto para uma vista, o que efetivamente apenas seleciona tudo, desde o agoraLEGACYtabela designada. Como recarregar os anos de dados contidos nessas tabelas não é uma opção da perspectiva dos negócios, como as novas tabelas particionadas e particionadas pelo SSIS entram na produção, as importações antigas do DTS são desativadas e os aplicativos precisam poder acesse também os novos dados nas novas tabelas. Nesse ponto, as visualizações são atualizadas para selecionar os dados das novas tabelas ( dbo.DailyTransactionCompletepor exemplo, quando disponíveis) e selecionar nas tabelas herdadas quando não estiverem.

Com efeito, algo como o seguinte está sendo feito:

CREATE VIEW dbo.DailyTransaction
AS  SELECT  DailyTransaction_PK, FileDate, Foo
    FROM    dbo.DailyTransactionComplete
    UNION ALL
    SELECT  DailyTransaction_PK, FileDate, Foo
    FROM    dbo.DailyTransaction_LEGACY l
    WHERE NOT EXISTS (  SELECT  1
                        FROM    dbo.DailyTransactionComplete t
                        WHERE   t.FileDate = l.FileDate );

Embora logicamente correto, isso não apresenta um bom desempenho em vários casos de agregação, geralmente resultando em um plano de execução que executa uma varredura completa do índice nos dados da tabela herdada. Provavelmente, isso é bom para algumas dezenas de milhões de registros, mas não tanto para algumas dezenas de milhões de registros. Como o último é de fato o caso, tive que recorrer a ser ... "criativo", levando-me a criar uma exibição indexada.

Aqui está o pequeno caso de teste que eu configurei, incluindo a FileDatechave de controle que foi portada para a DateCode_FKporta compatível com o Data Warehouse para ilustrar o quão pouco me importo com o fato de as consultas contra a nova tabela serem sargáveis por enquanto:

USE tempdb;
GO

SET NOCOUNT ON;
GO

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'DailyTransaction_LEGACY'
                    AND type = 'U' )
BEGIN
    --DROP TABLE dbo.DailyTransaction_LEGACY;
    CREATE TABLE dbo.DailyTransaction_LEGACY
    (
        DailyTransaction_PK         BIGINT IDENTITY( 1, 1 ) NOT NULL,
        FileDate                    DATETIME NOT NULL,
        Foo                         INT NOT NULL
    );

    INSERT INTO dbo.DailyTransaction_LEGACY ( FileDate, Foo )
    SELECT  DATEADD( DAY, ( 1 - ROW_NUMBER() 
                OVER( ORDER BY so1.object_id ) - 800 ) % 1000, 
                CONVERT( DATE, GETDATE() ) ),
            so1.object_id % 1000 + so2.object_id % 1000
    FROM    sys.all_objects so1
    CROSS JOIN sys.all_objects so2;

    ALTER TABLE dbo.DailyTransaction_LEGACY
    ADD CONSTRAINT PK__DailyTrainsaction
        PRIMARY KEY CLUSTERED ( DailyTransaction_PK )
    WITH ( DATA_COMPRESSION = PAGE, FILLFACTOR = 100 );
END;
GO

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'DailyTransactionComplete'
                    AND type = 'U' )
BEGIN
    --DROP TABLE dbo.DailyTransactionComplete;
    CREATE TABLE dbo.DailyTransactionComplete
    (
        DailyTransaction_PK            BIGINT IDENTITY( 1, 1 ) NOT NULL,
        DateCode_FK                    INTEGER NOT NULL,
        Foo                            INTEGER NOT NULL
    );

    INSERT INTO dbo.DailyTransactionComplete ( DateCode_FK, Foo )
    SELECT  TOP 100000
            CONVERT( INTEGER, CONVERT( VARCHAR( 8 ), DATEADD( DAY, 
                ( 1 - ROW_NUMBER() OVER( ORDER BY so1.object_id ) ) % 100, 
                GETDATE() ), 112 ) ),
            so1.object_id % 1000
    FROM    sys.all_objects so1
    CROSS JOIN sys.all_objects so2;

    ALTER TABLE dbo.DailyTransactionComplete
    ADD CONSTRAINT PK__DailyTransaction
        PRIMARY KEY CLUSTERED ( DateCode_FK, DailyTransaction_PK )
    WITH ( DATA_COMPRESSION = PAGE, FILLFACTOR = 100 );        
END;
GO

Na minha sandbox local, a tabela acima me fornece uma tabela herdada com cerca de 4,4 milhões de linhas e uma nova tabela contendo 0,1 milhão de linhas, com alguma sobreposição dos valores DateCode_FK/ FileDate.

Um MAX( FileDate )contra a tabela herdada sem índices adicionais é executado sobre o que eu esperaria.

SET STATISTICS IO, TIME ON;

DECLARE @ConsumeOutput        DATETIME;
SELECT  @ConsumeOutput = MAX( FileDate )
FROM    dbo.DailyTransaction_LEGACY;

SET STATISTICS IO, TIME OFF;
GO

Tabela 'DailyTransaction_LEGACY'. Contagem de varredura 1, leituras lógicas 9228, leituras físicas 0, leituras antecipadas 0, leituras lógicas lob 0, leituras físicas lob 0, leituras físicas lob 0, leituras antecipadas lob.

Tempos de execução do SQL Server: tempo de CPU = 889 ms, tempo decorrido = 886 ms.

Índice agrupado, herdado

Jogar um índice simples em cima da mesa torna as coisas muito melhores. Ainda uma varredura, mas uma varredura em um registro, em vez dos 4,4 milhões de registros. Eu sou legal com isso.

CREATE NONCLUSTERED INDEX IX__DailyTransaction__FileDate
    ON    dbo.DailyTransaction_LEGACY ( FileDate );

SET STATISTICS IO, TIME ON;

DECLARE @ConsumeOutput        DATETIME;
SELECT  @ConsumeOutput = MAX( FileDate )
FROM    dbo.DailyTransaction_LEGACY;

SET STATISTICS IO, TIME OFF;
GO

Tempo de análise e compilação do SQL Server: tempo da CPU = 0 ms, tempo decorrido = 1 ms. Tabela 'DailyTransaction_LEGACY'. Contagem de varreduras 1, leituras lógicas 3, leituras físicas 0, leituras antecipadas 0, leituras lógicas lob 0, leituras lógicas lob 0, leituras físicas lob 0, leituras antecipadas lob.

Tempos de execução do SQL Server: tempo de CPU = 0 ms, tempo decorrido = 0 ms.

Índice não agrupado, herdado

E agora, criando a visualização para que os desenvolvedores não precisem alterar nenhum código, porque aparentemente seria o fim do mundo como o conhecemos. Um tipo de cataclismo.

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'DailyTransaction'
                    AND type = 'V' )
BEGIN
    EXEC( 'CREATE VIEW dbo.DailyTransaction AS SELECT x = 1;' );
END;
GO

ALTER VIEW dbo.DailyTransaction
AS  SELECT  DailyTransaction_PK, FileDate = CONVERT( 
                DATETIME, CONVERT( VARCHAR( 8 ), DateCode_FK ), 112 ), Foo
    FROM    dbo.DailyTransactionComplete
    UNION ALL    
    SELECT  DailyTransaction_PK, FileDate, Foo
    FROM    dbo.DailyTransaction_LEGACY l
    WHERE   NOT EXISTS (    SELECT  1
                            FROM    dbo.DailyTransactionComplete t
                            WHERE   CONVERT( DATETIME, CONVERT( VARCHAR( 8 ),
                                        t.DateCode_FK ), 112 ) = l.FileDate );
GO

Sim, a subconsulta é péssima, mas esse não é o problema e provavelmente vou simplesmente criar uma coluna computada persistente e lançar um índice nela para esse fim quando o problema real for resolvido. Então, sem mais delongas,

O problema

SET STATISTICS IO, TIME ON;

DECLARE @ConsumeOutput1        DATETIME;
SELECT  @ConsumeOutput1 = MAX( FileDate )
FROM    dbo.DailyTransaction;

SET STATISTICS IO, TIME OFF;
GO

Tempo de análise e compilação do SQL Server: tempo da CPU = 0 ms, tempo decorrido = 4 ms. Tabela 'DailyTransaction_LEGACY'. Contagem de varredura 1, leituras lógicas 11972, leituras físicas 0, leituras de leitura antecipada 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de leitura antecipada de 0. Tabela 'Worktable'. Contagem de varreduras 0, leituras lógicas 0, leituras físicas 0, leituras de leitura antecipada 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de leitura antecipada de 0. Tabela 'Arquivo de Trabalho'. Contagem de varreduras 0, leituras lógicas 0, leituras físicas 0, leituras de leitura antecipada 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de leitura antecipada 0. 0. Tabela 'DailyTransactionComplete'. Contagem de varredura 2, leituras lógicas 620, leituras físicas 0, leituras antecipadas 0, leituras lógicas lob 0, leituras lógicas lob 0, leituras físicas lob 0, leituras antecipadas lob.

Tempos de execução do SQL Server: tempo de CPU = 983 ms, tempo decorrido = 983 ms.

Exibir plano

Ah, entendo, o Sql Server está tentando me dizer que o que estou fazendo é idiota. Embora eu concorde amplamente, isso não muda minha situação. Na verdade, isso funciona de maneira brilhante para consultas FileDateem que a dbo.DailyTransactionexibição está incluída no predicado, mas, embora o MAXplano seja ruim o suficiente, o TOPplano envia tudo para o sul. Sul verdadeiro.

SET STATISTICS IO, TIME ON;

SELECT  TOP 10 FileDate
FROM    dbo.DailyTransaction
GROUP BY FileDate 
ORDER BY FileDate DESC

SET STATISTICS IO, TIME OFF;
GO

Tabela 'DailyTransactionComplete'. Contagem de varredura 2, leituras lógicas 1800110, leituras físicas 0, leituras de leitura antecipada 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de leitura antecipada 0. 0. Tabela 'DailyTransaction_LEGACY'. Contagem de varredura 1, leituras lógicas 1254, leituras físicas 0, leituras de leitura antecipada 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de leitura antecipada de zero 0. Tabela 'Worktable'. Contagem de varreduras 0, leituras lógicas 0, leituras físicas 0, leituras de leitura antecipada 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de leitura antecipada de 0. Tabela 'Arquivo de Trabalho'. Contagem de varreduras 0, leituras lógicas 0, leituras físicas 0, leituras de leitura antecipada 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de leitura antecipada de lob 0.

Tempos de execução do SQL Server: tempo de CPU = 109559 ms, tempo decorrido = 109664 ms.

Topo

Eu mencionei ser "criativo" anteriormente, o que provavelmente foi enganoso. O que eu quis dizer foi "mais estúpido", então minhas tentativas de fazer essa visualização funcionar durante operações de agregação foram criar visualizações nas tabelas dbo.DailyTransactionCompletee dbo.DailyTransaction_LEGACY, ligar o esquema e indexar a última e, em seguida, usá-las em outra visualização com uma NOEXPANDdica na visão herdada. Embora esteja trabalhando mais ou menos para o que precisa fazer por enquanto, considero toda a "solução" bastante perturbadora, culminando com o seguinte:

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'v_DailyTransactionComplete'
                    AND type = 'V' )
BEGIN
    EXEC( 'CREATE VIEW dbo.v_DailyTransactionComplete AS SELECT x = 1;' );
END;
GO

ALTER VIEW dbo.v_DailyTransactionComplete
AS  SELECT  DailyTransaction_PK, FileDate = CONVERT( DATETIME, 
                CONVERT( VARCHAR( 8 ), DateCode_FK ), 112 ), 
            Foo
    FROM    dbo.DailyTransactionComplete;
GO

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'v_DailyTransaction_LEGACY'
                    AND type = 'V' )
BEGIN
    EXEC( 'CREATE VIEW dbo.v_DailyTransaction_LEGACY AS SELECT x = 1;' );
END;
GO

ALTER VIEW dbo.v_DailyTransaction_LEGACY
WITH SCHEMABINDING
AS  SELECT  l.DailyTransaction_PK,
            l.FileDate,
            l.Foo,
            CountBig = COUNT_BIG( * )
    FROM    dbo.DailyTransaction_LEGACY l
    INNER JOIN dbo.DailyTransactionComplete n
        ON  l.FileDate <> CONVERT( DATETIME, CONVERT( VARCHAR( 8 ), 
                n.DateCode_FK ), 112 )
    GROUP BY l.DailyTransaction_PK,
            l.FileDate,
            l.Foo;
GO

CREATE UNIQUE CLUSTERED INDEX CI__v_DailyTransaction_LEGACY
    ON dbo.v_DailyTransaction_LEGACY ( FileDate, DailyTransaction_PK )
WITH ( DATA_COMPRESSION = PAGE, FILLFACTOR = 80 );
GO

IF NOT EXISTS ( SELECT  1
                FROM    sys.objects
                WHERE   name = 'DailyTransaction'
                    AND type = 'V' )
BEGIN
    EXEC( 'CREATE VIEW dbo.DailyTransaction AS SELECT x = 1;' );
END;
GO

ALTER VIEW dbo.DailyTransaction
AS  SELECT  DailyTransaction_PK, FileDate, Foo
    FROM    dbo.v_DailyTransactionComplete
    UNION ALL    
    SELECT  DailyTransaction_PK, FileDate, Foo
    FROM    dbo.v_DailyTransaction_LEGACY WITH ( NOEXPAND );
GO

Forçar o otimizador a usar o índice fornecido pela exibição indexada diminui os problemas MAXe TOP, mas deve haver uma maneira melhor de conseguir o que estou tentando fazer aqui. Absolutamente qualquer sugestão / bronca seria muito apreciada !!

SET STATISTICS IO, TIME ON;

DECLARE @ConsumeOutput1        DATETIME;
SELECT  @ConsumeOutput1 = MAX( FileDate )
FROM    dbo.DailyTransaction;

SET STATISTICS IO, TIME OFF;
GO

Tabela 'v_DailyTransaction_LEGACY'. Contagem de varredura 1, leituras lógicas 3, leituras físicas 0, leituras de read-ahead 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras de lob de leitura antecipada 0. Tabela 'DailyTransactionComplete'. Contagem de varredura 1, leituras lógicas 310, leituras físicas 0, leituras de read-ahead 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de leitura antecipada de lob 0.

Tempos de execução do SQL Server: tempo de CPU = 31 ms, tempo decorrido = 36 ms.

SET STATISTICS IO, TIME ON;

DECLARE @ConsumeOutput1        DATETIME;
SELECT  TOP 10 @ConsumeOutput1 = FileDate
FROM    dbo.DailyTransaction
GROUP BY FileDate 
ORDER BY FileDate DESC

SET STATISTICS IO, TIME OFF;
GO

Tabela 'v_DailyTransaction_LEGACY'. Contagem de varredura 1, leituras lógicas 101, leituras físicas 0, leituras de leitura antecipada 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de leitura antecipada de 0. Tabela 'Worktable'. Contagem de varreduras 0, leituras lógicas 0, leituras físicas 0, leituras de leitura antecipada 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de leitura antecipada de 0. Tabela 'Arquivo de Trabalho'. Contagem de varreduras 0, leituras lógicas 0, leituras físicas 0, leituras de leitura antecipada 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de leitura antecipada 0. 0. Tabela 'DailyTransactionComplete'. Contagem de varredura 1, leituras lógicas 310, leituras físicas 0, leituras de read-ahead 0, leituras lógicas de lob 0, leituras físicas de lob 0, leituras físicas de lob 0, leituras de leitura antecipada de lob 0.

Tempos de execução do SQL Server: tempo de CPU = 63 ms, tempo decorrido = 66 ms.

TL; DR:

Ajude-me a entender o que preciso fazer para que as consultas de agregação na primeira exibição que eu mencionei sejam executadas em quantidades razoáveis ​​de tempo com utilização razoável de recursos de E / S.

Avarkx
fonte
3
Uma visualização não indexada não armazena nenhum dado e você não pode indexar uma visualização com subconsultas, uniões etc. Acho que é necessário materializar os dados de uma maneira diferente, como dividir essa visualização em duas visualizações. e, em seguida, consultando-os ou ignorando completamente a exibição.
Aaron Bertrand
Acho que estou tentando alcançar o que você está sugerindo, mas não entendo totalmente o que preciso fazer. Estou pensando que a visão herdada que consegui indexar e materializar como um band-aid parece ser uma solução bastante grosseira para a limitação das subconsultas e, embora funcione mais ou menos no estado atual, é muito suscetível a problemas adicionais. Oportunista. Estou lutando com a idéia de configurar um processo para preencher uma tabela base totalmente nova após a importação e alterar a exibição para fazer referência a isso.
Avarkx

Respostas:

4

Reescrever NOT EXISTScomo DISTINCTuma junção de desigualdade permite que a exibição seja indexada, mas há boas razões para isso não ser feito com frequência.

O plano de execução gerado para criar o índice na visualização é inevitavelmente horrível. A desigualdade força uma junção física de loops aninhados, que, com exceção de um valor, é uma junção cruzada. O recolhimento do produto com um grupo distinto ou equivalente produzirá os resultados corretos, supondo que a coluna de junção não seja anulável (como no código de exemplo), mas nunca será eficiente. Essa ineficiência só piora com o passar do tempo e as tabelas envolvidas se tornam maiores.

Problemas semelhantes afetam o plano de execução de qualquer instrução DML que afeta uma tabela referenciada pela exibição (porque a exibição deve ser sincronizada com as tabelas base o tempo todo no SQL Server). Veja o plano de execução gerado para adicionar ou modificar uma única linha em qualquer tabela para ver o que quero dizer.

Em um nível alto, o problema que você está enfrentando é que o otimizador de consultas do SQL Server nem sempre gera bons planos sobre as visualizações que incluem a UNION ALL. Muitas das otimizações que tomamos como garantidas (como MAX-> TOP (1)) simplesmente não são implementadas em toda a união.

Para cada problema resolvido, você encontrará outro caso em que uma otimização normal e esperada não ocorre, resultando em um plano de execução com desempenho desesperado. A solução óbvia é evitar o uso de união nas visualizações. Como você implementa isso no seu caso depende de detalhes que, apesar dos detalhes da pergunta, provavelmente são conhecidos apenas por você.

Se você tiver espaço, uma solução é manter completee legacybasear as tabelas separadamente (incluindo a lógica não existe). Isso resulta em duplicação de dados e vem com problemas de sincronização, mas, na minha experiência, eles são muito mais fáceis de resolver com robustez do que tentar obter visualizações de união para gerar bons planos de execução para uma ampla gama de consultas em todas (ou mesmo na maioria) circunstâncias.

O SQL Server fornece vários recursos para ajudar na sincronização de dados, como tenho certeza, incluindo rastreamento de alterações, captura de dados alterados, gatilhos ... e assim por diante. As especificidades da implementação estão além deste fórum. O ponto importante é apresentar o otimizador com tabelas de base, não unir todas as visualizações.

Paul White 9
fonte
Agradecemos a você e a @AaronBertrand por sua contribuição. Suas idéias e sugestões são muito apreciadas! Provavelmente, acabarei migrando manualmente os dados da tabela antiga para a nova tabela, para poder alterar a exibição para não precisar mais se unir da tabela antiga. Teoricamente, eu deveria ser capaz de abandonar completamente a tabela herdada. Haverá outros desafios com essa abordagem, mas, como você mencionou, talvez esses desafios sejam mais gerenciáveis ​​a longo prazo, pois o que estou fazendo obviamente não vai funcionar bem, nunca.
Avarkx