T-SQL - 将数据插入到父子表中

7

代码:

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 列的值并不总是相互匹配的。在 udt (tEmployeeData) 中有比 4 条记录更多的记录。

目标:

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

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

  • 然后,该过程应将数据插入到 @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,但我不确定如何将正确的子记录插入到正确的表中,并与父表进行正确的映射。希望能得到帮助。


使用OUTPUT inserted.EmployedId, inserted.ParentEmployeeDepartmentType语句将employeeid和...插入到TempTable中,然后分别执行两个INSERT语句,一个是department = A的子级,另一个是department = b的下一个子级。 - Matt
你可能想看一下这个答案:https://dev59.com/-ZPea4cB1Zd3GeqP-hUd#34832231。我的解决方案是为每个表使用不同的UDT,但如果必须,可以将其转换为使用单个UDT。 - Zohar Peled
@Matt,比如说,我该如何防止将ParentEmployeeDepartmentID为2的子数据错误地映射到4?ZoharPeled,我会研究一下这个解决方案。我认为这种情况的问题在于我们没有针对主/父表的唯一“id”字段。 - 007
进一步看,是的,您需要一个临时密钥。这可以通过将UDT转储到临时表中并分配标识或扩展UDT以持有标识符来完成,就像Zohar建议的那样。没有密钥,您必须依赖于名字和姓氏组合是唯一的,在小范围内可能是正确的,但不会长久保持。虽然我并不总是喜欢MERGE,但我确实喜欢Zohar使用它来维护键之间关系的方法。 - Matt
@matt 谢谢,我对合并的看法也是如此。然而,这是我所知道的唯一获取源表和插入数据输出的方法。 - Zohar Peled
1个回答

12

这是我的解决方案(基于我在评论中链接的同一个答案):

首先,您必须向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的数据,以获取实际的员工ID:
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

我确信你现在可以轻松地自己找出最后两个插入点。


太棒了!感谢您提供MERGE输出子句选项提示(与INSERT相比)。非常酷,谢谢! - 007

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接