Les opérations d'agrégation sur la vue ignorent l'index [fermé]


8

Le scénario

Il était une fois une base de données Staging dans une petite entreprise qui participait à un processus ETL, agissant comme un catalogue de réception pour les différents formats de fichiers provenant d'un certain nombre de sources tierces. L'E a été géré par le biais de packages DTS, avec peu de structures de contrôle pour l'audit ou le contrôle, mais a été jugé «assez bon» et à toutes fins utiles, il l'était.

Les données fournies par la partie E étaient destinées à être utilisées par une application unique, développée et gérée par une poignée de programmeurs jeunes et compétents. Bien que manquant d'expérience ou de connaissances des techniques d'entreposage de données de l'époque, ils ont exposé et créé leurs propres processus T et L à partir du code d'application. Fulgurant, ces ingénieurs logiciels novices ont inventé ce que les étrangers pourraient appeler une "roue moins qu'idéale", mais avec "Good Enough" comme niveau de service toujours présent, ils ont pu fournir un cadre opérationnel.

Pendant un temps, tout allait bien dans le domaine étroitement couplé, le catalogue Staging se nourrissant des données d'une douzaine de tiers, à son tour alimenté par l'application. Au fur et à mesure que l'application grandissait, ses appétits augmentaient également, mais avec les développeurs habiles du chevalier blanc qui surveillaient le système, ces appétits ont été traités rapidement et, dans de nombreux cas, même bien.

Mais l'âge d'or ne pouvait pas durer éternellement, bien sûr. Avec la prospérité accordée par l'application réussie, l'entreprise a grandi et grandi. Au fur et à mesure de sa croissance, l'environnement et l'application Staging ont été contraints de croître avec lui. Malgré toute leur vigilance, la seule poignée de développeurs de héros n'a pas pu suivre le maintien du système désormais étendu, et les consommateurs avaient désormais droit à leurs données. Ce n'était plus une question de ce dont ils avaient besoin ou même voulu, mais la population estimait qu'ils le méritaient simplement, exigeant encore plus.

Armée d'un peu plus que des coffres remplis de butin, l'entreprise a pénétré le marché en embauchant des développeurs et des administrateurs pour aider à soutenir le système en constante croissance. Des mercenaires de tous les horizons affluent vers l'entreprise, mais avec cette poussée de croissance, peu de conseils d'experts sont disponibles. Les nouveaux développeurs et administrateurs ont eu du mal à comprendre les subtilités de la suite maison, jusqu'à ce que les frustrations entraînent une guerre totale. Chaque département a commencé à tenter de résoudre chaque problème seul, en faisant plus pour travailler les uns contre les autres que travailler les uns avec les autres. Un seul projet ou initiative serait mis en œuvre de plusieurs manières différentes, chacune légèrement différente de la suivante. La tension de tout cela s'est avérée être trop pour certains des chevaliers blancs et comme ils sont tombés, l'empire s'est effondré. Bientôt, le système était en ruine,

Malgré la transformation de ces domaines de promesse en code de spaghetti sanglant, l'entreprise a perduré. C'était, après tout, «assez bon».

Le défi

Quelques changements de régime et embauches plus tard, je me retrouve dans l'emploi de l'entreprise. Cela fait de nombreuses années depuis les grandes guerres, mais les dégâts causés sont encore très visibles. J'ai réussi à corriger certaines des faiblesses de la partie E du système et à ajouter des tables de contrôle sous le prétexte de mettre à niveau les packages DTS vers SSIS, qui sont maintenant utilisés par certains professionnels de l'entreposage de données car ils créent un environnement normal et remplacement de T et L documenté.

Le premier obstacle était d'importer les données à partir des fichiers tiers d'une manière qui ne tronquerait pas les valeurs ou ne changerait pas les types de données natifs, mais inclurait également des clés de contrôle pour les rechargements et les purges. Tout allait bien, mais les applications devaient pouvoir accéder à ces nouvelles tables de manière transparente et transparente. Un package DTS peut remplir une table, qui est ensuite directement lue par l'application. Les mises à niveau SSIS doivent être effectuées en parallèle pour des raisons d'assurance qualité, mais ces nouveaux packages incluent diverses clés de contrôle et tirent également parti d'un schéma de partitionnement, sans oublier que les changements de métadonnées réels peuvent à eux seuls être suffisamment importants pour justifier une nouvelle table de toute façon, donc un une nouvelle table a été utilisée pour les nouveaux packages SSIS.

Les importations de données fiables étant désormais opérationnelles et utilisées par l'équipe d'entreposage, le véritable défi consiste à servir les nouvelles données aux applications qui accèdent directement à l'environnement de transfert, avec un impact minimal (alias "Non") sur le code de l'application. Pour cela, j'ai choisi de vue de l' utilisation, de renommer une table par exemple dbo.DailyTransactionpour dbo.DailyTranscation_LEGACYet réutiliser le dbo.DailyTransactionnom de l' objet pour une vue, qui a effectivement tout sélectionne tout le maintenantLEGACYtable désignée. Étant donné que le rechargement des années de données contenues dans ces tables n'est pas une option du point de vue de l'entreprise, alors que les nouvelles tables SSIS peuplées et partitionnées entrent en production, les anciennes importations DTS sont désactivées et les applications doivent pouvoir accéder également aux nouvelles données dans les nouvelles tables. À ce stade, les vues sont mises à jour pour sélectionner les données des nouvelles tables ( dbo.DailyTransactionCompletepar exemple, par exemple) lorsqu'elles sont disponibles et les sélectionner dans les tables héritées lorsqu'elles ne le sont pas.

En fait, quelque chose comme ce qui suit est en cours:

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

Bien que logique, cela ne fonctionne pas du tout dans un certain nombre de cas d'agrégation, ce qui entraîne généralement un plan d'exécution qui effectue une analyse complète de l'index par rapport aux données de la table héritée. C'est probablement bien pour quelques dizaines de millions d'enregistrements, mais pas tant pour quelques dizaines de centaines de millions d'enregistrements. Étant donné que ce dernier est en fait le cas, j'ai dû recourir à être ... "créatif", ce qui m'a conduit à créer une vue indexée.

Voici le petit cas de test que j'ai mis en place, y compris la FileDateclé de contrôle ayant été portée sur le DateCode_FKport compatible de Data Warehouse pour illustrer à quel point je me soucie peu des requêtes contre la nouvelle table pouvant être discutées pour le moment:

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

Sur mon sandbox local, ce qui précède me donne une table héritée avec environ 4,4 millions de lignes et une nouvelle table contenant 0,1 million de lignes, avec un certain chevauchement des valeurs DateCode_FK/ FileDate.

Une MAX( FileDate )contre la table héritée sans index supplémentaire fonctionne à peu près à quoi je m'attendrais.

SET STATISTICS IO, TIME ON;

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

SET STATISTICS IO, TIME OFF;
GO

Tableau 'DailyTransaction_LEGACY'. Nombre de balayages 1, lectures logiques 9228, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0.

Temps d'exécution SQL Server: temps CPU = 889 ms, temps écoulé = 886 ms.

Index clusterisé, hérité

Lancer un simple index sur la table rend les choses bien meilleures. Encore une analyse, mais une analyse d'un enregistrement au lieu des 4,4 millions d'enregistrements. Je suis cool avec ça.

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

Temps d'analyse et de compilation SQL Server: temps CPU = 0 ms, temps écoulé = 1 ms. Tableau 'DailyTransaction_LEGACY'. Nombre de balayages 1, lectures logiques 3, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0.

Temps d'exécution SQL Server: temps CPU = 0 ms, temps écoulé = 0 ms.

Index non clusterisé, hérité

Et maintenant, créer la vue pour que les développeurs n'aient pas à changer de code car ce serait apparemment la fin du monde tel que nous le connaissons. Un cataclysme en quelque sorte.

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

Oui, la sous-requête est épouvantable, mais ce n'est pas le problème et je vais probablement simplement créer une colonne calculée persistante et y jeter un index à cette fin lorsque le vrai problème sera résolu. Alors sans plus tarder,

Le problème

SET STATISTICS IO, TIME ON;

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

SET STATISTICS IO, TIME OFF;
GO

Temps d'analyse et de compilation SQL Server: temps CPU = 0 ms, temps écoulé = 4 ms. Tableau 'DailyTransaction_LEGACY'. Nombre de balayages 1, lectures logiques 11972, lectures physiques 0, lectures anticipées 0, lob lectures logiques 0, lob physiques lectures 0, lob lectures anticipées lisent 0. Tableau 'Table de travail'. Nombre de balayages 0, lectures logiques 0, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob physiques lectures 0, lob lectures anticipées 0. Tableau 'Workfile'. Nombre de balayages 0, lectures logiques 0, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0. Tableau 'DailyTransactionComplete'. Nombre de balayages 2, lectures logiques 620, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0.

Temps d'exécution SQL Server: temps CPU = 983 ms, temps écoulé = 983 ms.

Voir le plan

Oh je vois, Sql Server essaie de me dire que ce que je fais est idiot. Bien que je sois largement d'accord, cela ne change pas ma situation. Cela fonctionne en fait brillamment pour les requêtes où la vue FileDatesur la dbo.DailyTransactionvue est incluse dans le prédicat, mais bien que le MAXplan soit suffisamment mauvais, le TOPplan envoie le tout vers le sud. Vrai 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

Tableau 'DailyTransactionComplete'. Nombre de balayages 2, lectures logiques 1800110, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0. Tableau 'DailyTransaction_LEGACY'. Nombre de balayages 1, lectures logiques 1254, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0. Tableau 'Table de travail'. Nombre de balayages 0, lectures logiques 0, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob physiques lectures 0, lob lectures anticipées 0. Tableau 'Workfile'. Nombre de balayages 0, lectures logiques 0, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0.

Temps d'exécution SQL Server: temps CPU = 109559 ms, temps écoulé = 109664 ms.

Haut

J'ai mentionné plus tôt que j'étais "créatif", ce qui était probablement trompeur. Ce que je voulais dire était "plus stupide", donc mes tentatives pour faire fonctionner cette vue pendant les opérations d'agrégation ont été de créer des vues sur les tables dbo.DailyTransactionCompleteet dbo.DailyTransaction_LEGACY, de lier le schéma et d'indexer cette dernière, puis d'utiliser ces vues dans une autre vue avec un NOEXPANDindice sur la vue héritée. Bien qu'il fonctionne plus ou moins pour ce qu'il doit faire pour l'instant, je trouve que toute la "solution" est assez bouleversante, culminant avec ce qui suit:

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

Forcer l'optimiseur à utiliser l'index fourni par la vue indexée fait disparaître les problèmes MAXet TOP, mais il doit y avoir un meilleur moyen de réaliser ce que j'essaie de faire ici. Absolument toute suggestion / réprimande serait très appréciée !!

SET STATISTICS IO, TIME ON;

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

SET STATISTICS IO, TIME OFF;
GO

Tableau 'v_DailyTransaction_LEGACY'. Nombre de balayages 1, lectures logiques 3, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0. Tableau 'DailyTransactionComplete'. Nombre de balayages 1, lectures logiques 310, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0.

Temps d'exécution SQL Server: temps CPU = 31 ms, temps écoulé = 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

Tableau 'v_DailyTransaction_LEGACY'. Nombre de balayages 1, lectures logiques 101, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0. Tableau 'Table de travail'. Nombre de balayages 0, lectures logiques 0, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob physiques lectures 0, lob lectures anticipées 0. Tableau 'Workfile'. Nombre de balayages 0, lectures logiques 0, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0. Tableau 'DailyTransactionComplete'. Nombre de balayages 1, lectures logiques 310, lectures physiques 0, lectures anticipées 0, lectures logiques 0, lob lectures physiques 0, lob lectures anticipées 0.

Temps d'exécution SQL Server: temps CPU = 63 ms, temps écoulé = 66 ms.

TL; DR:

Aidez-moi à comprendre ce que je dois faire pour que les requêtes d'agrégation sur la première vue que j'ai mentionnée s'exécutent dans des délais raisonnables avec une utilisation raisonnable des ressources d'E / S.


3
Une vue non indexée ne stocke aucune donnée, et vous ne pouvez pas indexer une vue avec des sous-requêtes, des unions, etc. Je pense que vous devez considérer la matérialisation des données d'une manière différente, comme la division de cette vue en deux vues. puis les interroger ou contourner complètement la vue.
Aaron Bertrand

Je pense que j'essaie de réaliser ce que vous proposez, mais je ne saisis pas complètement ce que je dois faire. Je pense que la vue héritée que j'ai réussi à indexer et à matérialiser comme un pansement semble être une solution de contournement assez brute à la limitation des sous-requêtes, et bien qu'elle fonctionne plus ou moins dans l'état actuel, elle est très sensible aux fluage portée. Je me débat avec l'idée de mettre en place un processus pour remplir une toute nouvelle table de base après une importation et de modifier la vue pour y faire référence.
Avarkx

Réponses:


4

La réécriture en NOT EXISTStant que DISTINCTsur une jointure d'inégalité permet d'indexer la vue, mais il y a de bonnes raisons que cela ne soit pas courant.

Le plan d'exécution généré pour construire l'index sur la vue est inévitablement horrible. L'inégalité force une jointure physique de boucles imbriquées, qui, à l'exception d'une valeur, est une jointure croisée. Réduire le produit avec un groupe distinct ou équivalent par produira les résultats corrects, en supposant que la colonne de jointure n'est pas annulable (comme dans l'exemple de code), mais elle ne sera jamais efficace. Cette inefficacité ne fera que s'aggraver avec le temps et les tables impliquées deviennent plus grandes.

Des problèmes similaires affectent le plan d'exécution de toute instruction DML qui affecte une table référencée par la vue (car la vue doit être synchronisée à tout moment avec les tables de base dans SQL Server). Regardez le plan d'exécution généré pour ajouter ou modifier une seule ligne dans les deux tableaux pour voir ce que je veux dire.

À un niveau élevé, le problème que vous combattez est que l'optimiseur de requêtes SQL Server ne génère pas toujours de bons plans sur les vues qui incluent a UNION ALL. De nombreuses optimisations que nous tenons pour acquises (comme MAX-> TOP (1)) ne sont tout simplement pas mises en œuvre dans tous les syndicats.

Pour chaque problème que vous résolvez, vous trouverez un autre cas où une optimisation normale et attendue ne se produit pas, résultant en un plan d'exécution aux performances désespérées. La solution évidente est d'éviter d'utiliser l'union dans les vues. La façon dont vous implémentez cela dans votre cas dépend de détails qui, malgré les détails de la question, ne sont probablement connus que de vous.

Si vous avez de l'espace, une solution consiste à maintenir completeet à legacybaser les tables séparément (y compris la logique qui n'existe pas). Cela entraîne une duplication des données et entraîne des problèmes de synchronisation, mais d'après mon expérience, ils sont beaucoup plus faciles à résoudre de manière robuste que d'essayer d'obtenir des vues d'union pour générer de bons plans d'exécution pour un large éventail de requêtes dans toutes (voire la plupart) des circonstances.

SQL Server fournit un certain nombre de fonctionnalités pour aider à la synchronisation des données, comme je suis sûr que vous le savez, y compris le suivi des modifications, la capture des données modifiées, les déclencheurs ... et ainsi de suite. Les spécificités de la mise en œuvre dépassent ce forum. L'important est de présenter l'optimiseur avec des tables de base, et non de réunir toutes les vues.


Merci à vous et à @AaronBertrand pour votre contribution. Vos idées et suggestions sont très appréciées! Je vais probablement finir par migrer manuellement les anciennes données de la table vers la nouvelle table afin de pouvoir modifier la vue pour ne plus avoir besoin de l'union à partir de l'ancienne table. Théoriquement, je devrais alors être en mesure de supprimer complètement la table héritée. Il y aura d'autres défis avec cette approche, mais comme vous l'avez mentionné, peut-être que ces défis seront plus faciles à gérer à long terme, car ce que je fais ne fonctionnera évidemment pas bien, jamais.
Avarkx
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.