SQL Server - 使用游标复制多个表

时间:2016-04-15 07:42:39

标签: sql sql-server

我被赋予了一个使用游标来复制表的任务。现在说我有3个表可以复制:Company,Person_Link&人。这是一个“一对多”的关系,因为一家公司可以拥有多个人。

这是我的代码:

SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[DuplicateCompanyInfo]
    @Comp_CompanyId NVARCHAR(80)
AS
BEGIN
    SET NOCOUNT ON;

    DECLARE @CompanyID NVARCHAR(30),
            @PersonID NVARCHAR(30),
            @PersonLinkID NVARCHAR(30),
            @AddressLinkID NVARCHAR(30),
            @AddressID NVARCHAR(30),
            @PhoneLinkID NVARCHAR(30),
            @PhoneID NVARCHAR(30)

    EXEC @companyId = crm_next_id 5
    EXEC @PersonId = crm_next_id 13
    EXEC @PersonLinkId = crm_next_id 31
    EXEC @AddressLinkId = crm_next_id 21
    EXEC @AddressId = crm_next_id 1
    EXEC @PhoneLinkId = crm_next_id 10208
    EXEC @PhoneId = crm_next_id 14

    -- Add Company
    INSERT INTO Company
    (
        Comp_CompanyId, Comp_PrimaryPersonId, Comp_PrimaryAddressId, Comp_Name, Comp_Type, Comp_Status, Comp_CreatedBy,
        Comp_CreatedDate, Comp_UpdatedBy, Comp_UpdatedDate, Comp_TimeStamp, Comp_SecTerr, Comp_WebSite
    )
    SELECT  @companyId, @PersonId, @AddressId, Comp_Name, Comp_Type, Comp_Status, Comp_CreatedBy,
            Comp_CreatedDate, '1', GETDATE(), Comp_TimeStamp, Comp_SecTerr, Comp_WebSite

    FROM Company
    WHERE Comp_CompanyId = @comp_companyid


------- PersonLink Cursor ---------------------------------------------
    -- Declare Variables
    DECLARE @c_PeLi_PersonLinkId NVARCHAR(30)
    DECLARE @c_PeLi_PersonId NVARCHAR(30)
    DECLARE @c_PeLi_CompanyID NVARCHAR(30)
    DECLARE @c_PeLi_CreatedBy NVARCHAR(30)
    DECLARE @c_PeLi_CreatedDate NVARCHAR(30)
    DECLARE @c_PeLi_UpdatedBy NVARCHAR(30)
    DECLARE @c_PeLi_UpdatedDate NVARCHAR(30)
    DECLARE @c_PeLi_TimeStamp NVARCHAR(30)


    --Declare Cursor
    DECLARE @getPeLiID CURSOR
    SET @getPeLiID= CURSOR FOR
    SELECT PeLi_PersonLinkId, PeLi_PersonId, PeLi_CompanyID, PeLi_CreatedBy, PeLi_CreatedDate, PeLi_UpdatedBy,
        PeLi_UpdatedDate, PeLi_TimeStamp
    FROM Person_Link
    INNER JOIN Person
    ON PeLi_PersonId = Pers_PersonId
    AND PeLi_CompanyID = @comp_companyid

    --Open Cursor & fetch 1st row into variables
    OPEN @getPeLiID
    FETCH NEXT FROM @getPeLiID INTO @c_PeLi_PersonLinkId, @c_PeLi_PersonId, @c_PeLi_CompanyID, @c_PeLi_CreatedBy,
                                  @c_PeLi_CreatedDate, @c_PeLi_UpdatedBy, @c_PeLi_UpdatedDate, @c_PeLi_TimeStamp


    --Fetch successful
    --Check for a new row
    WHILE @@FETCH_STATUS = 0
    BEGIN

        --EXEC @PersonLinkId = crm_next_id 31


    INSERT INTO Person_Link
    (
        PeLi_PersonLinkId, PeLi_PersonId, PeLi_CompanyID, PeLi_CreatedBy, PeLi_CreatedDate, PeLi_UpdatedBy,
        PeLi_UpdatedDate, PeLi_TimeStamp
    )
    VALUES
    (
        @PersonLinkId, @PersonId, @CompanyId, @c_PeLi_CreatedBy, @c_PeLi_CreatedDate, '1', GETDATE(), @c_PeLi_TimeStamp
    )


    --Get next available row into variables
    FETCH NEXT FROM @getPeLiID INTO @c_PeLi_PersonLinkId, @c_PeLi_PersonId, @c_PeLi_CompanyID, @c_PeLi_CreatedBy,
                                  @c_PeLi_CreatedDate, @c_PeLi_UpdatedBy, @c_PeLi_UpdatedDate, @c_PeLi_TimeStamp

    END

    CLOSE @getPeLiID
    DEALLOCATE @getPeLiID


------- Person Cursor -------------------------------------------------
    -- Declare Variables
    DECLARE @c_Pers_PersonId NVARCHAR(30)
    DECLARE @c_Pers_CompanyId NVARCHAR(30)
    DECLARE @c_Pers_PrimaryUserId NVARCHAR(30)
    DECLARE @c_Pers_FirstName NVARCHAR(30)
    DECLARE @c_Pers_SecTerr NVARCHAR(30)
    DECLARE @c_Pers_CreatedBy NVARCHAR(30)
    DECLARE @c_Pers_CreatedDate NVARCHAR(30)
    DECLARE @c_Pers_UpdatedBy NVARCHAR(30)
    DECLARE @c_Pers_UpdatedDate NVARCHAR(30)
    DECLARE @c_Pers_TimeStamp NVARCHAR(30)


    --Declare Cursor
    DECLARE @getPersID CURSOR
    SET @getPersID= CURSOR FOR
    SELECT Pers_PersonId, Pers_CompanyId, Pers_PrimaryUserId, Pers_FirstName, Pers_SecTerr, Pers_CreatedBy,
           Pers_CreatedDate, Pers_UpdatedBy, Pers_UpdatedDate, Pers_TimeStamp
    FROM Person
    INNER JOIN Person_Link
    ON Pers_PersonId = PeLi_PersonId
    AND PeLi_CompanyID = @comp_companyid    


    --Open Cursor & fetch 1st row into variables
    OPEN @getPersID
    FETCH NEXT FROM @getPersID INTO @c_Pers_PersonId, @c_Pers_CompanyId, @c_Pers_PrimaryUserId, @c_Pers_FirstName,
                                  @c_Pers_SecTerr, @c_Pers_CreatedBy, @c_Pers_CreatedDate, @c_Pers_UpdatedBy,
                                  @c_Pers_UpdatedDate, @c_Pers_TimeStamp


    --Fetch successful
    --Check for a new row
    WHILE @@FETCH_STATUS = 0
    BEGIN

    --EXEC @PersonId = crm_next_id 13


    INSERT INTO Person
    (
        Pers_PersonId, Pers_CompanyId, Pers_PrimaryUserId, Pers_FirstName, Pers_SecTerr, Pers_CreatedBy,
        Pers_CreatedDate, Pers_UpdatedBy, Pers_UpdatedDate, Pers_TimeStamp
    )
    VALUES
    (
        @PersonId, @companyId, @c_Pers_PrimaryUserId, @c_Pers_FirstName, @c_Pers_SecTerr, @c_Pers_CreatedBy,
        @c_Pers_CreatedDate, '1', GETDATE(), @c_Pers_TimeStamp
    )


    --Get next available row into variables
    FETCH NEXT FROM @getPersID INTO @c_Pers_PersonId, @c_Pers_CompanyId, @c_Pers_PrimaryUserId, @c_Pers_FirstName,
                                  @c_Pers_SecTerr, @c_Pers_CreatedBy, @c_Pers_CreatedDate, @c_Pers_UpdatedBy,
                                  @c_Pers_UpdatedDate, @c_Pers_TimeStamp

    END

    CLOSE @getPersID
    DEALLOCATE @getPersID

这是我的表结构:

公司表:

comp_companyid | comp_primarypersonid | comp_primaryaddressid | comp_name
-------------------------------------------------------------------------
2              | 2                    | 2                     | company 2
3              | 3                    | 3                     | company 3

人员表:

pers_personid  | pers_companyid       | pers_primaryaddressid | pers_name
-------------------------------------------------------------------------
2              | 2                    | 2                     | person 2
3              | 3                    | 3                     | person 3
4              | 2                    | 2                     | person 4

人员链接表:

peli_personlinkid |    peli_personid     | peli_companyid
-------------------------------------------------------------------------
2                 | 2                    | 2                  
3                 | 3                    | 3                                

当只有1个人属于1家公司时,执行存储过程没有问题,但是当公司有2个人或以上时,第2个人的行不会重复,只会是第一个。

将弹出错误消息:

  

Msg 2627,Level 14,State 1,Procedure DuplicateCompanyInfo,Line 80
  违反PRIMARY KEY约束'PK__Person_L__BCD268642CF2ADDF'。无法在对象'dbo.Person_Link'中插入重复键。

     

消息2627,级别14,状态1,过程DuplicateCompanyInfo,第141行
  违反PRIMARY KEY约束'PK__Person__381A5AC529221CFB'。无法在对象'dbo.Person'中插入重复键。

我可以通过删除评论--EXEC @PersonLinkId = crm_next_id 31&来解决此问题。光标开始后--EXEC @PersonID = crm_next_id 13,以便每次重复时都会生成一个新ID。但它会导致Comp_PrimaryPersonID& Pers_PersonID& PeLi_PersonID没有计算,因为他们每个人都会根据他们的表复制生成自己的新ID而不是互相应用外键。

请帮助或指导我完成!在此先感谢您的时间和努力! 德斯蒙德

1 个答案:

答案 0 :(得分:1)

抱歉,目前无法访问SQL-Server。

我无法在您插入数据的位置看到您的表定义。但是从错误看起来你的表定义不正确。即,您的主键选择对于person和person_link表不正确。您的当前主键应该是外键。

第二,为什么游标?如果你只需要将一组表中的数据复制到另一组表,为什么不只是使用查询?是否有使用光标的具体原因?

<强>更新 从更新的表结构中,主键和外键不明确,但看起来comp_companyid是公司表中的主键,它在Person(pers_companyid)和Person_Link(peli_companyid)表中作为外键链接。

现在,如果您的表格结构如下:

公司表: comp_companyid(PK)

人员表: pers_personid(PK)和pers_companyid(FK)

人员链接表: peli_personlinkid(PK)和peli_personid(FK),peli_companyid(FK)

除非您在PK列中插入重复项,否则我看不到任何重复问题。

现在仔细查看代码后,您将把peli_personlinkid提取到@c_PeLi_PersonLinkId变量中。

FETCH NEXT FROM @getPeLiID INTO @c_PeLi_PersonLinkId,@ c_PeLi_PersonId,@ c_PeLi_CompanyID,@ c_PeLi_CreatedBy,@ c_PeLi_CreatedDate,@ c_PeLi_UpdatedBy,@ c_PeLi_UpdatedDate,@ c_PeLi_TimeStamp

但是当您插入数据时,您没有插入此值,而是在主键中插入@PersonLinkId。由于此值来自另一个存储过程,(我不知道这个存储过程正在做什么,但看起来像生成下一个id并返回)。您在程序开始时执行的是

EXEC @PersonLinkId = crm_next_id 31

因此,在循环中,您继续为每个person_link插入此值。如果表中有多个person_link,那当然违反了PK约束。完全相同的问题也出现在Person表中。

这就是您遇到主键违规错误的原因。

不应每次都插入此值,而应插入从@c_PeLi_PersonLinkId中检索到的取值行的值。

希望这会有所帮助,您将能够解决问题。

老实说,我仍然相信你可以和你的大四学生交谈,并且在这种情况下可以很容易地避免使用游标,你可以通过简单的insert和select语句实现所有这些,并且这样做也会更好,更快。但是,如果您无法替换游标,则可以按上述标识和建议修复存储过程。