查找两个表之间的已修改/新建/已删除记录

时间:2014-11-25 10:10:11

标签: sql sql-server tsql

我希望通过将一个表(tableA)与另一个表(tableB)进行比较,在一个表(tableA)中找到新的,已修改的和已删除的记录。两个表都具有相同的模式,并具有唯一的ID字段。

在我的情况下,tableA最初与tableB相同,但它已由一些外部组织编辑,一旦完成编辑,他们通过ZIP文件将表发回,我们重新填充(截断并插入)那个数据到tableA。所以我想找出tableA中哪些记录发生了变化。我正在使用SQL Server 2012。

我可以使用"来获取新的和修改过的记录,除了"关键字:

select * from tableA
except
select * form tableB

(让我们调用上述结果A)

我也可以删除和修改记录:

select * from tableB
except
select * form tableA

(让我们调用上述结果B)

问题是,ResultsA和ResultsB都有相同的修改/编辑记录。因此修改/编辑的记录加倍。我可以在ResultsA和ResultsB上使用内部联接或交叉来获取仅修改的记录(将此结果称为结果C)。但是,我需要在ResultsA和ResultsC之间再次使用join / except来获取新的记录,并在ResultsB和ResultsC之间再次加入/除了只删除记录......我尝试thisthis,但他们不适合我。

显然这不好。有没有更优雅,更简单的方法来查找已经删除,修改或添加到tableA中的记录与tableB相比?

6 个答案:

答案 0 :(得分:5)

怎么样:

-- DELETED
SELECT B.*, 'DELETED' AS 'CHANGE_TYPE'
FROM TableB B
LEFT JOIN TableA A  ON B.PK_ID = A.PK_ID
WHERE A.PK_ID IS NULL
UNION
-- NEW
SELECT A.*, 'NEW'  AS 'CHANGE_TYPE'
FROM TableA  A
LEFT JOIN TableB  B ON B.PK_ID = A.PK_ID
WHERE B.PK_ID IS NULL
UNION
-- MODIFIED
SELECT B.*, 'MODIFIED'  AS 'CHANGE_TYPE'
FROM (
        SELECT * FROM TableA 
        EXCEPT
        SELECT * FROM TableB 
    ) S1
INNER JOIN TableB  B ON S1.PK_ID = B.PK_ID;

不完全优雅,但有效。

答案 1 :(得分:1)

根据我的理解,我提出了以下解决方案。

DECLARE @tableA TABLE (ID INT, Number INT)
DECLARE @tableB TABLE (ID INT, Number INT)

INSERT INTO @tableA VALUES
    (1,10),
    (2,20),
    (3,30),
    (4,40)

INSERT INTO @tableB VALUES
    (1,11),
    (2,20),

    (4,40),
    (5,50)


SELECT *,'Modified or deleted' as 'Status' FROM
(
    select * from @tableA
    except
    select * from @tableB
)a WHERE ID NOT IN 
(
    select ID from @tableB
    except
    select ID from @tableA
)
UNION
SELECT *,'New' as 'Status' FROM 
(
    select * from @tableB
    except
    select * from @tableA
)b WHERE ID NOT IN 
(
    SELECT ID FROM
    (
        select * from @tableA
        except
        select * from @tableB
    )a WHERE ID NOT IN 
    (
        select ID from @tableB
        except
        select ID from @tableA
    )
)

答案 2 :(得分:0)

您可以使用OUTPUT子句:

  

从受INSERT,UPDATE或DELETE语句影响的每一行返回信息或基于表达式的表达式。这些结果可以返回到处理应用程序,以用于确认消息,存档和其他此类应用程序要求。或者,可以将结果插入表或表变量中。

请参阅以下内容,抱歉,我没有适合您的实用代码。但请注意,SQL输出子句可用于在执行插入或更新时从“inserted”和“deleted”(新值和旧值)表中返回任何值。 follow this for more info

答案 3 :(得分:0)

另一种非常有效的解决方案是使用两个表之间不存在交叉的地方。它非常紧凑。

SELECT
    IsNull(tableB.ID,tableA.ID) as 'ID',
    IsNull(tableB.Number,tableA.Number) as 'Number',
    'Action' = CASE 
                   WHEN tableB.ID IS NULL THEN 'Deleted'
                   WHEN tableA.ID IS NULL THEN 'Created'
                   ELSE 'Updated'
               END
FROM tableA
FULL OUTER JOIN tableB 
    ON tableB.ID = tableA.ID
WHERE 
    NOT EXISTS (SELECT tableB.* INTERSECT SELECT tableA.*)

这使表扫描保持最小,并提供对新记录,已删除记录和已更改记录的检测。

我把这三个人放在小提琴里,令人惊讶的是他们的编纂方式有多么不同。

http://sqlfiddle.com/#!6/b1a5a/5

答案 4 :(得分:0)

ages = [20,20,11,12,10,11,15]
age_counts={} #define dict
for i in ages:
    #if age_counts does not have i, set its count to 1
    #increment otherwise
    if not age_counts.has_key(i):
        age_counts[i]=1
    else:
        age_counts[i]+=1
#you can now have counts stored
for i in age_counts:
    print i, age_counts[i] 

答案 5 :(得分:0)

这个没有主键也有点优雅。(在我看来!)

WITh A AS (SELECT 1,2,3 FROM DUAL
UNION ALL
SELECT 1,3,2 FROM DUAL
UNION ALL
SELECT 1,3,1 FROM DUAL),
B AS (SELECT 1,3,2 FROM DUAL
UNION ALL
SELECT 1,2,3 FROM DUAL
UNION ALL
SELECT 1,3,5 FROM DUAL
)
,
C AS 
(SELECT * FROM A
MINUS 
SELECT * FROM B
),
D AS( SELECT * FROM b
MINUS 
SELECT * FROM A)
SELECT C.* ,'Deleted' FROM  c
UNION  ALL
SELECT D.* ,'Added' FROM D