如何更好地在SQL Server中复制一组数据

问题描述 投票:5回答:3

我有几个相关的表,我希望能够在更新引用时复制某些行。

我想复制表1中的一行,以及它与表2和表3中所有相关的行,并且我试图找出一种有效的方式来做到这一点,而无需遍历行。

例如,我有一张桌子:

+----------+---------------+
| BasketId |  BasketName   |
+----------+---------------+
|        1 | Home Basket   |
|        2 | Office Basket |
+----------+---------------+

每个篮子都有水果:

+---------+----------+-----------+
| FruitId | BasketId | FruitName |
+---------+----------+-----------+
|       1 |        1 | Apple     |
|       2 |        1 | Orange    |
|       3 |        2 | Mango     |
|       4 |        2 | Pear      |
+---------+----------+-----------+

每个水果都有一些特性:

+------------+---------+--------------+
| PropertyId | FruitId | PropertyText |
+------------+---------+--------------+
|          1 |       2 | Is juicy     |
|          2 |       2 | Hard to peel |
|          3 |       1 | Is red       |
+------------+---------+--------------+

对于此示例,我的属性特定于单个水果行,这些“苹果”属性并非所有篮子中所有苹果的属性,仅针对该特定篮子中的特定苹果。

我想做的是复制一个篮子。因此,给定购物篮1,我想创建一个新的购物篮,复制其中包含的水果行,并复制指向这些水果的属性。最后,我希望有这样的数据:

+----------+---------------+
| BasketId |  BasketName   |
+----------+---------------+
|        1 | Home Basket   |
|        2 | Office Basket |
|        3 | Friends Basket|
+----------+---------------+

+---------+----------+-----------+
| FruitId | BasketId | FruitName |
+---------+----------+-----------+
|       1 |        1 | Apple     |
|       2 |        1 | Orange    |
|       3 |        2 | Mango     |
|       4 |        2 | Pear      |
|       5 |        3 | Apple     |
|       6 |        3 | Orange    |
+---------+----------+-----------+

+------------+---------+--------------+
| PropertyId | FruitId | PropertyText |
+------------+---------+--------------+
|          1 |       2 | Is juicy     |
|          2 |       2 | Hard to peel |
|          3 |       1 | Is red       |
|          4 |       6 | Is juicy     |
|          5 |       6 | Hard to peel |
|          6 |       5 | Is red       |
+------------+---------+--------------+

复制篮子和它的水果非常简单,但是复制水果的属性在我看来会导致遍历行,我希望在TSQL中有更好的解决方案。

有什么想法吗?

sql sql-server tsql stored-procedures
3个回答
2
投票

您为什么不加入FruitName来获得包含新旧FruitId的表?考虑到将同时添加信息。...这可能不是最佳选择,但您将不会使用任何循环。

INSERT INTO BASKET(BASKETNAME)
VALUES ('COPY BASKET')

DECLARE @iBasketId int
SET @iBasketId = @@SCOPE_IDENTITY;


insert into Fruit (BasketId, FruitName)
select @iBasketId, FruitName
from Fruit 
where BasketId = @originalBasket

declare @tabFruit table (originalFruitId int, newFruitId int)

insert into @tabFruit (originalFruitId, newFruitId)
select o.FruitId, n.FruitId
from (SELECT FruitId, FruitName from Fruit where BasketId = @originalBasket) as o
join (SELECT FruitId, FruitName from Fruit where BasketId = @newBasket) as n
    on o.FruitName = n.FruitName


insert into Property (FruitId, PropertyText)
select NewFruitId, PropertyText
from Fruit f join @tabFruit t on t.originalFruitId = f.FruitId

2
投票

((ab)将MERGEMERGE子句一起使用。

[OUTPUT可以MERGEINSERTUPDATE行。在我们的情况下,我们只需要DELETEINSERT始终为假,因此始终执行1=0部分。通常,可能还有其他分支,请参阅文档。 NOT MATCHED BY TARGET通常用于WHEN MATCHEDUPDATE通常用于WHEN NOT MATCHED BY SOURCE,但是我们在这里不需要它们。

DELETE的这种复杂形式等效于简单的MERGE,但与简单的INSERT不同,它的INSERT子句允许引用我们需要的列。

我将明确写下表的定义。表中的每个主键均为OUTPUT。我还配置了外键。

购物篮

IDENTITY

水果

CREATE TABLE [dbo].[Baskets](
    [BasketId] [int] IDENTITY(1,1) NOT NULL,
    [BasketName] [varchar](50) NOT NULL,
 CONSTRAINT [PK_Baskets] PRIMARY KEY CLUSTERED 
(
    [BasketId] ASC
)

属性

CREATE TABLE [dbo].[Fruits](
    [FruitId] [int] IDENTITY(1,1) NOT NULL,
    [BasketId] [int] NOT NULL,
    [FruitName] [varchar](50) NOT NULL,
 CONSTRAINT [PK_Fruits] PRIMARY KEY CLUSTERED 
(
    [FruitId] ASC
)

ALTER TABLE [dbo].[Fruits]  WITH CHECK 
ADD CONSTRAINT [FK_Fruits_Baskets] FOREIGN KEY([BasketId])
REFERENCES [dbo].[Baskets] ([BasketId])

ALTER TABLE [dbo].[Fruits] CHECK CONSTRAINT [FK_Fruits_Baskets]

复制篮

首先复制CREATE TABLE [dbo].[Properties]( [PropertyId] [int] IDENTITY(1,1) NOT NULL, [FruitId] [int] NOT NULL, [PropertyText] [varchar](50) NOT NULL, CONSTRAINT [PK_Properties] PRIMARY KEY CLUSTERED ( [PropertyId] ASC ) ALTER TABLE [dbo].[Properties] WITH CHECK ADD CONSTRAINT [FK_Properties_Fruits] FOREIGN KEY([FruitId]) REFERENCES [dbo].[Fruits] ([FruitId]) ALTER TABLE [dbo].[Properties] CHECK CONSTRAINT [FK_Properties_Fruits] 表中的一行,然后使用Baskets获取生成的SCOPE_IDENTITY

ID

复制水果

然后使用BEGIN TRANSACTION; -- Parameter of the procedure. What basket to copy. DECLARE @VarOldBasketID int = 1; -- Copy Basket, one row DECLARE @VarNewBasketID int; INSERT INTO [dbo].[Baskets] (BasketName) VALUES ('Friends Basket'); SET @VarNewBasketID = SCOPE_IDENTITY(); 复制Fruits,并记住表变量中新旧ID之间的映射。

MERGE

复制属性

然后使用记住的新旧水果ID之间的映射复制-- Copy Fruits, multiple rows DECLARE @FruitIDs TABLE (OldFruitID int, NewFruitID int); MERGE INTO [dbo].[Fruits] USING ( SELECT [FruitId] ,[BasketId] ,[FruitName] FROM [dbo].[Fruits] WHERE [BasketId] = @VarOldBasketID ) AS Src ON 1 = 0 WHEN NOT MATCHED BY TARGET THEN INSERT ([BasketId] ,[FruitName]) VALUES (@VarNewBasketID ,Src.[FruitName]) OUTPUT Src.[FruitId] AS OldFruitID, inserted.[FruitId] AS NewFruitID INTO @FruitIDs(OldFruitID, NewFruitID) ; 。>>

Properties

检查结果,确认代码正确运行后,将回滚更改为提交。

-- Copy Properties, many rows
INSERT INTO [dbo].[Properties] ([FruitId], [PropertyText])
SELECT
    F.NewFruitID
    ,[dbo].[Properties].PropertyText
FROM
    [dbo].[Properties]
    INNER JOIN @FruitIDs AS F ON F.OldFruitID = [dbo].[Properties].FruitId
;

0
投票

我和OP有相同的需求:克隆sql服务器表,它们是层次结构的sql服务器表,其中包含彼此的外键。换句话说,克隆具有父子关系的sql server表。

© www.soinside.com 2019 - 2024. All rights reserved.