0

我有一个计数表,我希望能够在重命名一个复合键字段时对计数求和。这很难解释,所以让我给你看一些 SQL:

示例表:

CREATE TABLE `test` (
  `id` int(11) NOT NULL,
  `type` int(11) NOT NULL,
  `count` int(11) NOT NULL,
  PRIMARY KEY (`id`,`type`)
);

插入一些数据:

INSERT INTO test VALUES(1, 10, 1);
INSERT INTO test VALUES(2, 20, 3);
INSERT INTO test VALUES(2, 10, 3);
INSERT INTO test VALUES(2, 30, 3);

查询数据:

mysql> SELECT SUM(count) as count FROM test WHERE id = 2;
+-------+
| count |
+-------+
|     9 |
+-------+

mysql> SELECT SUM(count) as count FROM test WHERE type = 10;
+-------+
| count |
+-------+
|     4 |
+-------+

效果很好,快速灵活。

现在,我想将类型 10重新映射为类型 20

UPDATE test SET type = 20 WHERE type = 10;
Duplicate entry '2-20' for key 'PRIMARY'

在这里使用ON DUPLICATE KEY UPDATE是无效的。我认为这应该可以通过创意插入来实现,但我不确定。谁能想到一种方法?

4

3 回答 3

1

更新查询时,您将使用id = 2and创建多行,由于您的约束type = 20,这是不允许的。primary key

您应该改为使用单列作为主键,并在插入新行时使其自动递增。这样可以保证它是独一无二的。

于 2012-08-02T15:26:46.300 回答
1

一种方法是“松开”PRIMARY KEY,意思是将其从 PRIMARY(唯一)KEY 更改为非唯一 KEY。这将允许重复值,并允许您的 UPDATE 语句成功,这样您将有两个(或更多)行匹配(id,type)。(请注意,这会使更新单行出现问题,因此您可能希望添加一个可以唯一的新列。AUTO_INCREMENT 列可以很好地工作。)


如果您不想这样做,那么另一种方法是将类型 10 和类型 20 行的计数“组合”在一起(对于每个 id)进入类型 20 行,设置类型 10 行的计数为零,并且(可选)在单独的语句中删除冗余类型 10 行。

下面的语句通过在第一次选择中将 10 值“映射”到 20 来“组合”类型 10 和类型 20 计数。


 -- combine count for types 10 and 20 as new count for type 20
 -- and set count to zero for type 10

 INSERT INTO test (id, `type`, `count`)
 SELECT t.id
      , IF(t.`type`=10,20,t.`type`) AS new_type
      , SUM(t.`count`) AS `count`
   FROM test t
  WHERE t.`type` IN (10,20)
  GROUP BY id, new_type
  UNION ALL
 SELECT u.id
      , u.`type`
      , 0 AS `count`
   FROM test u
  WHERE u.`type` = 10
     ON DUPLICATE KEY UPDATE `count` = VALUES(`count`);

 -- remove unnecessary type 10 rows.
 DELETE FROM test WHERE `type` = 10 AND `count` = 0;

注意:第一个 SELECT 中的 IF() 表达式等价于:

CASE WHEN t.type = 10 THEN 20 ELSE t.type END

第二个选择是为我们提供所有需要更新的类型 10 行。我们使用 UNION ALL 运算符连接这两个结果集。

然后我们获取组合(连接)的结果集,并将它们运行到插入中。任何我们点击“重复”键的地方,我们都会通过 ON DUPLICATE KEY 子句执行更新操作。

您可能希望在事务中执行此操作(如果您使用的是 InnoDB)并验证第一条语句是否成功完成,然后再执行DELETE. 如果第一条语句抛出异常(可能有一个不稳定的触发器),那么您可能想要 ROLLBACK 事务。如果 DELETE 由于某种原因失败,您还需要 ROLLBACK(可能存在会被违反的外键约束。)

或者,您不一定需要执行 DELETE,您可以只保留 10 行类型,计数为零。


重要的:

不要同时实施这两种解决方案!只有其中之一。

第一种方法是模式更改,它不需要任何数据更改。该更改将使您的 UPDATE 成功。

第二种方法要求模式保持不变,并且取决于 (id,type) 上 UNIQUE KEY 的存在(和执行)。

于 2012-08-02T22:59:23.273 回答
0

您在两列上设置了主键,更新后第三行主键将与第二行相同,因此不允许这样做。

最后你会得到 1..20;2..20、2..20 等

于 2012-08-02T15:27:42.990 回答