【问题标题】:How to update in SQL to get distinct tuples / not to violate a unique constraint如何在 SQL 中更新以获得不同的元组/不违反唯一约束
【发布时间】:2013-02-09 04:48:39
【问题描述】:

我在元组(c_id, t_id) 上有一个具有唯一约束的映射表。

这里有一些示例数据来说明这种情况:

id  c_id    t_id  
----------------
1   10      2
2   10      3
3   10      7
4   12      2
5   13      3

我为t_ids (x,y -> z OR x,y -> x) 编写了一个合并函数。 如果我的内容(c_id)同时具有t_ids,那么我当然违反了使用此语句的约束:

UPDATE mapping_table
SET t_id = '$target_tid'
WHERE t_id = '$t1_id' OR t_id = '$t2_id';

结果是:

id  c_id    t_id
----------------
1   10      4
2   10      4       /* violates unique constraint */
3   10      7

现在我想出了这个:

/* delete one of the duplicate entries */
DELETE FROM mapping_table
WHERE   ( SELECT count(c_id)
          FROM mapping_table
          WHERE t_id = '$t1_id' OR t_id = '$t2_id'
        ) > 1;

/* update the remaining row */
UPDATE mapping_table
SET t_id = '$target_tid'
WHERE t_id = '$t1_id' OR t_id = '$t2_id';

现在我收到以下错误:
You can't specify target table 'mapping_table' for update in FROM clause

我的问题是:

  1. 这里到底出了什么问题? DELETE 语句是否被视为更新并且不能在WHERE 子句中使用?
  2. 这还有什么更有效的方法吗?

【问题讨论】:

  • delete语句没有问题,但是mysql不喜欢同一张表出现在子查询中。

标签: mysql merge sql-update duplicates unique-constraint


【解决方案1】:

试试这个

DELETE FROM mapping_table
    WHERE   ( SELECT count(c_id)
      FROM mapping_table
      WHERE t_id = '$t1_id' OR t_id = '$t2_id'
      Having count(c_id) > 1
    );

编辑:

在你的更新声明中试试这个

 UPDATE mapping_table
 SET t_id = '$target_tid'
WHERE t_id in (select t_id from mapping_table where t_id= '$t1_id' OR t_id = '$t2_id') 

【讨论】:

  • 谢谢,但这仍然产生同样的错误。 You can't specify target table 'mapping_table' for update in FROM clause
【解决方案2】:

您遇到的错误是 MySQL 的一个特性。你可以用一组子查询来解决这个问题:

DELETE FROM mapping_table
WHERE  (select *
        from ( SELECT count(c_id)
               FROM mapping_table
               WHERE t_id = '$t1_id' OR t_id = '$t2_id'
             ) > 1
        ) t

不过,要解决您的问题,只需删除除最小值之外的所有 id。我认为这也可能有效:

delete from mapping_table
where id > (select minid from (select min(id) from mapping_table mt2
                               where mt2.c_id = mapping_table.c_id and
                                     mt2.t_id = mapping_table.t_id
                              )
           )

您还可以将 id 列表存储在临时表中,并在查询中使用它:

create temporary table minids as
     select c_id, t_id, min(id) as minid
     from mapping_table
     group by c_id, t_id;

delete from mapping_table
where exists (select 1 from minids
              where mt2.c_id = mapping_table.c_id and
                    mt2.t_id = mapping_table.t_id and
                    mt2.minid > mapping_table.id
             )

【讨论】:

  • 我遇到了一些问题,即您的陈述不完整且存在语法错误,但由于您的建议将我推向了正确的方向,因此我接受您的回答。 -- 检查我的答案,看看我真正需要什么。
【解决方案3】:

我一直在寻找这个解决方案。性能可能低得惊人,但至少我找到了一个可行的解决方案(并且学到了一些东西)。

/* actually delete rows that will become duplicate after the update */
DELETE FROM mt1 USING mapping_table AS mt1 WHERE id IN (

    /* sub-query to allow `mapping_table` in the DELETE statement */
    SELECT * FROM (

        /* select ids/rows with one t_id available */
        SELECT id
        FROM mapping_table AS mt2
        WHERE mt2.tag_id = $t1_id AND c_id IN (

            /* select ids/rows with both t_id available */
            SELECT c_id
            FROM mapping_table AS mt3
            WHERE mt3.c_id = mt2.c_id AND mt3.tag_id = $t2_id)

    /* alias needed for every derived table */
    ) as mres
)

/* Update to merge t_ids */
UPDATE mapping_table
SET t_id = '$target_id'
WHERE t_id = '$t1_id' OR t_id = '$t2_id';

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2020-04-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-03
    • 2015-12-17
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多