处理重复项

mysql cookbook00014

处理重复项
14.0 引言
处理重复行的操作包括一下几种:
    1. 在一开始就进制在数据表中产生重复项。
    2. 对重复项技术以确定他们是否存在以及严重到什么程度
    3. 通过鉴别重复值来发现哪些是重复项以及他们是在什么时候出现的
    4. 消除重复项以确保每行都是唯一的
有几种方式可以帮你处理重复行,你可以根据你要实现的目标来选择他们:
    1. 创建包含主键或者唯一索引
    2. 与唯一索引相结合,INSERT IGONE和REPLACE语句使你能够从容的处理重复行插入而不报错,对于统一载入操作LOAD DATA 语句中也适用
    3. 如果你需要确定表中是否含有重复行,使用GROUP BY分类计算
    4. SELECT DISTINCT 用于在结果集中删除重复行,对于已经存在重复行的表,可以通过增加唯一索引来删除他们。如果表中含有N个同样的行,你可以使用DELETE..LIMIT从这个特殊的行集合中删除n-1条记录
14.1 防止在表中发生重复
建立索引:primary key/ unique(它是允许NULL值的,则NULL值具有特殊性是因为他可以出现多次,
这样做的理由是因为确定一个未知值是否与另一个未知值相等是不可能的,所以未知值是存在的)
14.2 处理向表中装载行是出现的重复错误
问题:对于已经建立起来了唯一索引的表,怎样向其中的表中插入数据
解决:简单的方法忽略这些错误,另一种方式是通过使用:INSERT IGONRE、REPLACE或INSERT ..ON DUPLICATE KEY UPDATE语句,
这些语句修改了MYSQL的重复处理机制对于桶载入操作,再入数据的修饰语使你能够指定如何处理重复
对于已经建立的唯一或主键约束的表来说插入重复的行会报错,对于下面的几种方式来解决上面的问题:
    1. 先使用SELECT 以检查该行是否已经存在,再向其中行插入记录(这种操作是错误的,因为在你插入之前,查询之后另一个客户端操作了表)
    2. 如果当重复发生是你希望保持原有的行,那么就应该使用INSERT IGNORE来取代INSERT;当重复发生时,IGNORE关键字告诉MYSQL悄悄的丢弃被插入的记录,而保持原有记录。:1代表插入;0代表重复,丢弃。
    3. 如果当重复发生时,你希望被插入的行被覆盖;此时应该是使用REPLACE取代IGNORE关键字了:1代表插入;2代表替换行。
    4. 如果重复发生时,你希望修改已存在的相关列;此时应该是使用INSERT ...ON DUPLICATE KEY UPDATE语句。通过受影响行的计数值了解具体情况:1代表插入;2代表更新。
    5. 总结:INSERT IGNORE比REPLACE效率更高,因为他并不实际的插入重复行。因此,假如你只需要证实表中是否存在一个给定行的拷贝时,最好是使用前者,另一方面,REPLACE更适合用于表中其他非键列需要被替换的情况。而结果你需要对于表中未出现的int记录能够正常插入,而在索引列发生重复时更行其中的某些列的话,INSERT...ON DUPLICATE KEY UPDATE更为合适
问题1:如何为新用户添加记录,为旧用户修改密码?
解决:REPLACE INTO user(username,password) VALUE('name','pwd');
问题2:如何做好选票记录的工作?
解决:INSERT INTO vote(username,count) VALUE('name',1)ON DUPLICATE KEY UPDATE count = count +1;


14.3 计数和识别重复项
使用计数摘要以查找并显示重复项。为了查看重复值的发生,将该摘要与原始表相关联
SELECT COUNT(*) , col1,col2,...FROM tab1 GROUP BY col1,col2,...HAVING COUNT(*) > 1//列集合相同
14.4 从表中消除重复项
通过选择表在中具有唯一性的行,并放入到第二张表中,你可以用它替换原表,或者使用alter子句增加唯一索引,甚至通过DELETE ...LIMIT N
来删除给定重复行多余的记录行。
mysql> select * from catalog_list;
+------------+-----------+---------+
| first_name | last_name | address |
+------------+-----------+---------+
| aaa | aaaa | aaaaa |
| aaa | bbbb | bbbbb |
| bbb | bbbb | bbbbb |
| bbb | ccc | ccccc |
| ccc | cccc | ccccc |
| ddd | dddd | ddddd |
| ddd | eeee | eeeee |
| eee | eeee | eeeee |
| aaa | aaaa | aaaaa |
| aaa | bbbb | bbbbb |
| bbb | bbbb | bbbbb |
+------------+-----------+---------+
11 rows in set (0.00 sec)
上面已经说了有三种方式来消除重复项:
    1. 使用表替换来删除重复相
      mysql> select * from catalog_list;
      +------------+-----------+---------+
      | first_name | last_name | address |
      +------------+-----------+---------+
      | aaa | aaaa | aaaaa |
      | aaa | bbbb | bbbbb |
      | bbb | bbbb | bbbbb |
      | bbb | ccc | ccccc |
      | ccc | cccc | ccccc |
      | ddd | dddd | ddddd |
      | ddd | eeee | eeeee |
      | eee | eeee | eeeee |
      | aaa | aaaa | aaaaa |
      | aaa | bbbb | bbbbb |
      | bbb | bbbb | bbbbb |
      +------------+-----------+---------+
      11 rows in set (0.00 sec)
      mysql> create table catalog_list_new like catalog_list;
      Query OK, 0 rows affected (0.16 sec)
      mysql> insert into catalog_list_new select distinct * from catalog_list;
      Query OK, 8 rows affected (0.08 sec)
      Records: 8 Duplicates: 0 Warnings: 0
      mysql>
      该方法可以使用在缺乏索引的情况下(尽管可能对于大型表来说速度比较慢)。对于包含重复NULL值的表,它将删除哪些重复的项,但是不能组织将来出现重复项
      该方法要求被人为是重复项的行必须是完全相同的,如果重复项被定义为只与表中列的子集相关,可以为那些列建立唯一性索引表,并且使用INSERT IGNORE将数据插入。
      mysql> create table catalog_list_new like catalog_list;
      Query OK, 0 rows affected (0.17 sec)

      mysql> alter table catalog_list_new add primary key(first_name,last_name);
      Query OK, 0 rows affected (0.28 sec)
      Records: 0 Duplicates: 0 Warnings: 0

      mysql> insert ignore into catalog_list_new select * from catalog_list;
      Query OK, 8 rows affected (0.11 sec)
      Records: 11 Duplicates: 3 Warnings: 0

      mysql> select * from catalog_list_new;
      +------------+-----------+---------+
      | first_name | last_name | address |
      +------------+-----------+---------+
      | aaa | aaaa | aaaaa |
      | aaa | bbbb | bbbbb |
      | bbb | bbbb | bbbbb |
      | bbb | ccc | ccccc |
      | ccc | cccc | ccccc |
      | ddd | dddd | ddddd |
      | ddd | eeee | eeeee |
      | eee | eeee | eeeee |
      +------------+-----------+---------+
      唯一索引阻止了在向表中插入行时出现的重复键值,IGONE提示MYSQL当重复发生时不要因为报错停止。这种方法的
      的缺点是如果索引列需要包含NULL值那么你必须使用UNIQUE索引而不是PRIMARY KEY。
2. 通过增加索引来删除重复项
为了适当的在表中删除重复项,需要使用ALTER TABLE 向表中增加唯一索引,并使用IGONE关键字来提示创建索引构建过程中
丢弃带有重复键值的航记录。
mysql>alter ignore table catalog_list_new add primary key(first_name,last_name);
ERROR 1062 (23000): Duplicate entry 'aaa-aaaa' for key 'PRIMARY'
mysql> show create table catalog_list_new;
+------------------+---------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------+
| Table | Create Table
|
+------------------+---------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------+
| catalog_list_new | CREATE TABLE `catalog_list_new` (
`first_name` varchar(30) NOT NULL DEFAULT '',
`last_name` varchar(30) NOT NULL DEFAULT '',
`address` varchar(30) DEFAULT NULL
) ENGINE=InnoDB DEFAULT CHARSET=utf8 |
+------------------+---------------------------------------------------------------------------------------------------------
--------------------------------------------------------------------------+
1 row in set (0.00 sec)
mysql>alter table catalog_list_new engine =myisam;
Query OK, 11 rows affected (0.28 sec)
Records: 11 Duplicates: 0 Warnings: 0

mysql>alter ignore table catalog_list_new add primary key(first_name,last_name);
Query OK, 11 rows affected (0.16 sec)
Records: 11 Duplicates: 3 Warnings: 0
mysql>
注意:你会发现当表的引擎是INNODB的时候通过第二种方式来删除重复记录是不能成功的,只有通过修改表引擎为MYISAM才可以使用 方式2删除重复记录


3. 删除特定行的重复项
mysql> delete cln from catalog_list_new cln inner join temp on cln.first_name = temp.first_name
-> and cln.last_name = temp.last_name
-> where limit temp.count;
ERROR 1064 (42000): You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use
ear 'limit temp.count' at line 3
//通过上面红色的标记可以看到在LIMIT中引用了行记录中的元数据这样会报语法错误,这是因为LIMIT是用来刷选查询出来的行记录,而在这个关键字中使用行中记录来确定全部记录信息这就矛盾了。顶多是自己确定自己而不能约束他人

可以自己写一个函数通过重复的条目数来删除记录
基本的删除重复语法是:DELETE FROM tab1 WHERE col1 = '区别记录值' LIMIT duplicatesCount - 1;


14.5 从自连接的结果中消除重复




















你可能感兴趣的:(处理)