2009-09-09 11 views
0

J'ai le même "problème" que décrit dans (Last record of Join table): Je dois joindre une "table principale" avec un "tableau d'historique" alors que je ne veux que rejoindre le dernier (par date) Enregistrement de la table d'historique. Donc, chaque fois que j'interroge un enregistrement pour la table principale, je trouve aussi les "dernières" données de la table d'historique.Dernier enregistrement d'une table de jointure (comment optimiser)

Master Table 
    ID 
    FIRSTNAME 
    LASTNAME 
    ... 

History Table 
    ID 
    LASTACTION 
    DATE 

Ceci est possible en se joignant à deux tables et en utilisant un sous-sélection pour récupérer le dernier enregistrement de la table d'historique comme décrit dans la réponse donnée dans le lien ci-dessus.

Mes questions sont: Comment puis-je résoudre le problème, qu'il pourrait y avoir en théorie deux historiques avec la même date?

Est-ce que ce genre de jonction avec le sous-réseau est vraiment la meilleure solution en termes de performance (et en général)? Que pensez-vous (je ne suis pas expert dans tous ces domaines) si j'intègre un autre attribut dans la table Historique nommé "ISLATESTRECORD" comme un indicateur booléen que je gère manuellement (et qui a une contrainte unique). Cet attribut sera alors marquer explicitement le dernier enregistrement et je ne pas besoin de sous-requêtes que je peux utiliser directement cet attribut dans la clause where de la jointure. D'un autre côté, cela rend un peu plus compliqué l'insertion d'un nouvel enregistrement: je dois d'abord retirer le drapeau "ISLATESTRECORD" du dernier enregistrement, je dois insérer le nouvel enregistrement d'historique avec l'ensemble "ISLATESTRECORD" et valider la transaction.

Selon vous, quelle est la solution recommandée? Je n'ai pas la moindre idée de l'impact sur les performances des sous-sélections: Je pourrais avoir des millions de « MasterTable » Records « que je dois rechercher un enregistrement spécifique en utilisant également les attributs de recherche de la table d'historique rejoint comme: » Donne-moi la L'enregistrement de table principale avec FIRSTNAME XYZ et LASTACTION (de la table d'historique) était "changed_name". Donc, cette sous-sélection pourrait être appelée des millions de fois.

Ou est-il préférable de travailler avec un sous-ensemble pour trouver le dernier enregistrement, étant donné que les sous-sélectifs sont très efficaces et qu'il est préférable de tout normaliser?

Merci beaucoup

+0

Qu'est-ce que la base de données (y compris. version)? –

Répondre

2

Je résous votre problème avec une requête sur vos tables existantes et sur vos tables avec une colonne d'identité auto-incrémentée ajoutée à la table d'historique. En ajoutant une colonne d'identité auto-incrémentée dans votre table d'historique, vous pouvez contourner le problème unique des dates et faciliter la recherche.

Pour résoudre le problème avec vos tables (avec le code exemple SQL Server):

DECLARE @MasterTable table (MasterID int,FirstName varchar(20),LastName varchar(20)) 
DECLARE @HistoryTable table (MasterID int,LastAction char(1),HistoryDate datetime) 

INSERT INTO @MasterTable VALUES (1,'AAA','aaa') 
INSERT INTO @MasterTable VALUES (2,'BBB','bbb') 
INSERT INTO @MasterTable VALUES (3,'CCC','ccc') 

INSERT INTO @HistoryTable VALUES (1,'I','1/1/2009') 
INSERT INTO @HistoryTable VALUES (1,'U','2/2/2009') 
INSERT INTO @HistoryTable VALUES (1,'U','3/3/2009') --<<dups 
INSERT INTO @HistoryTable VALUES (1,'U','3/3/2009') --<<dups 
INSERT INTO @HistoryTable VALUES (2,'I','5/5/2009') 
INSERT INTO @HistoryTable VALUES (3,'I','7/7/2009') 
INSERT INTO @HistoryTable VALUES (3,'U','8/8/2009') 

SELECT 
    MasterID,FirstName,LastName,LastAction,HistoryDate 
    FROM (SELECT 
       m.MasterID,m.FirstName,m.LastName,h.LastAction,h.HistoryDate,ROW_NUMBER() OVER(PARTITION BY m.MasterID ORDER BY m.MasterID) AS RankValue 
       FROM @MasterTable m 
        INNER JOIN (SELECT 
            MasterID,MAX(HistoryDate) AS MaxDate 
            FROM @HistoryTable 
            GROUP BY MasterID 
          ) dt ON m.MasterID=dt.MasterID 
        INNER JOIN @HistoryTable h ON dt.MasterID=h.MasterID AND dt.MaxDate=h.HistoryDate 
     ) AllRows 
    WHERE RankValue=1 

SORTIE:

MasterID FirstName LastName LastAction HistoryDate 
----------- --------- -------- ---------- ----------- 
1   AAA  aaa  U   2009-03-03 
2   BBB  bbb  I   2009-05-05 
3   CCC  ccc  U   2009-08-08 

(3 row(s) affected) 

Pour résoudre le problème avec un meilleur, HistoryTable (avec SQL Exemple de code de serveur): c'est mieux car il a une colonne d'identité de l'historique de l'auto-incrémentation

DECLARE @MasterTable table (MasterID int,FirstName varchar(20),LastName varchar(20)) 
DECLARE @HistoryTableNEW table (HistoryID int identity(1,1), MasterID int,LastAction char(1),HistoryDate datetime) 

INSERT INTO @MasterTable VALUES (1,'AAA','aaa') 
INSERT INTO @MasterTable VALUES (2,'BBB','bbb') 
INSERT INTO @MasterTable VALUES (3,'CCC','ccc') 

INSERT INTO @HistoryTableNEW VALUES (1,'I','1/1/2009') 
INSERT INTO @HistoryTableNEW VALUES (1,'U','2/2/2009') 
INSERT INTO @HistoryTableNEW VALUES (1,'U','3/3/2009') --<<dups 
INSERT INTO @HistoryTableNEW VALUES (1,'U','3/3/2009') --<<dups 
INSERT INTO @HistoryTableNEW VALUES (2,'I','5/5/2009') 
INSERT INTO @HistoryTableNEW VALUES (3,'I','7/7/2009') 
INSERT INTO @HistoryTableNEW VALUES (3,'U','8/8/2009') 

SELECT 
    m.MasterID,m.FirstName,m.LastName,h.LastAction,h.HistoryDate,h.HistoryID 
    FROM @MasterTable m 
     INNER JOIN (SELECT 
         MasterID,MAX(HistoryID) AS MaxHistoryID 
         FROM @HistoryTableNEW 
         GROUP BY MasterID 
        ) dt ON m.MasterID=dt.MasterID 
     INNER JOIN @HistoryTableNEW h ON dt.MasterID=h.MasterID AND dt.MaxHistoryID=h.HistoryID 

SORTIE:

MasterID FirstName LastName LastAction HistoryDate    HistoryID 
----------- --------- -------- ---------- ----------------------- --------- 
1   AAA  aaa  U   2009-03-03 00:00:00.000 4 
2   BBB  bbb  I   2009-05-05 00:00:00.000 5 
3   CCC  ccc  U   2009-08-08 00:00:00.000 7 

(3 row(s) affected) 
0

Si la table d'historique a une clé primaire (et toutes les tables doivent), vous pouvez modifier le sous-sélection pour extraire le dossier avec soit la plus grande (ou plus petite) valeur PK des multiples qui correspondent aux critères de date ...

Select M.*, H.* 
    From Master M 
    Join History H 
     On H.PK = (Select Max(PK) From History 
        Where FK = M.PK 
         And Date = (Select Max(Date) From History 
            Where FK = M.PK)) 

Quant à la performance, qui peut être résolu en ajoutant les indices appropriés à ces tables (History.Date, History.FK), mais en général, selon les données de table spécifique modèles de distribution, les sous-requêtes peuvent affecter les performances.

Questions connexes