插入多个表

Insert into multiple tables

相关领域部分的简要说明:

一个类别由四个数据组成:

  1. 性别(Male/Female)
  2. 年龄划分(Mighty Mite to Master)
  3. 腰带颜色(白色到黑色)
  4. 重量级(公鸡到重型)

所以,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,所以我需要 CategorySetCategorySet_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

我希望我已经表达清楚了我的意图。

我也created a SQLFiddle.

编辑 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 CategoriesOptionBeltColorCategoriesOptionAgeDivisionCategoriesOptionWeightDivisonCategoriesOptionGender。那四个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.aspxBest way to get identity of inserted row?

一些 API(驱动程序)returns 来自 update() 函数的 int,即 ID,如果它是 "insert"。你用什么API/environment?

我不明白第一个问题。您不应插入标识列。

此处描述的解决方案将在多用户环境中以及目标 tables CategorySetCategorySet_Category 不为空时正常工作。 我使用了您 SQL Fiddle.

中的模式和示例数据

第一部分很简单

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

MERGE 可以 INSERTUPDATEDELETE 行。在我们的例子中,我们只需要 INSERT1=0 始终为 false,因此始终执行 NOT MATCHED BY TARGET 部分。通常,可能还有其他分支,请参阅文档。 WHEN MATCHED通常用于UPDATEWHEN 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)
;

FakeResultSetCategory 相结合得到 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.

然后加入@TCategory得到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)

当然这仅适用于空的 CategorySetCategorySet_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