插入多个表

时间:2015-07-30 18:29:25

标签: sql sql-server sql-server-2014

有关域名部分的简要说明:

类别由四个数据组成:

  1. 性别(男/女)
  2. 年龄划分(Mighty Mite to Master)
  3. 腰带颜色(白色至黑色)
  4. 重量分区(雄鸡到重型)
  5. 因此,Male Adult Black Rooster形成一个类别。可能不存在某些组合,例如强大的螨黑带。

    运动员与同一类别的运动员作战,如果他分类,他会与不同重量级别的运动员作战(但性别,年龄和腰带相同)。

    进行建模。我有一个Category表,已经填充了域中存在的所有组合。

    CREATE TABLE Category (
      [Id] [int] IDENTITY(1,1) NOT NULL,
      [AgeDivision_Id] [int] NULL,
      [Gender] [int] NULL,
      [BeltColor] [int] NULL,
      [WeightDivision] [int] NULL
    )
    

    CategorySetCategorySet_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              |
    

    因为运动员可以对抗两个分类集,我需要以两种不同的方式填充CategorySetCategorySet_Category(可以是两个查询):

    每行一Category_Set条,其中一个CategorySet_Category指向相应的Category

    一个Category_Set,在同一AgeDivision_Id,BeltColor,Gender中的一个CategorySet中对所有WeightDivisions进行分组。在此示例中,只有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返回ID。问题是,SCOPE_IDENTITY仅返回最后插入的Id,我在CategorySet中插入了多行。

    编辑3:回答@FutbolFan谁询问如何检索FakeResultSet。

    这是一个表CategoriesOption(Id,Price_Id,MaxAthletesByTeam)

    表格CategoriesOptionBeltColorCategoriesOptionAgeDivisionCategoriesOptionWeightDivisonCategoriesOptionGender。这四个表基本相同(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
    

5 个答案:

答案 0 :(得分:2)

此处描述的解决方案可在多用户环境中正常运行,并且目标表CategorySetCategorySet_Category不为空时。 我使用了SQL Fiddle中的架构和示例数据。

第一部分是直截了当的

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

MERGE可以INSERTUPDATEDELETE行。在我们的例子中,我们只需要INSERT1=0始终为false,因此始终会执行NOT MATCHED BY TARGET部分。一般来说,可能还有其他分支,请参阅docs。 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)
;

FakeResultSetCategory合并,为Category.id的每一行获取FakeResultSet。假设Category具有AgeDivision_Id, Gender, BeltColor, WeightDivision的唯一组合。

OUTPUT子句中,我们需要来自源表和目标表的列。简单OUTPUT语句中的INSERT子句不提供它们,因此我们在此使用MERGE

上面的MERGE查询会在CategorySet中插入8行,并使用生成的ID在CategorySet_Category中插入8行。

第二部分

需要临时表。我将使用表变量来存储生成的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进行分组,并在CategorySet中插入2行,在@T中插入2行。

然后与@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
;

这将在CategorySet_Category中插入8行。

答案 1 :(得分:1)

这不是完整的答案,而是可以用来解决这个问题的方向:

第一次查询:

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,这样您就可以获取将插入到第二个查询的CategorySet行中的真实ID

答案 2 :(得分:1)

当我遇到这些情况时,我所做的是创建一个或多个带有row_number()的临时表,在子句中为我提供临时表的身份。然后我检查实际表中是否存在每条记录,如果存在,则用实际记录ID更新临时表。最后,我在临时表记录上运行了一个while循环记录,错过了实际的id并一次插入一个,在插入后我用实际的id更新临时表记录。这使您可以以受控方式处理所有数据。

答案 3 :(得分:0)

@@ IDENTITY是你问题第二部分的朋友 https://msdn.microsoft.com/en-us/library/ms187342.aspxBest way to get identity of inserted row?

某些API(驱动程序)从update()函数返回int,即ID为" insert"。您使用什么API /环境?

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

答案 4 :(得分:0)

下面的查询将给出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