SQL Server: est-il possible d'insérer deux tables en même temps?
ma base de données contient trois tables appelées Object_Table
, Data_Table
et Link_Table
. Le lien tableau contient deux colonnes, l'identité d'un objet et d'une identité d'enregistrement de données.
je veux copier les données de DATA_TABLE
où elles sont liées à une identité d'objet donnée et insérer les enregistrements correspondants dans Data_Table
et Link_Table
pour une identité d'objet donnée différente.
je peut faites ceci En choisissant dans une variable de table et la boucle à travers faire deux inserts pour chaque itération.
Est-ce la meilleure façon de le faire?
Edit : je veux éviter une boucle pour deux raisons, la première est que je suis paresseux et une table de boucle/temp nécessite plus de code, plus de code signifie plus d'endroits pour faire une erreur et la deuxième raison est une préoccupation au sujet de la performance.
je peux copier toutes les données dans un insert, mais comment obtenir la table de lien pour lier aux nouveaux enregistrements de données où chaque enregistrement a un nouvel id?
11 réponses
Dans un déclaration : Non.
Dans un transaction : Oui
BEGIN TRANSACTION
DECLARE @DataID int;
INSERT INTO DataTable (Column1 ...) VALUES (....);
SELECT @DataID = scope_identity();
INSERT INTO LinkTable VALUES (@ObjectID, @DataID);
COMMIT
la bonne nouvelle est que le code ci-dessus est également garanti être atomic , et peut être envoyé au serveur à partir d'une application client avec une chaîne sql dans un appel de fonction simple comme si c'était une déclaration. Vous pouvez également appliquer un déclencheur à une table pour obtenir l'effet d'un seul insert. Cependant, il s'agit finalement encore de deux énoncés et vous ne voulez probablement pas exécuter le déclencheur pour chaque insert.
vous avez encore besoin de deux INSERT
déclarations, mais il semble que vous voulez obtenir le IDENTITY
de la première insertion et de l'utiliser dans la seconde, dans ce cas, vous pourriez vouloir regarder dans OUTPUT
ou OUTPUT INTO
: http://msdn.microsoft.com/en-us/library/ms177564.aspx
la suite met en place la situation que j'avais, en utilisant des variables de tableau.
DECLARE @Object_Table TABLE
(
Id INT NOT NULL PRIMARY KEY
)
DECLARE @Link_Table TABLE
(
ObjectId INT NOT NULL,
DataId INT NOT NULL
)
DECLARE @Data_Table TABLE
(
Id INT NOT NULL Identity(1,1),
Data VARCHAR(50) NOT NULL
)
-- create two objects '1' and '2'
INSERT INTO @Object_Table (Id) VALUES (1)
INSERT INTO @Object_Table (Id) VALUES (2)
-- create some data
INSERT INTO @Data_Table (Data) VALUES ('Data One')
INSERT INTO @Data_Table (Data) VALUES ('Data Two')
-- link all data to first object
INSERT INTO @Link_Table (ObjectId, DataId)
SELECT Objects.Id, Data.Id
FROM @Object_Table AS Objects, @Data_Table AS Data
WHERE Objects.Id = 1
merci à un autre réponse qui m'a pointé vers la clause de sortie je peux démontrer une solution:
-- now I want to copy the data from from object 1 to object 2 without looping
INSERT INTO @Data_Table (Data)
OUTPUT 2, INSERTED.Id INTO @Link_Table (ObjectId, DataId)
SELECT Data.Data
FROM @Data_Table AS Data INNER JOIN @Link_Table AS Link ON Data.Id = Link.DataId
INNER JOIN @Object_Table AS Objects ON Link.ObjectId = Objects.Id
WHERE Objects.Id = 1
Il s'avère cependant qu'il n'est pas aussi simple dans la vraie vie en raison de l'erreur suivante:
la sortie dans la clause ne peut pas être sur de chaque côté d'une (clé primaire, étranger clé) relation 151940920"
je peux encore OUTPUT INTO
une table de température et puis terminer avec insert normal. Donc je peux éviter ma boucle mais je ne peux pas éviter la table d'intérim.
il semble que la table de lien capture la relation beaucoup:beaucoup entre la table D'objet et la table de données.
Ma suggestion est d'utiliser une procédure stockée pour gérer les transactions. Quand vous voulez insérer à L'objet ou à la table de données effectuer vos inserts, obtenez les nouveaux ID et les insérer à la table de lien.
cela permet à toute votre logique de rester encapsulée dans un sproc facile à appeler.
si vous voulez que les actions soient plus ou moins atomiques, Je m'assurerais de les envelopper dans une transaction. De cette façon, vous pouvez être sûr que les deux se sont produits ou les deux ne se sont pas produits comme nécessaire.
vous pouvez créer une vue en sélectionnant les noms de colonne Requis par votre instruction insert, ajouter un au lieu D'insérer Trigger, et insérer dans cette vue.
Insert ne peut fonctionner que sur une seule table à la fois. Plusieurs Insertions avoir plusieurs états.
Je ne sais pas si vous avez besoin de faire la boucle à travers une variable de table - ne pouvez-vous pas juste utiliser un insert de masse dans une table, puis la masse insert dans l'autre?
soit dit en passant - je suppose que vous voulez dire copier les données de Object_Table; sinon la question n'a pas de sens.
avant de pouvoir faire un insert multitable dans Oracle, vous pourriez utiliser un truc impliquant un insert dans une vue qui avait un déclencheur au lieu de défini pour effectuer les inserts. Cela peut-il être fait dans SQL Server?
je veux insister sur l'utilisation
SET XACT_ABORT ON;
pour la transaction MSSQL avec plusieurs États sql.
voir: https://msdn.microsoft.com/en-us/library/ms188792.aspx Ils fournissent un bon exemple.
ainsi, le code final devrait ressembler à ce qui suit:
SET XACT_ABORT ON;
BEGIN TRANSACTION
DECLARE @DataID int;
INSERT INTO DataTable (Column1 ...) VALUES (....);
SELECT @DataID = scope_identity();
INSERT INTO LinkTable VALUES (@ObjectID, @DataID);
COMMIT
-- ================================================
-- Template generated from Template Explorer using:
-- Create Procedure (New Menu).SQL
--
-- Use the Specify Values for Template Parameters
-- command (Ctrl-Shift-M) to fill in the parameter
-- values below.
--
-- This block of comments will not be included in
-- the definition of the procedure.
-- ================================================
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
ALTER PROCEDURE InsetIntoTwoTable
(
@name nvarchar(50),
@Email nvarchar(50)
)
AS
BEGIN
SET NOCOUNT ON;
insert into dbo.info(name) values (@name)
insert into dbo.login(Email) values (@Email)
END
GO
//si vous souhaitez insérer le même que le premier tableau,
$qry = "INSERT INTO table (one, two, three) VALUES('$one','$two','$three')";
$result = @mysql_query($qry);
$qry2 = "INSERT INTO table2 (one,two, three) VVALUES('$one','$two','$three')";
$result = @mysql_query($qry2);
//ou si vous voulez insérer certaines parties du tableau un
$qry = "INSERT INTO table (one, two, three) VALUES('$one','$two','$three')";
$result = @mysql_query($qry);
$qry2 = "INSERT INTO table2 (two) VALUES('$two')";
$result = @mysql_query($qry2);
/ / je sais que ça a l'air trop beau pour avoir raison, mais ça fonctionne et vous pouvez continuer à ajouter des requêtes juste changer le
"$qry"-number and number in @mysql_query($qry"")
j'ai 17 tableaux dans lesquels cela a fonctionné.