【问题标题】:Trouble deleting duplicate records using partition and rank() in sql server在 sql server 中使用 partition 和 rank() 删除重复记录时遇到问题
【发布时间】:2013-11-16 02:00:51
【问题描述】:

我正在尝试识别重复记录,然后使用 PARTITION 和 RANK() n SQL Server 2008 删除其中一条重复记录。我删除重复记录的条件是它不应在另一个表中引用。

我有一个包含一些重复语言的语言表。员工表有员工和语言映射。如果该语言 ID 未映射到 Employee 表中,我必须删除其中一条重复记录。

CREATE TABLE MY_LANGUAGE (LANGUAGEID INT, LANGUAGENAME VARCHAR(20))
CREATE TABLE MY_EMPLOYEE (EMPID INT, NAME VARCHAR(20), LANGUAGEID INT)

INSERT INTO MY_LANGUAGE VALUES(1, 'ENGLISH')
INSERT INTO MY_LANGUAGE VALUES(2, 'FRENCH')
INSERT INTO MY_LANGUAGE VALUES(3, 'ITALIAN')
INSERT INTO MY_LANGUAGE VALUES(4, 'GERMAN')
INSERT INTO MY_LANGUAGE VALUES(5, 'ITALIAN')
INSERT INTO MY_LANGUAGE VALUES(6, 'GERMAN')
INSERT INTO MY_LANGUAGE VALUES(7, 'SPANISH')

INSERT INTO MY_EMPLOYEE VALUES (10, 'GLEN', 1)
INSERT INTO MY_EMPLOYEE VALUES (20, 'PETER', 2)
INSERT INTO MY_EMPLOYEE VALUES (30, 'MARIA', 3)

如果您看到,我有两种语言是重复的,其中一种正在被员工使用。我想删除语言 ID 4 和 5。

LANGUAGENAME    LANGUAGEID  EMPNAME
GERMAN          4   
GERMAN          6   
ITALIAN             3               MARIA   
ITALIAN             5   

我试图创建一个选择语句来返回我要删除的内容:

WITH CTE AS (
SELECT  L.LANGUAGENAME,  L.LANGUAGEID, RANK() OVER(PARTITION BY L.LANGUAGENAME ORDER BY L.LANGUAGEID) AS RANKING
FROM MY_LANGUAGE L
INNER JOIN (
    SELECT LANGUAGENAME, COUNT(*) AS DUPECOUNT
    FROM MY_LANGUAGE
    GROUP BY LANGUAGENAME
    HAVING COUNT(*) > 1
) LC ON L.LANGUAGENAME = LC.LANGUAGENAME
WHERE NOT EXISTS (SELECT 1 FROM MY_EMPLOYEE WHERE MY_EMPLOYEE.LANGUAGEID = L.LANGUAGEID))  
SELECT * FROM CTE WHERE RANKING = 1

这会返回以下内容

LANGUAGENAME    LANGUAGEID  RANKING
GERMAN              4           1
ITALIAN         5           1

当我尝试删除时出现错误:

WITH CTE AS (
SELECT  L.LANGUAGENAME,  L.LANGUAGEID, RANK() OVER(PARTITION BY L.LANGUAGENAME ORDER BY L.LANGUAGEID) AS RANKING
FROM MY_LANGUAGE L
INNER JOIN (
    SELECT LANGUAGENAME, COUNT(*) AS DUPECOUNT
    FROM MY_LANGUAGE
    GROUP BY LANGUAGENAME
    HAVING COUNT(*) > 1
) LC ON L.LANGUAGENAME = LC.LANGUAGENAME
WHERE NOT EXISTS (SELECT 1 FROM MY_EMPLOYEE WHERE MY_EMPLOYEE.LANGUAGEID = L.LANGUAGEID))  
DELETE FROM CTE WHERE RANKING = 1

我得到的错误是:

消息 4405,第 16 级,状态 1,第 1 行
视图或函数“CTE”不可更新,因为修改会影响多个基表。

任何如何解决这个问题的想法,或者可能是它可以被简化。感谢@Szymon 展示了一个临时表解决方案,但我希望得到一个没有临时表的解决方案(如果可能的话)。

【问题讨论】:

    标签: sql-server duplicate-removal


    【解决方案1】:

    查询:

    DELETE ll
    FROM MY_LANGUAGE ll
    JOIN (SELECT L.LANGUAGENAME,
           L.LANGUAGEID,
           ROW_NUMBER()OVER(PARTITION BY L.LANGUAGENAME, e.EMPID
                            ORDER BY L.LANGUAGEID ASC) rnk,
           COUNT(*)OVER(PARTITION BY L.LANGUAGENAME) cnt,
           e.EMPID
          FROM MY_LANGUAGE l
          LEFT JOIN MY_EMPLOYEE e ON e.LANGUAGEID = l.LANGUAGEID)  a 
        ON ll.LANGUAGEID = a.LANGUAGEID
    AND a.rnk = 1
    AND a.cnt > 1
    and a.EMPID IS NULL
    

    结果:

    | LANGUAGEID | LANGUAGENAME |
    |------------|--------------|
    |          1 |      ENGLISH |
    |          2 |       FRENCH |
    |          3 |      ITALIAN |
    |          6 |       GERMAN |
    |          7 |      SPANISH |
    

    【讨论】:

    • 起初,我认为这很好用,但似乎内部查询(连接查询)将获取所有重复项以及员工尚未引用的语言。添加此附加语言:INSERT INTO MY_LANGUAGE VALUES(7, 'SPANISH')
    • 太棒了!它现在就像一个魅力,这是一个会让很多人开心的查询:)
    【解决方案2】:

    您可以将 CTE 查询中的记录放入临时表中,然后根据该表删除。

    WITH CTE AS (
    SELECT  L.LANGUAGENAME,  L.LANGUAGEID, RANK() OVER(PARTITION BY L.LANGUAGENAME ORDER BY L.LANGUAGEID) AS RANKING
    FROM MY_LANGUAGE L
    INNER JOIN (
        SELECT LANGUAGENAME, COUNT(*) AS DUPECOUNT
        FROM MY_LANGUAGE
        GROUP BY LANGUAGENAME
        HAVING COUNT(*) > 1
    ) LC ON L.LANGUAGENAME = LC.LANGUAGENAME
    WHERE NOT EXISTS (SELECT 1 FROM MY_EMPLOYEE WHERE MY_EMPLOYEE.LANGUAGEID = L.LANGUAGEID))  
    SELECT * INTO #temp FROM CTE WHERE RANKING = 1
    

    然后使用#temp中的记录从原表中删除。

    【讨论】:

    • 实际上我并没有在寻找临时表解决方案,尽管我没有在我的问题中提及。但这是修复它的另一种方法,但不确定性能会受到什么影响?
    • 性能命中取决于您尝试删除的记录数,因为它们必须存储在临时表中。但在实践中,无论如何都必须提取此信息以进行删除。
    【解决方案3】:

    试试这个..

    ;WITH CTE AS (
    SELECT t.LANGUAGEID, LANGUAGENAME, RANK() OVER(PARTITION BY T.LANGUAGENAME ORDER BY T.LANGUAGEID) AS RANKING
    FROM MY_LANGUAGE T 
    WHERE T.LANGUAGEID  IN (
                            SELECT  L.LANGUAGEID 
                            FROM MY_LANGUAGE L LEFT JOIN MY_EMPLOYEE E
                            ON L.LANGUAGEID = E.LANGUAGEID
                            WHERE E.LANGUAGEID IS NULL)
    ) 
    DELETE FROM CTE
    WHERE RANKING = 1 AND (CTE.LANGUAGENAME IN (SELECT LANGUAGENAME 
                                            FROM MY_LANGUAGE 
                                            GROUP BY LANGUAGENAME
                                            HAVING COUNT(LANGUAGENAME) > 1))
    

    【讨论】:

    • 与@Justine 的结果相同,似乎内部查询获取了所有重复项以及员工尚未引用的语言。添加此附加语言:INSERT INTO MY_LANGUAGE VALUES(7, 'SPANISH')
    猜你喜欢
    • 2021-05-18
    • 2019-04-13
    • 1970-01-01
    • 2011-03-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-12-16
    相关资源
    最近更新 更多