Question

Ma base de données contient trois tables nommées Object_Table , Data_Table et Link_Table . La table de liens ne contient que deux colonnes, l’identité d’un enregistrement d’objet et l’identité d’un enregistrement de données.

Je souhaite copier les données de DATA_TABLE lorsqu'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 peux le faire en sélectionnant une variable de table et en effectuant la boucle en faisant deux insertions pour chaque itération.

Est-ce la meilleure façon de le faire?

Modifier : je souhaite éviter une boucle pour deux raisons. La première est que je suis paresseux et qu'une table boucle / temp nécessite plus de code, plus de code signifie plus d'endroits où commettre une erreur et la deuxième raison est une préoccupation pour la performance.

Je peux copier toutes les données dans un seul insert, mais comment faire en sorte que la table des liens soit liée aux nouveaux enregistrements de données, chaque enregistrement ayant un nouvel identifiant?

Était-ce utile?

La solution 3

Ce qui suit met en place la situation que j'ai eue, à l'aide de variables de table.

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 dirigé vers la clause OUTPUT, 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 toutefois que ce n'est pas si simple dans la vie réelle à cause de l'erreur suivante

  

la clause OUTPUT INTO ne peut pas être activée   de chaque côté d'un (clé primaire, étrangère   clé) relation

Je peux encore OUTPUT INTO une table temporaire puis terminer par une insertion normale. Je peux donc éviter ma boucle mais je ne peux pas éviter la table temporaire.

Autres conseils

Dans une instruction : Non.

Dans une 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 comme étant atomique et peut être envoyé au serveur à partir d'une application cliente avec une chaîne SQL dans un seul appel de fonction comme s'il s'agissait d'un seul. déclaration. Vous pouvez également appliquer un déclencheur à une table pour obtenir l’effet d’une seule insertion. Cependant, il reste finalement deux instructions et vous ne souhaiterez probablement pas exécuter le déclencheur pour chaque insertion .

Vous avez toujours besoin de deux instructions INSERT , mais il semblerait que vous souhaitiez obtenir le IDENTITY du premier insert et l'utiliser dans le second, auquel cas vous peut vouloir examiner OUTPUT ou OUTPUT INTO : http://msdn.microsoft.com/en-us/library/ms177564.aspx

On dirait que la table des liens capture les nombreuses relations entre la table des objets et la table des données.

Je suggère d'utiliser une procédure stockée pour gérer les transactions. Lorsque vous souhaitez insérer dans la table Object ou Data, effectuez vos insertions, récupérez les nouveaux ID et insérez-les dans la table Link.

Ceci permet à toute votre logique de rester encapsulée dans un seul sproc facile à appeler.

Si vous souhaitez que les actions soient plus ou moins atomiques, assurez-vous de les envelopper dans une transaction. De cette façon, vous pouvez être sûr que les deux sont arrivés ou que les deux ne sont pas arrivés au besoin.

Vous pouvez créer une vue en sélectionnant les noms de colonne requis par votre instruction insert, ajouter un déclencheur INSTEAD OF INSERT et l'insérer dans cette vue.

Je veux insister sur l'utilisation de

SET XACT_ABORT ON;

pour la transaction MSSQL avec plusieurs instructions SQL.

Voir: https://msdn.microsoft.com/en-us/ bibliothèque / ms188792.aspx Ils fournissent un très bon exemple.

Ainsi, le code final devrait ressembler à ceci:

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

Insert ne peut fonctionner que sur une table à la fois. Plusieurs insertions doivent avoir plusieurs déclarations.

Je ne sais pas si vous devez faire la boucle dans une variable de table. Ne pouvez-vous pas simplement utiliser un insert de masse dans un tableau, puis l'insertion de masse dans l'autre?

Au fait - je suppose que vous voulez dire copier les données de Object_Table; sinon la question n'a pas de sens.

Avant de pouvoir effectuer une insertion multitable dans Oracle, vous pouvez utiliser une astuce impliquant une insertion dans une vue dans laquelle un déclencheur INSTEAD OF a été défini pour effectuer les insertions. Cela peut-il être fait dans SQL Server?

-- ================================================
-- 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 voulez 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 souhaitez insérer certaines parties du premier tableau

 $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 c'est trop beau pour avoir raison, mais cela fonctionne et vous pouvez continuer à ajouter des requêtes en changeant simplement la

    "$qry"-number and number in @mysql_query($qry"")

J'ai 17 tables qui ont fonctionné.

Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top