【问题标题】:SQL: Upsert and get the old and the new valuesSQL:Upsert 并获取旧值和新值
【发布时间】:2017-03-16 19:26:12
【问题描述】:

我有以下表格项目:

Id          MemberId          MemberGuid        ExpiryYear         Hash    
--------------------------------------------------------------------------- 
1           1                 Guid1             2017               Hash1
2           1                 Guid2             2018               Hash2
3           2                 Guid3             2020               Hash3
4           2                 Guid4             2017               Hash1

我需要将项目从一个成员复制到另一个成员(不仅仅是更新 MemberId,插入新记录)。规则是:如果我想将所有项目从一个成员迁移到另一个成员,我必须检查该项目在新成员中是否存在。

例如,如果我想将成员 1 中的项目移动到成员 2,我将只移动 id 为 2 的项目,因为我在成员 2 处已经有一个具有相同哈希和相同到期年份的项目(这是我在插入新项目之前需要检查的列)。

如何编写一个查询,仅将不存在的项目从一个成员迁移到另一个成员并获取记录的旧 id 和新 id?不知何故有一个 upsert?

【问题讨论】:

  • 我使用的是 Sql server 2014
  • MemberGuid 是从旧项目复制的还是计算出来的?旧id会记录在哪里?
  • 生成为新的 guid,仅复制到期年份和哈希

标签: sql sql-server tsql upsert sql-merge


【解决方案1】:

你可以如下:

-- MOCK DATA
DECLARE @Tbl TABLE
(    
    Id INT IDENTITY NOT NULL PRIMARY KEY,
    MemberId INT,
    MemberGuid CHAR(5),
    ExpiryYear CHAR(4),
    Hash CHAR(5)
)   

INSERT INTO @Tbl        
VALUES
(1, 'Guid1', '2017', 'Hash1'),
(1, 'Guid2', '2018', 'Hash1'),
(2, 'Guid3', '2020', 'Hash3'),
(2, 'Guid4', '2017', 'Hash1')
-- MOCK DATA

-- Parameters
DECLARE @FromParam INT = 1 
DECLARE @ToParam INT = 2


DECLARE @TmpTable TABLE (NewDataId INT, OldDataId INT)

MERGE @Tbl AS T
USING
(
    SELECT * FROM @Tbl
    WHERE MemberId = @FromParam
) AS F
ON  T.Hash = F.Hash AND 
    T.ExpiryYear = F.ExpiryYear AND 
    T.MemberId = @ToParam
WHEN NOT MATCHED THEN
    INSERT ( MemberId, MemberGuid, ExpiryYear, Hash) 
    VALUES ( @ToParam, F.MemberGuid, F.ExpiryYear, F.Hash)
    OUTPUT inserted.Id, F.Id INTO @TmpTable;

SELECT * FROM @TmpTable

【讨论】:

    【解决方案2】:

    其实你只需要一个插入。当ExpiryYear 和Hash 匹配时,你什么都不想做。您只想从源插入到这些列不匹配的目标。您可以使用Merge 或Insert 来做到这一点。

    CREATE TABLE YourTable
    (
        Oldid INT,
        OldMemberId INT,
        Id INT,
        MemberId INT,
        MemberGuid CHAR(5),
        ExpiryYear CHAR(4),
        Hash CHAR(5)
    )   
    
    INSERT INTO YourTable VALUES
    
    (null, null, 1, 1, 'Guid1', '2017', 'Hash1'),
    (null, null, 2, 1, 'Guid2', '2018', 'Hash2'),
    (null, null, 3, 2, 'Guid3', '2020', 'Hash3'),
    (null, null, 4, 2, 'Guid4', '2017', 'Hash1')
    
    DECLARE @SourceMemberID AS INT = 1
    DECLARE @TargetMemberID AS INT = 2
    
    MERGE [YourTable] AS t
    USING
    (
        SELECT * FROM [YourTable]
        WHERE MemberId = @SourceMemberID
    ) AS s
    ON t.Hash = s.Hash AND t.ExpiryYear = s.ExpiryYear AND t.MemberId = @TargetMemberID
    WHEN NOT MATCHED THEN
        INSERT(Oldid, OldMemberId, Id, MemberId, MemberGuid, ExpiryYear, Hash) VALUES (s.Id, s.MemberId, (SELECT MAX(Id) + 1 FROM [YourTable]), @TargetMemberID, s.MemberGuid, s.ExpiryYear, s.Hash);
    
    SELECT * FROM YourTable
    
    DROP TABLE YourTable
    
    /* Output:
    Oldid   OldMemberId Id  MemberId    MemberGuid  ExpiryYear  Hash
    -----------------------------------------------------------------
    NULL    NULL        1   1           Guid1       2017        Hash1
    NULL    NULL        2   1           Guid2       2018        Hash2
    NULL    NULL        3   2           Guid3       2020        Hash3
    NULL    NULL        4   2           Guid4       2017        Hash1
    2       1           5   2           Guid2       2018        Hash2
    

    如果你只是想选择然后做如下

    SELECT null AS OldID, null AS OldMemberID, Id, MemberId, MemberGuid, ExpiryYear, Hash FROM YourTable
    UNION ALL
    SELECT A.Id AS OldID, A.MemberId AS OldMemberID, (SELECT MAX(Id) + 1 FROM YourTable) AS Id, @TargetMemberID AS MemberId, A.MemberGuid, A.ExpiryYear, A.Hash
    FROM YourTable A
    LEFT JOIN
    (
        SELECT * FROM YourTable WHERE MemberId = @TargetMemberID
    ) B ON A.ExpiryYear = B.ExpiryYear AND A.Hash = B.Hash
    WHERE A.MemberId = @SourceMemberID AND B.Id IS NULL
    

    【讨论】:

    • 但我还需要获取迁移项目的旧 ID
    • 当我从成员 1 迁移到成员 2 时,这应该是迁移项目的输出:OldId, OldMemberId, newId, NewmemberId 2,1,5,2
    • 您希望在哪一列中迁移行的旧 id ?
    • 您要添加两个新列 oldId 和 oldMemberid 吗?为此,您的表中必须有两个额外的可为空列。否则如何在 5 列表中插入 7 列(oldId、oldMemberId、Id、MemberId、MemberGuid、ExpiryYear、Hash)?
    • 所以 1:迁移项目,2:返回旧 id 和新 id。我宁愿这样做而不改变表结构
    【解决方案3】:

    第一步:

    Get in cursor all the data of member 1
    

    第 2 步:

     While moving through cursor.
            Begin
            select hash, expirydate from items where memberid=2 and hash=member1.hash and expirydate=member1.expirydate
    

    第三步

    If above brings any result, do not insert.
    
           else insert.        
    

    希望对你有帮助

    注意:这不是实际代码。我只是为你提供了编写 sql 的步骤。

    【讨论】:

    • 你可以在while循环中使用@@ROWCOUNT(return no of items Member1 has),然后在检查if语句后插入memebr2。
    猜你喜欢
    • 2015-09-26
    • 1970-01-01
    • 1970-01-01
    • 2019-03-03
    • 2014-03-21
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-10-26
    相关资源
    最近更新 更多