IT博客汇
  • 首页
  • 精华
  • 技术
  • 设计
  • 资讯
  • 扯淡
  • 权利声明
  • 登录 注册

    T-SQL合并多对多表的数据

    汪宇杰发表于 2016-02-04 03:32:12
    love 0

    最近在整理博客的数据,需要做一个操作就是合并文章的分类。我的博客中文章和分类是多对多的关系。即一篇文章可以属于多个分类,一个分类可以包含多篇文章。这是一个很典型的多对多关系,我用的是一个多对多的表,做联合主键关联这些数据。

    就像这样:

    我需要做的是把“DotNetBeginner”这个分类的文章移到“CSharpAndDotNet”分类里去。但是因为原先在“DotNetBeginner”里的文章有些也是属于“CSharpAndDotNet”的,所以直接Update关联表的话,会产生重复的联合主键,就会爆。

    直观一点看,写个SQL语句查询出原分类(DotNetBeginner)和目标分类(CSharpAndDotNet)中的数据:

    DECLARE @SourceCatId     AS UNIQUEIDENTIFIER,
            @TargetCatId     AS UNIQUEIDENTIFIER
     
    SET @SourceCatId = '678A4011-40E0-4F75-BC23-1FFC25B72D4A'
    SET @TargetCatId = 'D58043FF-B3CB-43DA-9067-522D76D21BE3'
    
    SELECT p.Title,
           c.Name
    FROM   PostCategory         AS pc
           INNER JOIN Post      AS p
                ON  p.Id = pc.PostId
           INNER JOIN Category  AS c
                ON  c.Id = pc.CategoryId
    WHERE  pc.CategoryId IN (@SourceCatId, @TargetCatId)

    结果如下图:

    《C#字符和ASCII码互转》这篇如果直接update分类到CSharpAndDotNet就会爆,因为PostCategory表中已经有一条这样的记录了。但是像《上海轨道交通……》那篇就可以直接update。

    解决这个问题的思路分两步:

    1. 删除如《C#字符和ASCII码互转》这种的会爆破联合主键的记录

    2. 更新关联表,把旧分类的ID改成新分类

    那么首先我们要知道有哪些记录是符合被删除的条件的,把这些文章的ID找出来,用一个group by having就可以爆出来:

    SELECT pc.PostId
    FROM   PostCategory AS pc
    WHERE  pc.CategoryId IN (@SourceCatId, @TargetCatId)
    GROUP BY
           pc.PostId
    HAVING COUNT(*) >= 2
    

    结果有10条:

    这10篇文章同时属于DotNetBeginner、CSharpAndDotNet和其他分类。

    为了直观的显示和之后删除操作方便,定义一个表变量叫temp,然后验证一下是哪些文章:

    DECLARE @Temp TABLE (PostId UNIQUEIDENTIFIER)
    
    INSERT INTO @Temp
      (
        PostId
      )(
           SELECT pc.PostId
           FROM   PostCategory AS pc
           WHERE  pc.CategoryId IN (@SourceCatId, @TargetCatId)
           GROUP BY
                  pc.PostId
           HAVING COUNT(*) >= 2
       )
    ------------------------------------------------------------------------------
    SELECT --pc.PostId
           p.Title,
           --pc.CategoryId,
           c.Name
    FROM   PostCategory         AS pc
           INNER JOIN Post      AS p
                ON  p.Id = pc.PostId
           INNER JOIN Category  AS c
                ON  c.Id = pc.CategoryId
    WHERE  pc.PostId IN (SELECT t.PostId
                         FROM   @Temp t)

    发现结果是正确的:

    然后就可以从关联表PostCategory中删除所有文章ID(PostId)在@Temp表中,且CategoryId对应DotNetBeginner的记录。然后用update语句完成文章分类的合并。

    -- Step 1. Delete records that will fuck up the primary key
    DELETE 
    FROM   PostCategory
    WHERE  CategoryId = @SourceCatId
           AND PostId IN (SELECT t.PostId
                          FROM   @Temp t)
    
    -- Step 2. Update old key to new key
    UPDATE PostCategory
    SET    CategoryId     = @TargetCatId
    WHERE  CategoryId     = @SourceCatId

    最后验证一下,数据已经成功合并了。



沪ICP备19023445号-2号
友情链接