【问题标题】:T-SQL - Insert Data into Parent and Child TablesT-SQL - 将数据插入父表和子表
【发布时间】:2016-11-07 20:32:29
【问题描述】:

代码:

CREATE TYPE dbo.tEmployeeData AS TABLE 
(
     FirstName NVARCHAR(50),
     LastName NVARCHAR(50),
     DepartmentType NVARCHAR(10),
     DepartmentBuilding NVARCHAR(50),
     DepartmentEmployeeLevel NVARCHAR(10),
     DepartmentTypeAMetadata NVARCHAR(100),
     DepartmentTypeBMetadata NVARCHAR(100)
)
GO

CREATE PROC dbo.EmployeeImport 
    (@tEmployeeData tEmployeeData READONLY)
AS
BEGIN
     DECLARE @MainEmployee TABLE 
                           (EmployeeID INT IDENTITY(1,1),
                            FirstName NVARCHAR(50),
                            LastName NVARCHAR(50))

    DECLARE @ParentEmployeeDepartment TABLE 
                                      (EmployeeID INT,
                                       ParentEmployeeDepartmentID INT IDENTITY(1,1),
                                       DepartmentType NVARCHAR(10))

    DECLARE @ChildEmployeeDepartmentTypeA TABLE 
                  (ParentEmployeeDepartmentID INT,
                   DepartmentBuilding NVARCHAR(50),
                   DepartmentEmployeeLevel NVARCHAR(10),
                   DepartmentTypeAMetadata NVARCHAR(100))

    DECLARE @ChildEmployeeDepartmentTypeB TABLE 
                  (ParentEmployeeDepartmentID INT,
                   DepartmentBuilding NVARCHAR(50),
                   DepartmentEmployeeLevel NVARCHAR(10),
                   DepartmentTypeBMetadata NVARCHAR(100))

    -- INSERT CODE GOES HERE
    SELECT * FROM @MainEmployee
    SELECT * FROM @ParentEmployeeDepartment
    SELECT * FROM @ChildEmployeeDepartmentTypeA
    SELECT * FROM @ChildEmployeeDepartmentTypeB
END
GO

DECLARE @tEmployeeData tEmployeeData

INSERT INTO @tEmployeeData (FirstName, LastName, DepartmentType,
                            DepartmentBuilding, DepartmentEmployeeLevel,
                            DepartmentTypeAMetadata, DepartmentTypeBMetadata)
    SELECT  
        N'Tom_FN', N'Tom_LN', N'A',
        N'101', N'IV', N'Tech/IT', NULL
    UNION
    SELECT  
        N'Mike_FN', N'Mike_LN', N'B',
        N'OpenH', N'XII', NULL, N'Med' 
    UNION
    SELECT  
        N'Joe_FN', N'Joe_LN', N'A',
        N'101', N'IV', N'Tech/IT', NULL
    UNION
    SELECT  
        N'Dave_FN', N'Dave_LN', N'B',
        N'OpenC', N'XII', NULL, N'Lab' 

    EXEC EmployeeImport @tEmployeeData
GO

DROP PROC dbo.EmployeeImport 
DROP TYPE dbo.tEmployeeData

注意事项:

  • 表格变量在实时环境中被真实表格替换。

  • EmployeeIDParentEmployeeDepartmentID 列的值并不总是相互匹配。 Live 环境在 udt (tEmployeeData) 中的记录多于 4 条

目标:

  • udt (tEmployeeData) 将被传递到过程中

  • 该过程应首先将数据插入@MainEmployee表中(并获取EmployeeIDs)

  • 接下来,该过程应该将数据插入@ParentEmployeeDepartment 表(并获取ParentEmployeeDepartmentID) - 注意EmployeeID 来自先前的输出。

  • 然后,该过程应根据DepartmentType 拆分子级数据(“A” = 插入@ChildEmployeeDepartmentTypeA 和“B” = 插入@ChildEmployeeDepartmentTypeB)。

  • 在将数据插入@ChildEmployeeDepartmentTypeA@ChildEmployeeDepartmentTypeB 时,应使用来自@ParentEmployeeDepartmentParentEmployeeDepartmentID

  • 程序应该运行得快(需要避免逐行操作)

输出:

@MainEmployee

EmployeeID  FirstName   LastName
---------------------------------
1           Tom_FN      Tom_LN
2           Mike_FN     Mike_LN
3           Joe_FN      Joe_LN
4           Dave_FN     Dave_LN

@ParentEmployeeDepartment

EmployeeID  ParentEmployeeDepartmentID  DepartmentType
-------------------------------------------------------
1           1                           A
2           2                           B
3           3                           A
4           4                           B

@ChildEmployeeDepartmentTypeA

ParentEmployeeDepartmentID  DepartmentBuilding  DepartmentEmployeeLevel DepartmentTypeAMetadata
---------------------------------------------------------------------------------------------------------
1                           101                 IV                      Tech/IT
3                           101                 IV                      Tech/IT

@ChildEmployeeDepartmentTypeB

ParentEmployeeDepartmentID  DepartmentBuilding  DepartmentEmployeeLevel DepartmentTypeAMetadata
----------------------------------------------------------------------------------------------------------
2                           OpenH               XII                     Med
4                           OpenC               XII                     Lab

我知道我可以在插入后使用OUTPUT 子句并获得EmployeeIDParentEmployeeDepartmentID,但我不确定如何将正确的子记录插入到正确映射到父表的正确表中。任何帮助将不胜感激。

【问题讨论】:

  • USE OUTPUT Inserted.EmployedId, Inserted.ParentEmployeeDepartmentType INTO TempTable (employeeid, ...) 然后为子项执行 2 条插入语句,其中部门 = A,然后为下一个部门 = b。
  • You might want to look at this answer。我的解决方案是为每个表使用不同的 UDT,但如果必须,可以将其转换为使用单个 UDT。
  • @Matt,例如,如何防止 ParentEmployeeDepartmentID 的 2 与 4 的子数据错误映射? ZoharPeled,我会研究那个解决方案。我认为,这种情况的问题是我们没有主/父表的唯一“id”字段。
  • 进一步看,是的,您将需要一个临时密钥。这可以通过将您的 UDT 转储到临时表中并分配一个身份来完成,或者按照 Zohar 的建议扩展您的 udt 以保存一个身份。如果没有密钥,您将不得不依赖名字和姓氏组合是唯一的,这在一个小的设置中可能是正确的,但不会持续很长时间。虽然我并不总是 MERGE 的粉丝,但我确实喜欢 Zohar 使用它来维护键之间的关系。
  • @matt 谢谢,我对合并也有同样的感觉。但是,这是我所知道的从源表和插入的数据中获取输出的唯一方法。

标签: sql sql-server sql-server-2012


【解决方案1】:

这是我的解决方案(基于我在 cmets 中链接到的 answer):

首先,您必须在 UDT 中添加另一列,以保存员工的临时 ID:

CREATE TYPE dbo.tEmployeeData AS TABLE 
(
     FirstName NVARCHAR(50),
     LastName NVARCHAR(50),
     DepartmentType NVARCHAR(10),
     DepartmentBuilding NVARCHAR(50),
     DepartmentEmployeeLevel NVARCHAR(10),
     DepartmentTypeAMetadata NVARCHAR(100),
     DepartmentTypeBMetadata NVARCHAR(100),
     EmployeeId int
)
GO

用新的employeeId 列填充它:

DECLARE @tEmployeeData tEmployeeData

INSERT INTO @tEmployeeData (FirstName, LastName, DepartmentType,
                            DepartmentBuilding, DepartmentEmployeeLevel,
                            DepartmentTypeAMetadata, DepartmentTypeBMetadata, EmployeeId)
SELECT  
    N'Tom_FN', N'Tom_LN', N'A',
    N'101', N'IV', N'Tech/IT', NULL, 5
UNION
SELECT  
    N'Mike_FN', N'Mike_LN', N'B',
    N'OpenH', N'XII', NULL, N'Med', 6
UNION
SELECT  
    N'Joe_FN', N'Joe_LN', N'A',
    N'101', N'IV', N'Tech/IT', NULL, 7
UNION
SELECT  
    N'Dave_FN', N'Dave_LN', N'B',
    N'OpenC', N'XII', NULL, N'Lab', 8

此处插入部分

然后,您使用表变量将插入的值从员工表映射到您发送到过程的数据中的临时员工 ID:

DECLARE @EmployeeidMap TABLE
(
    temp_id int,
    id int
)

现在,诀窍是使用 MERGE 语句而不是 INSERT...SELECT 填充员工表,因为您必须在输出子句中使用插入数据和源数据中的值:

MERGE INTO @MainEmployee USING @tEmployeeData AS sourceData ON 1 = 0 -- Always not matched
WHEN NOT MATCHED THEN
INSERT (FirstName, LastName)
VALUES (sourceData.FirstName, sourceData.LastName)
OUTPUT sourceData.EmployeeId, inserted.EmployeeID 
INTO @EmployeeidMap (temp_id, id); -- populate the map table

从那时起就很简单了,您需要加入您发送到@EmployeeidMap 的数据以获取实际的employeeId:

INSERT INTO @ParentEmployeeDepartment (EmployeeID, DepartmentType)
SELECT Id, DepartmentType
FROM @tEmployeeData 
INNER JOIN @EmployeeidMap ON EmployeeID = temp_id

现在您可以使用@ParentEmployeeDepartment 中的数据将ParentEmployeeDepartmentID 中的实际值映射到您发送的数据:

到目前为止测试插入

SELECT FirstName,
     LastName,
     SentData.DepartmentType As [Dept. Type],
     DepartmentBuilding As Building,
     DepartmentEmployeeLevel As [Emp. Level],
     DepartmentTypeAMetadata As [A Meta],
     DepartmentTypeBMetadata As [B Meta],
     SentData.EmployeeId As TempId, EmpMap.id As [Emp. Id], DeptMap.ParentEmployeeDepartmentID As [Dept. Id]
FROM @tEmployeeData SentData
INNER JOIN @EmployeeidMap EmpMap ON SentData.EmployeeId = temp_id 
INNER JOIN @ParentEmployeeDepartment DeptMap ON EmpMap.id = DeptMap.EmployeeID

结果:

FirstName   LastName    Dept. Type  Building    Emp. Level  A Meta      B Meta  TempId      Emp. Id     Dept. Id
---------   --------    ----------  --------    ----------  ------      ------  ------      ----------- -----------
Dave_FN     Dave_LN     B           OpenC       XII         NULL        Lab     8           1           1
Joe_FN      Joe_LN      A           101         IV          Tech/IT     NULL    7           2           2
Mike_FN     Mike_LN     B           OpenH       XII         NULL        Med     6           3           3
Tom_FN      Tom_LN      A           101         IV          Tech/IT     NULL    5           4           4

我敢肯定,从这一点开始,您可以自己轻松找出最后 2 个插入。

【讨论】:

  • 这太棒了!!!感谢 MERGE Output 子句选项提示(与 INSERT 相比)。很酷,谢谢!!
猜你喜欢
  • 1970-01-01
  • 2011-11-06
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-12-18
  • 1970-01-01
  • 2017-04-11
相关资源
最近更新 更多