插入多个表
Insert into multiple tables
相关领域部分的简要说明:
一个类别由四个数据组成:
- 性别(Male/Female)
- 年龄划分(Mighty Mite to Master)
- 腰带颜色(白色到黑色)
- 重量级(公鸡到重型)
所以,Male Adult Black Rooster
形成一类。有些组合可能不存在,比如mighty mite black belt.
一名运动员与同一类别的运动员进行比赛,如果他进行分类,他将与不同体重级别(但性别、年龄和腰带相同)的运动员进行比赛。
到造型。我有一个 Category
table,已经填充了域中存在的所有组合。
CREATE TABLE Category (
[Id] [int] IDENTITY(1,1) NOT NULL,
[AgeDivision_Id] [int] NULL,
[Gender] [int] NULL,
[BeltColor] [int] NULL,
[WeightDivision] [int] NULL
)
一个CategorySet
和一个CategorySet_Category
,与Category
形成多对多的关系。
CREATE TABLE CategorySet (
[Id] [int] IDENTITY(1,1) NOT NULL,
[Championship_Id] [int] NOT NULL,
)
CREATE TABLE CategorySet_Category (
[CategorySet_Id] [int] NOT NULL,
[Category_Id] [int] NOT NULL
)
给定以下结果集:
| Options_Id | Championship_Id | AgeDivision_Id | BeltColor | Gender | WeightDivision |
|------------|-----------------|----------------|-----------|--------|----------------|
1. | 2963 | 422 | 15 | 7 | 0 | 0 |
2. | 2963 | 422 | 15 | 7 | 0 | 1 |
3. | 2963 | 422 | 15 | 7 | 0 | 2 |
4. | 2963 | 422 | 15 | 7 | 0 | 3 |
5. | 2964 | 422 | 15 | 8 | 0 | 0 |
6. | 2964 | 422 | 15 | 8 | 0 | 1 |
7. | 2964 | 422 | 15 | 8 | 0 | 2 |
8. | 2964 | 422 | 15 | 8 | 0 | 3 |
因为运动员可能会打两个 CategorySet,所以我需要 CategorySet
和 CategorySet_Category
以两种不同的方式填充(可以是两个查询):
每行一个Category_Set
,一个CategorySet_Category
指向相应的Category
。
一个 Category_Set
将所有 WeightDivisions 归为一个 CategorySet
在相同的 AgeDivision_Id、BeltColor、Gender 中。在此示例中,只有 BeltColor
不同。
因此最终结果总共有 10 CategorySet
行:
| Id | Championship_Id |
|----|-----------------|
| 1 | 422 |
| 2 | 422 |
| 3 | 422 |
| 4 | 422 |
| 5 | 422 |
| 6 | 422 |
| 7 | 422 |
| 8 | 422 |
| 9 | 422 | /* groups different Weight Division for BeltColor 7 */
| 10 | 422 | /* groups different Weight Division for BeltColor 8 */
而 CategorySet_Category
将有 16 行:
| CategorySet_Id | Category_Id |
|----------------|-------------|
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
| 6 | 6 |
| 7 | 7 |
| 8 | 8 |
| 9 | 1 | /* groups different Weight Division for BeltColor 7 */
| 9 | 2 | /* groups different Weight Division for BeltColor 7 */
| 9 | 3 | /* groups different Weight Division for BeltColor 7 */
| 9 | 4 | /* groups different Weight Division for BeltColor 7 */
| 10 | 5 | /* groups different Weight Division for BeltColor 8 */
| 10 | 6 | /* groups different Weight Division for BeltColor 8 */
| 10 | 7 | /* groups different Weight Division for BeltColor 8 */
| 10 | 8 | /* groups different Weight Division for BeltColor 8 */
我不知道如何插入 CategorySet
,获取它生成的 ID,然后用它插入 CategorySet_Category
我希望我已经表达清楚了我的意图。
编辑 1: 我在 Jacek 的回答中评论说这只会 运行 一次,但这是错误的。它每周 运行 几次。我可以选择 运行 作为来自 C# 或存储过程的 SQL 命令。性能并不重要。
编辑 2: Jacek 建议使用 SCOPE_IDENTITY
到 return Id。问题是,SCOPE_IDENTITY
return 只是最后插入的 Id,而我在 CategorySet
中插入了不止一行。
编辑 3: 回答 @FutbolFan 询问如何检索 FakeResultSet。
这是一个table CategoriesOption
(Id, Price_Id, MaxAthletesByTeam)
和tables CategoriesOptionBeltColor
、CategoriesOptionAgeDivision
、CategoriesOptionWeightDivison
、CategoriesOptionGender
。那四个table基本上是一样的(Id,CategoriesOption_Id,Value).
查询如下所示:
SELECT * FROM CategoriesOption co
LEFT JOIN CategoriesOptionAgeDivision ON
CategoriesOptionAgeDivision.CategoriesOption_Id = co.Id
LEFT JOIN CategoriesOptionBeltColor ON
CategoriesOptionBeltColor.CategoriesOption_Id = co.Id
LEFT JOIN CategoriesOptionGender ON
CategoriesOptionGender.CategoriesOption_Id = co.Id
LEFT JOIN CategoriesOptionWeightDivision ON
CategoriesOptionWeightDivision.CategoriesOption_Id = co.Id
@@IDENTITY 是问题第二部分的朋友
https://msdn.microsoft.com/en-us/library/ms187342.aspx
和
Best way to get identity of inserted row?
一些 API(驱动程序)returns 来自 update() 函数的 int,即 ID,如果它是 "insert"。你用什么API/environment?
我不明白第一个问题。您不应插入标识列。
此处描述的解决方案将在多用户环境中以及目标 tables CategorySet
和 CategorySet_Category
不为空时正常工作。
我使用了您 SQL Fiddle.
中的模式和示例数据
第一部分很简单
(ab)将 MERGE
与 OUTPUT
子句一起使用。
MERGE
可以 INSERT
、UPDATE
和 DELETE
行。在我们的例子中,我们只需要 INSERT
。 1=0
始终为 false,因此始终执行 NOT MATCHED BY TARGET
部分。通常,可能还有其他分支,请参阅文档。 WHEN MATCHED
通常用于UPDATE
; WHEN NOT MATCHED BY SOURCE
通常用于DELETE
,但我们这里不需要。
MERGE
的这种复杂形式等同于简单的 INSERT
,但与简单的 INSERT
不同的是,它的 OUTPUT
子句允许引用我们需要的列。
MERGE INTO CategorySet
USING
(
SELECT
FakeResultSet.Championship_Id
,FakeResultSet.Price_Id
,FakeResultSet.MaxAthletesByTeam
,Category.Id AS Category_Id
FROM
FakeResultSet
INNER JOIN Category ON
Category.AgeDivision_Id = FakeResultSet.AgeDivision_Id AND
Category.Gender = FakeResultSet.Gender AND
Category.BeltColor = FakeResultSet.BeltColor AND
Category.WeightDivision = FakeResultSet.WeightDivision
) AS Src
ON 1 = 0
WHEN NOT MATCHED BY TARGET THEN
INSERT
(Championship_Id
,Price_Id
,MaxAthletesByTeam)
VALUES
(Src.Championship_Id
,Src.Price_Id
,Src.MaxAthletesByTeam)
OUTPUT inserted.id AS CategorySet_Id, Src.Category_Id
INTO CategorySet_Category (CategorySet_Id, Category_Id)
;
FakeResultSet
与 Category
相结合得到 FakeResultSet
的每一行 Category.id
。假定 Category
具有 AgeDivision_Id, Gender, BeltColor, WeightDivision
.
的唯一组合
在 OUTPUT
子句中,我们需要来自源和目标 table 的列。简单的 INSERT
语句中的 OUTPUT
子句没有提供它们,因此我们在这里使用 MERGE
。
上面的 MERGE
查询将使用生成的 ID 将 8 行插入 CategorySet
并向 CategorySet_Category
插入 8 行。
第二部分
需要临时 table。我将使用 table 变量来存储生成的 ID。
DECLARE @T TABLE (
CategorySet_Id int
,AgeDivision_Id int
,Gender int
,BeltColor int);
我们需要记住生成的 CategorySet_Id
以及导致它的 AgeDivision_Id, Gender, BeltColor
的组合。
MERGE INTO CategorySet
USING
(
SELECT
FakeResultSet.Championship_Id
,FakeResultSet.Price_Id
,FakeResultSet.MaxAthletesByTeam
,FakeResultSet.AgeDivision_Id
,FakeResultSet.Gender
,FakeResultSet.BeltColor
FROM
FakeResultSet
GROUP BY
FakeResultSet.Championship_Id
,FakeResultSet.Price_Id
,FakeResultSet.MaxAthletesByTeam
,FakeResultSet.AgeDivision_Id
,FakeResultSet.Gender
,FakeResultSet.BeltColor
) AS Src
ON 1 = 0
WHEN NOT MATCHED BY TARGET THEN
INSERT
(Championship_Id
,Price_Id
,MaxAthletesByTeam)
VALUES
(Src.Championship_Id
,Src.Price_Id
,Src.MaxAthletesByTeam)
OUTPUT
inserted.id AS CategorySet_Id
,Src.AgeDivision_Id
,Src.Gender
,Src.BeltColor
INTO @T(CategorySet_Id, AgeDivision_Id, Gender, BeltColor)
;
上面的 MERGE
将根据需要对 FakeResultSet
进行分组,并将 2 行插入 CategorySet
并插入 2 行到 @T
.
然后加入@T
和Category
得到Category.IDs
:
INSERT INTO CategorySet_Category (CategorySet_Id, Category_Id)
SELECT
TT.CategorySet_Id
,Category.Id AS Category_Id
FROM
@T AS TT
INNER JOIN Category ON
Category.AgeDivision_Id = TT.AgeDivision_Id AND
Category.Gender = TT.Gender AND
Category.BeltColor = TT.BeltColor
;
这会将 8 行插入 CategorySet_Category
。
这里不是完整的答案,而是您可以用来解决这个问题的方向:
第一个查询:
select row_number() over(order by t, Id) as n, Championship_Id
from (
select distinct 0 as t, b.Id, a.Championship_Id
from FakeResultSet as a
inner join
Category as b
on
a.AgeDivision_Id=b.AgeDivision_Id and
a.Gender=b.Gender and
a.BeltColor=b.BeltColor and
a.WeightDivision=b.WeightDivision
union all
select distinct 1, BeltColor, Championship_Id
from FakeResultSet
) as q
第二次查询:
select q2.CategorySet_Id, c.Id as Category_Id from (
select row_number() over(order by t, Id) as CategorySet_Id, Id, BeltColor
from (
select distinct 0 as t, b.Id, null as BeltColor
from FakeResultSet as a
inner join
Category as b
on
a.AgeDivision_Id=b.AgeDivision_Id and
a.Gender=b.Gender and
a.BeltColor=b.BeltColor and
a.WeightDivision=b.WeightDivision
union all
select distinct 1, BeltColor, BeltColor
from FakeResultSet
) as q
) as q2
inner join
Category as c
on
(q2.BeltColor is null and q2.Id=c.Id)
OR
(q2.BeltColor = c.BeltColor)
当然这仅适用于空的 CategorySet
和 CategorySet_Category
表,但您可以使用 select coalese(max(Id), 0) from CategorySet
获取当前数字并将其添加到 row_number
,因此您将获得真实 ID,该 ID 将被插入到 CategorySet 行中以供第二次查询
当我 运行 遇到这些情况时,我所做的是创建一个或多个临时 tables with row_number() over 子句给我在临时 [=13] 上的身份=]秒。然后我检查实际 table 中每条记录是否存在,如果它们存在,则用实际记录 ID 更新临时 table。最后,我 运行 在缺少实际 ID 的临时 table 记录上存在 while 循环,一次插入一个,插入后我用实际 ID 更新临时 table 记录。这使您能够以受控方式处理所有数据。
下面的查询将为 CategorySet 行提供最终结果:
SELECT
ROW_NUMBER () OVER (PARTITION BY Championship_Id ORDER BY Championship_Id) RNK,
Championship_Id
FROM
(
SELECT
Championship_Id
,BeltColor
FROM #FakeResultSet
UNION ALL
SELECT
Championship_Id,BeltColor
FROM #FakeResultSet
GROUP BY Championship_Id,BeltColor
)BASE
相关领域部分的简要说明:
一个类别由四个数据组成:
- 性别(Male/Female)
- 年龄划分(Mighty Mite to Master)
- 腰带颜色(白色到黑色)
- 重量级(公鸡到重型)
所以,Male Adult Black Rooster
形成一类。有些组合可能不存在,比如mighty mite black belt.
一名运动员与同一类别的运动员进行比赛,如果他进行分类,他将与不同体重级别(但性别、年龄和腰带相同)的运动员进行比赛。
到造型。我有一个 Category
table,已经填充了域中存在的所有组合。
CREATE TABLE Category (
[Id] [int] IDENTITY(1,1) NOT NULL,
[AgeDivision_Id] [int] NULL,
[Gender] [int] NULL,
[BeltColor] [int] NULL,
[WeightDivision] [int] NULL
)
一个CategorySet
和一个CategorySet_Category
,与Category
形成多对多的关系。
CREATE TABLE CategorySet (
[Id] [int] IDENTITY(1,1) NOT NULL,
[Championship_Id] [int] NOT NULL,
)
CREATE TABLE CategorySet_Category (
[CategorySet_Id] [int] NOT NULL,
[Category_Id] [int] NOT NULL
)
给定以下结果集:
| Options_Id | Championship_Id | AgeDivision_Id | BeltColor | Gender | WeightDivision |
|------------|-----------------|----------------|-----------|--------|----------------|
1. | 2963 | 422 | 15 | 7 | 0 | 0 |
2. | 2963 | 422 | 15 | 7 | 0 | 1 |
3. | 2963 | 422 | 15 | 7 | 0 | 2 |
4. | 2963 | 422 | 15 | 7 | 0 | 3 |
5. | 2964 | 422 | 15 | 8 | 0 | 0 |
6. | 2964 | 422 | 15 | 8 | 0 | 1 |
7. | 2964 | 422 | 15 | 8 | 0 | 2 |
8. | 2964 | 422 | 15 | 8 | 0 | 3 |
因为运动员可能会打两个 CategorySet,所以我需要 CategorySet
和 CategorySet_Category
以两种不同的方式填充(可以是两个查询):
每行一个Category_Set
,一个CategorySet_Category
指向相应的Category
。
一个 Category_Set
将所有 WeightDivisions 归为一个 CategorySet
在相同的 AgeDivision_Id、BeltColor、Gender 中。在此示例中,只有 BeltColor
不同。
因此最终结果总共有 10 CategorySet
行:
| Id | Championship_Id |
|----|-----------------|
| 1 | 422 |
| 2 | 422 |
| 3 | 422 |
| 4 | 422 |
| 5 | 422 |
| 6 | 422 |
| 7 | 422 |
| 8 | 422 |
| 9 | 422 | /* groups different Weight Division for BeltColor 7 */
| 10 | 422 | /* groups different Weight Division for BeltColor 8 */
而 CategorySet_Category
将有 16 行:
| CategorySet_Id | Category_Id |
|----------------|-------------|
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| 5 | 5 |
| 6 | 6 |
| 7 | 7 |
| 8 | 8 |
| 9 | 1 | /* groups different Weight Division for BeltColor 7 */
| 9 | 2 | /* groups different Weight Division for BeltColor 7 */
| 9 | 3 | /* groups different Weight Division for BeltColor 7 */
| 9 | 4 | /* groups different Weight Division for BeltColor 7 */
| 10 | 5 | /* groups different Weight Division for BeltColor 8 */
| 10 | 6 | /* groups different Weight Division for BeltColor 8 */
| 10 | 7 | /* groups different Weight Division for BeltColor 8 */
| 10 | 8 | /* groups different Weight Division for BeltColor 8 */
我不知道如何插入 CategorySet
,获取它生成的 ID,然后用它插入 CategorySet_Category
我希望我已经表达清楚了我的意图。
编辑 1: 我在 Jacek 的回答中评论说这只会 运行 一次,但这是错误的。它每周 运行 几次。我可以选择 运行 作为来自 C# 或存储过程的 SQL 命令。性能并不重要。
编辑 2: Jacek 建议使用 SCOPE_IDENTITY
到 return Id。问题是,SCOPE_IDENTITY
return 只是最后插入的 Id,而我在 CategorySet
中插入了不止一行。
编辑 3: 回答 @FutbolFan 询问如何检索 FakeResultSet。
这是一个table CategoriesOption
(Id, Price_Id, MaxAthletesByTeam)
和tables CategoriesOptionBeltColor
、CategoriesOptionAgeDivision
、CategoriesOptionWeightDivison
、CategoriesOptionGender
。那四个table基本上是一样的(Id,CategoriesOption_Id,Value).
查询如下所示:
SELECT * FROM CategoriesOption co
LEFT JOIN CategoriesOptionAgeDivision ON
CategoriesOptionAgeDivision.CategoriesOption_Id = co.Id
LEFT JOIN CategoriesOptionBeltColor ON
CategoriesOptionBeltColor.CategoriesOption_Id = co.Id
LEFT JOIN CategoriesOptionGender ON
CategoriesOptionGender.CategoriesOption_Id = co.Id
LEFT JOIN CategoriesOptionWeightDivision ON
CategoriesOptionWeightDivision.CategoriesOption_Id = co.Id
@@IDENTITY 是问题第二部分的朋友 https://msdn.microsoft.com/en-us/library/ms187342.aspx 和 Best way to get identity of inserted row?
一些 API(驱动程序)returns 来自 update() 函数的 int,即 ID,如果它是 "insert"。你用什么API/environment?
我不明白第一个问题。您不应插入标识列。
此处描述的解决方案将在多用户环境中以及目标 tables CategorySet
和 CategorySet_Category
不为空时正常工作。
我使用了您 SQL Fiddle.
第一部分很简单
(ab)将 MERGE
与 OUTPUT
子句一起使用。
MERGE
可以 INSERT
、UPDATE
和 DELETE
行。在我们的例子中,我们只需要 INSERT
。 1=0
始终为 false,因此始终执行 NOT MATCHED BY TARGET
部分。通常,可能还有其他分支,请参阅文档。 WHEN MATCHED
通常用于UPDATE
; WHEN NOT MATCHED BY SOURCE
通常用于DELETE
,但我们这里不需要。
MERGE
的这种复杂形式等同于简单的 INSERT
,但与简单的 INSERT
不同的是,它的 OUTPUT
子句允许引用我们需要的列。
MERGE INTO CategorySet
USING
(
SELECT
FakeResultSet.Championship_Id
,FakeResultSet.Price_Id
,FakeResultSet.MaxAthletesByTeam
,Category.Id AS Category_Id
FROM
FakeResultSet
INNER JOIN Category ON
Category.AgeDivision_Id = FakeResultSet.AgeDivision_Id AND
Category.Gender = FakeResultSet.Gender AND
Category.BeltColor = FakeResultSet.BeltColor AND
Category.WeightDivision = FakeResultSet.WeightDivision
) AS Src
ON 1 = 0
WHEN NOT MATCHED BY TARGET THEN
INSERT
(Championship_Id
,Price_Id
,MaxAthletesByTeam)
VALUES
(Src.Championship_Id
,Src.Price_Id
,Src.MaxAthletesByTeam)
OUTPUT inserted.id AS CategorySet_Id, Src.Category_Id
INTO CategorySet_Category (CategorySet_Id, Category_Id)
;
FakeResultSet
与 Category
相结合得到 FakeResultSet
的每一行 Category.id
。假定 Category
具有 AgeDivision_Id, Gender, BeltColor, WeightDivision
.
在 OUTPUT
子句中,我们需要来自源和目标 table 的列。简单的 INSERT
语句中的 OUTPUT
子句没有提供它们,因此我们在这里使用 MERGE
。
上面的 MERGE
查询将使用生成的 ID 将 8 行插入 CategorySet
并向 CategorySet_Category
插入 8 行。
第二部分
需要临时 table。我将使用 table 变量来存储生成的 ID。
DECLARE @T TABLE (
CategorySet_Id int
,AgeDivision_Id int
,Gender int
,BeltColor int);
我们需要记住生成的 CategorySet_Id
以及导致它的 AgeDivision_Id, Gender, BeltColor
的组合。
MERGE INTO CategorySet
USING
(
SELECT
FakeResultSet.Championship_Id
,FakeResultSet.Price_Id
,FakeResultSet.MaxAthletesByTeam
,FakeResultSet.AgeDivision_Id
,FakeResultSet.Gender
,FakeResultSet.BeltColor
FROM
FakeResultSet
GROUP BY
FakeResultSet.Championship_Id
,FakeResultSet.Price_Id
,FakeResultSet.MaxAthletesByTeam
,FakeResultSet.AgeDivision_Id
,FakeResultSet.Gender
,FakeResultSet.BeltColor
) AS Src
ON 1 = 0
WHEN NOT MATCHED BY TARGET THEN
INSERT
(Championship_Id
,Price_Id
,MaxAthletesByTeam)
VALUES
(Src.Championship_Id
,Src.Price_Id
,Src.MaxAthletesByTeam)
OUTPUT
inserted.id AS CategorySet_Id
,Src.AgeDivision_Id
,Src.Gender
,Src.BeltColor
INTO @T(CategorySet_Id, AgeDivision_Id, Gender, BeltColor)
;
上面的 MERGE
将根据需要对 FakeResultSet
进行分组,并将 2 行插入 CategorySet
并插入 2 行到 @T
.
然后加入@T
和Category
得到Category.IDs
:
INSERT INTO CategorySet_Category (CategorySet_Id, Category_Id)
SELECT
TT.CategorySet_Id
,Category.Id AS Category_Id
FROM
@T AS TT
INNER JOIN Category ON
Category.AgeDivision_Id = TT.AgeDivision_Id AND
Category.Gender = TT.Gender AND
Category.BeltColor = TT.BeltColor
;
这会将 8 行插入 CategorySet_Category
。
这里不是完整的答案,而是您可以用来解决这个问题的方向:
第一个查询:
select row_number() over(order by t, Id) as n, Championship_Id
from (
select distinct 0 as t, b.Id, a.Championship_Id
from FakeResultSet as a
inner join
Category as b
on
a.AgeDivision_Id=b.AgeDivision_Id and
a.Gender=b.Gender and
a.BeltColor=b.BeltColor and
a.WeightDivision=b.WeightDivision
union all
select distinct 1, BeltColor, Championship_Id
from FakeResultSet
) as q
第二次查询:
select q2.CategorySet_Id, c.Id as Category_Id from (
select row_number() over(order by t, Id) as CategorySet_Id, Id, BeltColor
from (
select distinct 0 as t, b.Id, null as BeltColor
from FakeResultSet as a
inner join
Category as b
on
a.AgeDivision_Id=b.AgeDivision_Id and
a.Gender=b.Gender and
a.BeltColor=b.BeltColor and
a.WeightDivision=b.WeightDivision
union all
select distinct 1, BeltColor, BeltColor
from FakeResultSet
) as q
) as q2
inner join
Category as c
on
(q2.BeltColor is null and q2.Id=c.Id)
OR
(q2.BeltColor = c.BeltColor)
当然这仅适用于空的 CategorySet
和 CategorySet_Category
表,但您可以使用 select coalese(max(Id), 0) from CategorySet
获取当前数字并将其添加到 row_number
,因此您将获得真实 ID,该 ID 将被插入到 CategorySet 行中以供第二次查询
当我 运行 遇到这些情况时,我所做的是创建一个或多个临时 tables with row_number() over 子句给我在临时 [=13] 上的身份=]秒。然后我检查实际 table 中每条记录是否存在,如果它们存在,则用实际记录 ID 更新临时 table。最后,我 运行 在缺少实际 ID 的临时 table 记录上存在 while 循环,一次插入一个,插入后我用实际 ID 更新临时 table 记录。这使您能够以受控方式处理所有数据。
下面的查询将为 CategorySet 行提供最终结果:
SELECT
ROW_NUMBER () OVER (PARTITION BY Championship_Id ORDER BY Championship_Id) RNK,
Championship_Id
FROM
(
SELECT
Championship_Id
,BeltColor
FROM #FakeResultSet
UNION ALL
SELECT
Championship_Id,BeltColor
FROM #FakeResultSet
GROUP BY Championship_Id,BeltColor
)BASE