J'ai besoin d'obtenir les données en deux parents> Ensembles de table enfant fusionnés / combinés dans une tiers parent> table enfant.
Les tables ressemblent à ceci: p>
P>
La seule différence dans les trois ensembles de tables est que TableC a une colonne code> TableType > pour discerner la différence entre un enregistrement de table et un enregistrement de tableb. P>
Ma première pensée était d'utiliser un curseur. Voici le code pour créer la structure de la table, insérer des enregistrements, puis fusionner les données ensemble. Cela fonctionne très bien, SOOOOO .... P>
DROP TABLE TableAChild DROP TABLE TableBChild DROP TABLE TableCChild DROP TABLE TableA DROP TABLE TableB DROP TABLE TableC
enfin, si vous souhaitez réexécuter la requête ci-dessus, voici un petit script pour supprimer les tables créées. P>
--Create the tables CREATE TABLE TableA ( ID int not null identity primary key, Name VARCHAR(30) ); CREATE TABLE TableAChild ( ID int not null identity primary key, Parent int not null, Name VARCHAR(30), CONSTRAINT FK_A FOREIGN KEY (Parent) REFERENCES TableA(ID) ); CREATE TABLE TableB ( ID int not null identity primary key, Name VARCHAR(30) ); CREATE TABLE TableBChild ( ID int not null identity primary key, Parent int not null, Name VARCHAR(30), CONSTRAINT FK_B FOREIGN KEY (Parent) REFERENCES TableB(ID) ); CREATE TABLE TableC ( ID int not null identity primary key, TableType VARCHAR(1), Name VARCHAR(30) ); CREATE TABLE TableCChild ( ID int not null identity primary key, Parent int not null, Name VARCHAR(30), CONSTRAINT FK_C FOREIGN KEY (Parent) REFERENCES TableC(ID) ); -- Insert some test records.. INSERT INTO TableA (Name) Values ('A1') INSERT INTO TableAChild (Name, Parent) VALUES ('A1Child', SCOPE_IDENTITY()) INSERT INTO TableB (Name) Values ('B1') INSERT INTO TableBChild (Name, Parent) VALUES ('B1Child', SCOPE_IDENTITY()) -- Needed throughout.. DECLARE @ID INT -- Merge TableA and TableAChild into TableC and TableCChild DECLARE TableACursor CURSOR -- Get the primary key from TableA FOR SELECT ID FROM TableA OPEN TableACursor FETCH NEXT FROM TableACursor INTO @ID WHILE @@FETCH_STATUS = 0 BEGIN -- INSERT INTO SELECT the parent record into TableC, being sure to specify a TableType INSERT INTO TableC (Name, TableType) SELECT Name, 'A' FROM TableA WHERE ID = @ID -- INSERT INTO SELECT the child record into TableCChild using the parent ID of the last row inserted (SCOPE_IDENTITY()) -- and the current record from the cursor (@ID). INSERT INTO TableCChild(Name, Parent) SELECT Name, SCOPE_IDENTITY() FROM TableAChild WHERE Parent = @ID FETCH NEXT FROM TableACursor INTO @ID END; CLOSE TableACursor DEALLOCATE TableACursor -- Repeat for TableB DECLARE TableBCursor CURSOR FOR SELECT ID FROM TableB OPEN TableBCursor FETCH NEXT FROM TableBCursor INTO @ID WHILE @@FETCH_STATUS = 0 BEGIN INSERT INTO TableC (Name, TableType) SELECT Name, 'B' FROM TableB WHERE ID = @ID INSERT INTO TableCChild(Name, Parent) SELECT Name, SCOPE_IDENTITY() FROM TableBChild WHERE Parent = @ID FETCH NEXT FROM TableBCursor INTO @ID END; CLOSE TableBCursor DEALLOCATE TableBCursor
6 Réponses :
Vous pouvez utiliser une table Dans mon exemple, j'utilise l'ordre d'insertion dans requête d'échantillon strong> p> mappe code> pour relier les ID anciens et les nouveaux identifiants en fonction de certaines touches.
NAPP code>. p>
Table de table CODE> basé sur l'ordre de
ID code> de
Tablea code> et obtenez les identifiants insérés dans la carte li>
Tablea.ID Code> pour obtenir un
ROWNOMBER () CODE> et le correspond à la colonne Identity de la table et met à jour le
old_id < / Code> sur la carte pour correspondre
Tablea.id Code> avec
TableC.Id code>. Li>
S'il est possible d'ajouter une colonne supplémentaire à la table code> Napp code>, vous pouvez utiliser la table elle-même comme carte comme Sean Lange suggéré, puis supprimez la colonne après.
Cette réponse a fonctionné pour produire le résultat souhaité. Il semble plus compliqué que l'approche du curseur. Je vais devoir l'étudier cependant. Et voyons ce que les autres pensent par vote.
Voici une façon de le faire sans curseur ni autre type de type RBAR.
ALTER TABLE TableC ADD LegacyID INT GO INSERT INTO TableC (TableType, Name, LegacyID) SELECT 'A', Name, ID FROM TableA INSERT TableCChild SELECT C.ID, AC.Name FROM TableAChild AC JOIN TableA A ON A.Id = AC.ID JOIN TableC C ON C.LegacyID = A.ID AND C.TableType = 'A' INSERT INTO TableC (TableType, Name, LegacyID) SELECT 'B', Name, ID FROM TableB INSERT TableCChild SELECT C.ID, AC.Name FROM TableBChild AC JOIN TableB A ON A.Id = AC.ID JOIN TableC C ON C.LegacyID = A.ID AND C.TableType = 'B' ALTER TABLE TableC DROP COLUMN LegacyID GO
Malheureusement, c'est un exemple très incomplet. De plus, TABLEX.ID est une colonne d'identité, vous ne pouvez donc pas insérer des enregistrements. Et vous ne pouvez pas activer l'insertion d'identité, car vous auriez des conflits lors de la fusion des enregistrements de tableb et de tablebchild. Je me sens comme si tu as quelque chose d'aller, alors j'aimerais le voir fini.
Vous n'utilisez même pas legacyid. :)
Je pense que ce qu'il veut dire est à partir de la haleture hachuild AC Rejoignez-ci sur A.Legacyid = AC.ID CODE>
Oups. J'ai totalement fouillé ça. Veuillez consulter la mise à jour qui utilise maintenant des tables pour récupérer la nouvelle valeur d'identité.
Ne fonctionne pas sans erreurs. Pouvez-vous utiliser le script que j'ai inclus dans l'OP pour générer des tables et des données pour confirmer que votre réponse fonctionne correctement?
Je l'ai fait et ça court juste bien. :) Quelles erreurs voyez-vous?
Je pense que c'était juste qu'il n'y avait pas de Go code> après le tableau
alter code>. Ou peut-être qu'il avait besoin d'informations de colonne plus spécifiques dans les insertions. J'ai nettoyé votre réponse un peu et j'ai inclus un support pour tableb. Le problème est le résultat maintenant .. Je modifierai mon op pour que vous puissiez voir le résultat que je reçois.
J'ai édité @ la réponse de Seanlange pour produire le résultat correct. Je déteste que je devais faire une grande partie du travail seul pour que cette réponse soit valide, mais il m'a fait au moins me diriger dans la bonne direction. Jusqu'à présent, c'est ma réponse préférée, car c'est le plus simple.
Heureux cela a fonctionné pour vous. Je suppose en quelque sorte que vous devriez faire beaucoup de travail puisque j'espère que la table et les noms de colonne que vous avez fournis n'étaient que des exemples.
Ils ont été des exemples, mais votre dernière réponse n'a définitivement pas produit la sortie souhaitée.
Vous pouvez utiliser Fusionner comme décrit par Adam Machanic dans dr. Sortie ou: Comment j'ai appris à cesser de vous inquiéter et d'aimer la fusion et dans Cette question pour obtenir un mappage entre la nouvelle valeur d'identité et l'ancienne valeur principale de la variable de table et l'utilisation que lorsque vous insérez sur vos tables d'enfant.
declare @T table(ID int, IDC int); merge dbo.TableC as C using dbo.TableA as A on 0 = 1 when not matched by target then insert (TableType, Name) values('A', A.Name) output A.ID, inserted.ID into @T(ID, IDC); insert into dbo.TableCChild(Parent, Name) select T.IDC, AC.Name from dbo.TableAChild as AC inner join @T as T on AC.Parent = T.ID; delete from @T; merge dbo.TableC as C using dbo.TableB as B on 0 = 1 when not matched by target then insert (TableType, Name) values('B', B.Name) output B.ID, inserted.ID into @T(ID, IDC); insert into dbo.TableCChild(Parent, Name) select T.IDC, BC.Name from dbo.TableBChild as BC inner join @T as T on BC.Parent = T.ID;
Ceci est définitivement assez magique. Et étonnamment, je le comprends sans même avoir regardé les articles que vous avez référencés. Le 0 = 1 se sent un peu pirateux cependant. On dirait que vous utilisez fusionnez code> juste pour que vous puissiez tirer parti de
sortie code> et
inséré code>. En .NET, nous avons tendance à froncer les sourcils sur ce type d'abus. Mais, d'après ce que je peux dire, il semble être une utilisation acceptable de
fusionner code> en ligne. Attendons et voyons s'il y a d'autres soumissions ..
Une utilisation vraiment intelligente de fusion puisque la sortie d'insert normal ne peut pas être a.id dans la variable de table qui est pourquoi, dans ma solution, je devais sauter si des boucles pour correspondre aux deux identifiants
Je viens d'écrire le SQL suivant pour le faire si le nom est unique en tablea et unique dans tableb si le nom n'est pas unique et que seul l'identifiant est l'identifiant unique, puis je voudrais Ajoutez legacyid comme suggéré et le code serait alors comme suit p>
N'est-ce pas la même chose que la réponse de Sean, sauf que vous êtes un syndicat tout ensemble? Si oui, quel est le bénéfice de l'Union ici?
La même chose que son oui, il n'y a pas d'avantage réel au syndicat, mais dans la première requête si vos noms sont uniques et séparément en B, vous n'avez pas besoin de l'identifiant Legacy, ce qui signifie une solution plus simple. J'ai testé la mienne et je m'assurai que ça a également manqué d'erreurs aussi :)
En outre, je suis plus un promoteur de garder le héritage héritatif si c'était nécessaire, ou laissez-le tout ensemble, sinon.
Nous pouvons y arriver en tournant la colonne d'identité jusqu'à la fin de la fin de l'insertion comme l'exemple suivant.
--Create the tables CREATE TABLE TableA ( ID int not null identity primary key, Name VARCHAR(30) ); CREATE TABLE TableAChild ( ID int not null identity primary key, Parent int not null, Name VARCHAR(30), CONSTRAINT FK_A FOREIGN KEY (Parent) REFERENCES TableA(ID) ); CREATE TABLE TableB ( ID int not null identity primary key, Name VARCHAR(30) ); CREATE TABLE TableBChild ( ID int not null identity primary key, Parent int not null, Name VARCHAR(30), CONSTRAINT FK_B FOREIGN KEY (Parent) REFERENCES TableB(ID) ); CREATE TABLE TableC ( ID int not null identity primary key, TableType VARCHAR(1), Name VARCHAR(30) ); CREATE TABLE TableCChild ( ID int not null identity primary key, Parent int not null, Name VARCHAR(30), CONSTRAINT FK_C FOREIGN KEY (Parent) REFERENCES TableC(ID) ); -- Insert some test records.. INSERT INTO TableA (Name) Values ('A1') INSERT INTO TableAChild (Name, Parent) VALUES ('A1Child', SCOPE_IDENTITY()) INSERT INTO TableB (Name) Values ('B1') INSERT INTO TableBChild (Name, Parent) VALUES ('B1Child', SCOPE_IDENTITY()) SET IDENTITY_INSERT TableC ON INSERT INTO TableC(ID, TableType, Name) SELECT ID, 'A', Name FROM TableA INSERT INTO TableCChild(Parent, Name) SELECT Parent, Name FROM TableAChild DECLARE @MAXID INT SELECT @MAXID = MAX(ID) FROM TableC PRINT @MAXID SET IDENTITY_INSERT TableC ON INSERT INTO TableC(ID, TableType, Name) SELECT ID + @MAXID, 'B', Name FROM TableB SET IDENTITY_INSERT TableC OFF INSERT INTO TableCChild(Parent, Name) SELECT Parent + @MAXID, Name FROM TableBChild SET IDENTITY_INSERT TableC OFF SELECT * FROM TableC SELECT * FROM TableCChild DROP TABLE TableAChild DROP TABLE TableBChild DROP TABLE TableCChild DROP TABLE TableA DROP TABLE TableB DROP TABLE TableC
Si vous devez insérer des enregistrements dans la troisième table TableM et TableCChild pour une utilisation ultérieure, il convient d'insérer des données dans ces tables, mais si vous n'avez besoin que de cette table pour l'utiliser dans votre procédure stockée pour le moment où vous pouvez également. Il suffit de travailler avec les deux premières tables pour obtenir le résultat souhaité.
select * from ( select b.ID,b.Parent,b.Name from TableA a inner join TableAChild b on a.ID=b.ID union select b.ID,b.Parent,b.Name from TableB a inner join TableBChild b on a.ID=b.ID) TableCChild
Si les champs de noms dans
Tablea code> et
tableb code> sont uniques par table, vous pouvez réécrire ceci sans curseur et simplement utiliser un
joindre code>. Cependant, s'ils n'étaient pas uniques, le curseur est la seule option que je vois pour obtenir le champ
ID code> du parent.
@sgeddes Faites-vous référence aux valeurs i> dans la colonne Nom? Si oui, ils ne sont pas uniques.
Pourquoi ne pas simplement ajouter une autre colonne à la table pour l'héritage. Cela vous donnera une valeur pour utiliser une jointure. Il n'est pas nécessaire de faire ce genre de rbar.
@Seanlange hrmmm, tu m'as pensé, mais je ne sais pas comment ça marcherait. Il existe des relations entre la colonne TableX.Id et la colonne TableXchild.Parente dans ma base de données (excuses pour ne pas inclure celle de l'échantillon, car je ne pensais pas que c'était nécessaire). Et je ne veux certainement pas laisser derrière une colonne de la héritabilité indésirable. Pouvez-vous expliquer, éventuellement dans une réponse?
Vous pouvez ajouter cette colonne hégacyid comme une chose temporaire. Cela permettrait de l'utiliser pour rejoindre les lignes enfants au nouveau parent. Une fois que vous avez fini de fusionner les données, vous pouvez supprimer cette colonne car elle n'aurait plus de sens.
@Seanlange Votre suggère doit-elle prendre en compte qu'il existe de nouveaux enregistrements d'enfants qui doivent également être créés? J'ai l'impression d'être proche de la compréhension, mais je manque la connexion aux nouveaux enregistrements d'enfants.
FYI, j'ai mis à jour les scripts de création de table pour être plus spécifiques sur les clés et les relations primaires.