Aggregation Operations on View Ignora l'indice [chiuso]


8

Lo scenario

C'era una volta un database di gestione temporanea presso una piccola azienda che partecipava a un processo ETL, fungendo da catalogo di ricezione per i vari formati di file da una serie di fonti di terze parti. L'E è stata gestita attraverso pacchetti DTS, con poche strutture di controllo per l'auditing o il controllo, ma è stata considerata "abbastanza buona" e, a tutti gli effetti, lo è stata.

I dati forniti dalla porzione E erano destinati al consumo da un'unica applicazione, sviluppata e gestita da una manciata di programmatori giovani e capaci. Sebbene non avessero esperienza o conoscenza delle tecniche di data warehousing dell'epoca, hanno avviato e creato i propri processi T e L dal codice dell'applicazione. Sorprendentemente, questi ingegneri software alle prime armi hanno inventato quella che gli estranei potrebbero definire una "ruota tutt'altro che ideale", ma con "Abbastanza buono" come un livello di servizio sempre presente, sono stati in grado di fornire un quadro operativo.

Per un po ', tutto andò bene nel regno strettamente accoppiato, con il catalogo Staging che si dilettava con i dati di una dozzina di terze parti, a sua volta alimentata dall'applicazione. Man mano che l'applicazione cresceva, aumentavano anche i suoi appetiti, ma con gli abili sviluppatori del cavaliere bianco che sorvegliavano il sistema, questi appetiti venivano affrontati rapidamente e in molti casi, anche bene.

Ma l'età d'oro non poteva durare per sempre, ovviamente. Con la prosperità garantita dall'applicazione di successo, il business è cresciuto e cresciuto. Man mano che cresceva, l'ambiente di gestione temporanea e l'applicazione erano costretti a crescere con esso. Nonostante tutta la loro vigilanza, il solo pugno di sviluppatori di eroi non ha potuto tenere il passo con il mantenimento del sistema ora espansivo, e i consumatori erano diventati titolari dei loro dati. Non era più una questione di ciò di cui avevano bisogno o addirittura desideravano, ma la popolazione sentiva che se lo meritava semplicemente, chiedendo ancora di più.

Armato di poco più di casse piene di malloppo, l'azienda ha raggiunto il mercato, assumendo sviluppatori e amministratori per aiutare a supportare il sistema in continua crescita. Mercenari di ogni genere si affollarono nell'azienda, ma con questo scatto di crescita arrivò poco in termini di guida esperta disponibile. I nuovi sviluppatori e amministratori hanno faticato a comprendere le complessità della suite prodotta in casa, fino a quando le frustrazioni non hanno portato alla guerra. Ogni dipartimento ha iniziato a tentare di risolvere da solo ogni problema, facendo di più per lavorare l'uno contro l'altro che per lavorare l'uno con l'altro. Un singolo progetto o iniziativa sarebbe attuato in diversi modi, ognuno leggermente diverso dal successivo. La tensione di tutto ciò si rivelò eccessiva per alcuni cavalieri bianchi e mentre cadevano, l'impero si sbriciolò. Presto il sistema era in rovina,

Nonostante la trasformazione di questi campi promettenti in codice spaghetti spaghetti, l'azienda ha resistito. Dopotutto, era "abbastanza buono".

La sfida

Qualche altro cambio di regime e assunzioni folli in seguito, mi ritrovo nell'impiego dell'azienda. Sono passati molti anni dalle grandi guerre, ma il danno fatto è ancora molto visibile. Sono riuscito a risolvere alcuni dei punti deboli nella parte E del sistema e ad aggiungere alcune tabelle di controllo con il pretesto di aggiornare i pacchetti DTS a SSIS, che ora vengono utilizzati da alcuni professionisti di data warehousing mentre creano un normale e documentata sostituzione T e L.

Il primo ostacolo era importare i dati dai file di terze parti in modo da non troncare i valori o modificare i tipi di dati nativi, ma includere anche alcune chiavi di controllo per ricariche ed eliminazioni. Tutto ciò andava bene, ma le applicazioni dovevano poter accedere a questi nuovi tavoli in modo trasparente e trasparente. Un pacchetto DTS può popolare una tabella, che viene quindi letta direttamente dall'applicazione. Gli aggiornamenti SSIS devono essere eseguiti in parallelo per motivi di QA, ma questi nuovi pacchetti includono varie chiavi di controllo e sfruttano anche uno schema di partizionamento, per non parlare delle sole modifiche ai metadati da sole possono essere abbastanza significative da garantire comunque una nuova tabella, quindi un nuova tabella è stata utilizzata per i nuovi pacchetti SSIS.

Con le importazioni di dati affidabili ora funzionanti e utilizzate dal team di magazzino, la vera sfida consiste nel fornire i nuovi dati alle applicazioni che accedono direttamente all'ambiente di gestione temporanea, con un impatto minimo (noto anche come "No") sul codice dell'applicazione. Per questo, ho deciso di vista di utilizzo, la ridenominazione di un tavolo, come dbo.DailyTransactionper dbo.DailyTranscation_LEGACYe riutilizzare il dbo.DailyTransactionnome dell'oggetto per una vista, che di fatto appena seleziona tutto dalla societàLEGACYtabella designata. Dal momento che ricaricare gli anni di dati contenuti in queste tabelle non è un'opzione dal punto di vista aziendale, poiché le nuove tabelle popolate e partizionate da SSIS si fanno strada nella produzione, le vecchie importazioni DTS vengono disattivate e le applicazioni devono essere in grado di accedere ai nuovi dati anche nelle nuove tabelle. A questo punto, le viste vengono aggiornate per selezionare i dati dalle nuove tabelle (ad esempio dbo.DailyTransactionComplete, ad esempio) quando sono disponibili e selezionare dalle tabelle legacy quando non lo sono.

In effetti, si sta eseguendo qualcosa di simile al seguente:

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 );

Sebbene logicamente corretto, ciò non funziona affatto in numerosi casi di aggregazione, generando in genere un piano di esecuzione che esegue una scansione completa dell'indice rispetto ai dati nella tabella legacy. Questo probabilmente va bene per qualche dozzina di milioni di dischi, ma non tanto per qualche dozzina di milioni di dischi. Dato che quest'ultimo è in realtà il caso, ho dovuto ricorrere all'essere ... "creativo", portandomi a creare una vista indicizzata.

Ecco il piccolo caso di test che ho impostato, incluso il fatto che la FileDatechiave di controllo sia stata trasferita alla DateCode_FKporta compatibile con Data Warehouse per illustrare quanto poco mi preoccupi del fatto che le query sulla nuova tabella siano per il momento estese :

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

Sulla mia sandbox locale, quanto sopra mi dà una tabella legacy con circa 4,4 milioni di righe e una nuova tabella contenente 0,1 milioni di righe, con qualche sovrapposizione dei valori DateCode_FK/ FileDate.

A MAX( FileDate )contro la tabella legacy senza indici aggiuntivi si imbatte in ciò che mi aspetterei.

SET STATISTICS IO, TIME ON;

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

SET STATISTICS IO, TIME OFF;
GO

Tabella "DailyTransaction_LEGACY". Conteggio scansioni 1, letture logiche 9228, letture fisiche 0, letture read-ahead 0, letture log lob 0, letture fisiche lob 0, letture read lob 0.

Tempi di esecuzione di SQL Server: tempo CPU = 889 ms, tempo trascorso = 886 ms.

Indice cluster, eredità

Lanciare un semplice indice sul tavolo rende le cose molto migliori. Ancora una scansione, ma una scansione di un record invece dei 4,4 milioni di record. Sono a posto con quello.

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 di analisi e compilazione di SQL Server: tempo CPU = 0 ms, tempo trascorso = 1 ms. Tabella "DailyTransaction_LEGACY". Conteggio scansioni 1, letture logiche 3, letture fisiche 0, letture avanti 0, letture logiche lob 0, letture fisiche lob 0, letture read lob 0.

Tempi di esecuzione di SQL Server: tempo CPU = 0 ms, tempo trascorso = 0 ms.

Indice non cluster, Legacy

E ora, creando la vista in modo che gli sviluppatori non debbano cambiare alcun codice perché apparentemente sarebbe la fine del mondo come la conosciamo. Una specie di cataclisma.

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

Sì, la query secondaria è abissale, ma questo non è il problema e probabilmente creerò semplicemente una colonna calcolata persistente e vi lancerò un indice a tale scopo quando il problema reale sarà risolto. Quindi senza ulteriori indugi,

Il problema

SET STATISTICS IO, TIME ON;

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

SET STATISTICS IO, TIME OFF;
GO

Tempo di analisi e compilazione di SQL Server: tempo CPU = 0 ms, tempo trascorso = 4 ms. Tabella "DailyTransaction_LEGACY". Conteggio scansioni 1, letture logiche 11972, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read lob iniziali 0. Tabella 'Worktable'. Conteggio scansioni 0, letture logiche 0, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read lob iniziali 0. Tabella "File di lavoro". Conteggio scansioni 0, letture logiche 0, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read lob iniziali 0. Tabella 'DailyTransactionComplete'. Conteggio scansioni 2, letture logiche 620, letture fisiche 0, letture avanti 0, letture logiche lob 0, letture fisiche lob 0, letture read lob 0.

Tempi di esecuzione di SQL Server: tempo CPU = 983 ms, tempo trascorso = 983 ms.

Visualizza piano

Oh capisco, SQL Server sta cercando di dirmi che quello che sto facendo è un idiota. Mentre sono in gran parte d'accordo, ciò non cambia la mia situazione. Questo in realtà funziona brillantemente per le query in cui l' FileDatesulla dbo.DailyTransactionvista è incluso nel predicato, ma mentre il MAXpiano è già abbastanza grave, il TOPpiano di invia il tutto in esecuzione sud. Vero sud.

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

Tabella 'DailyTransactionComplete'. Conteggio scansioni 2, letture logiche 1800110, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read lob iniziali 0. Tabella 'DailyTransaction_LEGACY'. Conteggio scansioni 1, letture logiche 1254, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read avanti lob 0. Tabella "Tavolo da lavoro". Conteggio scansioni 0, letture logiche 0, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read lob iniziali 0. Tabella "File di lavoro". Conteggio scansioni 0, letture logiche 0, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read lob 0.

Tempi di esecuzione di SQL Server: tempo CPU = 109559 ms, tempo trascorso = 109664 ms.

Superiore

Ho accennato a diventare "creativo" in precedenza, il che probabilmente era fuorviante. Quello che intendevo dire era "più stupido", quindi i miei tentativi di far funzionare questa vista durante le operazioni di aggregazione sono stati quello di creare viste sulle tabelle dbo.DailyTransactionCompletee dbo.DailyTransaction_LEGACY, legare lo schema e indicizzare quest'ultima, quindi usare quelle vista in un'altra vista con un NOEXPANDsuggerimento nella vista legacy. Mentre funziona più o meno per quello che deve fare per ora, trovo che l'intera "soluzione" sia abbastanza sconvolgente, culminando con il seguente:

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

Forzare l'ottimizzatore a utilizzare l'indice fornito dalla vista indicizzata fa sì che i problemi MAXe TOPscompaiano, ma deve esserci un modo migliore per ottenere ciò che sto cercando di fare qui. Assolutamente ogni suggerimento / rimprovero sarebbe molto apprezzato !!

SET STATISTICS IO, TIME ON;

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

SET STATISTICS IO, TIME OFF;
GO

Tabella "v_DailyTransaction_LEGACY". Conteggio scansioni 1, letture logiche 3, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read lob iniziali 0. Tabella 'DailyTransactionComplete'. Conteggio scansioni 1, letture logiche 310, letture fisiche 0, letture read-ahead 0, letture log lob 0, letture fisiche lob 0, letture read lob 0.

Tempi di esecuzione di SQL Server: tempo CPU = 31 ms, tempo trascorso = 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

Tabella "v_DailyTransaction_LEGACY". Conteggio scansioni 1, letture logiche 101, letture fisiche 0, letture read-ahead 0, letture log lob 0, letture fisiche lob 0, letture read lob 0. Tabella "Tavolo da lavoro". Conteggio scansioni 0, letture logiche 0, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read lob iniziali 0. Tabella "File di lavoro". Conteggio scansioni 0, letture logiche 0, letture fisiche 0, letture read-ahead 0, letture logiche lob 0, letture fisiche lob 0, letture read lob iniziali 0. Tabella 'DailyTransactionComplete'. Conteggio scansioni 1, letture logiche 310, letture fisiche 0, letture read-ahead 0, letture log lob 0, letture fisiche lob 0, letture read lob 0.

Tempi di esecuzione di SQL Server: tempo CPU = 63 ms, tempo trascorso = 66 ms.

TL; DR:

Aiutami a capire cosa devo fare per fare query di aggregazione nella prima vista che ho menzionato eseguite in un ragionevole lasso di tempo con un ragionevole utilizzo delle risorse I / O.


3
Una vista non indicizzata non memorizza alcun dato e non puoi indicizzare una vista con sottoquery, sindacati, ecc. Penso che devi considerare di materializzare i dati in un modo diverso, come dividere quella vista in due viste e quindi interrogare contro di loro o ignorare del tutto la vista.
Aaron Bertrand

Penso che sto cercando di ottenere ciò che stai suggerendo, ma non sto comprendendo appieno quello che devo fare. Sto pensando che la vista legacy che sono riuscito a indicizzare e materializzare come un cerotto sembra essere una soluzione piuttosto grossolana alla limitazione delle sottoquery e, sebbene funzioni più o meno allo stato attuale, è molto suscettibile ad ulteriori creep di portata. Sto lottando con l'idea di impostare un processo per popolare una tabella di base completamente nuova dopo l'importazione e modificare la vista per fare riferimento a ciò.
Avarkx,

Risposte:


4

Riscrivere NOT EXISTScome DISTINCTsu un join di disuguaglianza consente l'indicizzazione della vista, ma ci sono buone ragioni per cui ciò non viene fatto comunemente.

Il piano di esecuzione generato per costruire l'indice sulla vista è inevitabilmente orribile. La disuguaglianza forza un join fisico di cicli nidificati, che ad eccezione di un valore, è un join incrociato. La compressione del prodotto con un gruppo distinto o equivalente produrrà i risultati corretti, supponendo che la colonna di join non sia nulla (come nel codice di esempio), ma non sarà mai efficiente. Questa inefficienza peggiorerà solo col passare del tempo e le tabelle coinvolte diventano più grandi.

Problemi simili riguardano il piano di esecuzione per qualsiasi istruzione DML che influisce su una tabella a cui fa riferimento la vista (poiché la vista deve essere sempre sincronizzata con le tabelle di base in SQL Server). Guarda il piano di esecuzione generato per aggiungere o modificare una singola riga in entrambe le tabelle per vedere cosa intendo.

A un livello elevato, il problema che si sta affrontando è che lo Strumento per ottimizzare le query di SQL Server non sempre genera buoni piani su viste che includono a UNION ALL. Molte delle ottimizzazioni che diamo per scontate (come MAX-> TOP (1)) semplicemente non sono implementate in tutto il sindacato.

Per ogni problema che risolvi, troverai un altro caso in cui non si verifica un'ottimizzazione normale e prevista, che si traduce in un piano di esecuzione con prestazioni disperate. La soluzione ovvia è evitare di usare l'unione nelle viste. Il modo in cui lo implementate nel vostro caso dipende da dettagli che, nonostante i dettagli nella domanda, sono probabilmente noti solo a voi.

Se hai lo spazio, una soluzione è quella di mantenere completee legacybasare le tabelle separatamente (inclusa la logica inesistente). Ciò comporta la duplicazione dei dati e presenta problemi di sincronizzazione, ma nella mia esperienza questi sono molto più facili da risolvere in modo robusto rispetto al tentativo di ottenere viste sindacali per generare buoni piani di esecuzione per una vasta gamma di query in tutte (o anche nella maggior parte) circostanze.

SQL Server offre una serie di funzionalità per facilitare la sincronizzazione dei dati, come sono sicuro che tu sappia, tra cui il rilevamento delle modifiche, l'acquisizione dei dati delle modifiche, i trigger ... e così via. Le specifiche dell'implementazione vanno oltre questo forum. Il punto importante è presentare l'ottimizzatore con le tabelle di base, non unire tutte le viste.


Grazie a te e @AaronBertrand per il tuo contributo. I tuoi approfondimenti e suggerimenti sono molto apprezzati! Probabilmente finirò per migrare manualmente i dati della vecchia tabella nella nuova tabella in modo da poter modificare la vista in modo da non dover più unire la unione dalla vecchia tabella. Teoricamente, dovrei essere in grado di abbandonare del tutto il tavolo legacy. Ci saranno altre sfide con questo approccio, ma come hai già detto, forse quelle sfide saranno più gestibili a lungo termine poiché quello che sto facendo ovviamente non funzionerà bene, mai.
Avarkx,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.