您的位置:首页 > 数据库 > MySQL

MySQL Cookbook 学习笔记-04

2011-08-31 11:41 417 查看
1、处理重复

<a>、创建表包含 primary key 或 unique index,阻止重复数据写入

<b>、联合 <a>,使用 INSERT IGNORE 或 REPLACE

<c>、检查表是否有重复行的方法,通过 group by 和 count( x ) 来统计

<d>、通过 SELECT DISTINCT 来过滤重复

a——示例

CREATE TABLE person
(
last_name   CHAR(20) NOT NULL,
first_name  CHAR(20) NOT NULL,
address     CHAR(40),
PRIMARY KEY (last_name, first_name)
);
CREATE TABLE person
(
last_name   CHAR(20) NOT NULL,
first_name  CHAR(20) NOT NULL,
address     CHAR(40),
UNIQUE (last_name, first_name)
);


b——示例

一般情况下,插入重复 primary key 或 union index 会报错

mysql> INSERT INTO person (last_name, first_name)
-> VALUES('X1','Y1');
Query OK, 1 row affected (0.00 sec)
mysql> INSERT INTO person (last_name, first_name)
-> VALUES('X1','Y1');
ERROR 1062 (23000): Duplicate entry 'X1-Y1' for key 1
使用 INSERT IGNORE

mysql> INSERT IGNORE INTO person (last_name, first_name)
-> VALUES('X2','Y2');
Query OK, 1 row affected (0.00 sec)
mysql> INSERT IGNORE INTO person (last_name, first_name)
-> VALUES('X2','Y2');
Query OK, 0 rows affected (0.00 sec)
使用 REPLACE

mysql> REPLACE INTO person (last_name, first_name)
-> VALUES('X3','Y3');
Query OK, 1 row affected (0.00 sec)
mysql> REPLACE INTO person (last_name, first_name)
-> VALUES('X3','Y3');
Query OK, 2 rows affected (0.00 sec)
使用 INSERT ... ON DUPLICATE KEY UPDATE 

CREATE TABLE poll_vote
(
poll_id      INT UNSIGNED NOT NULL AUTO_INCREMENT,
candidate_id INT UNSIGNED,
vote_count   INT UNSIGNED,
PRIMARY KEY (poll_id, candidate_id)
);

mysql> SELECT * FROM poll_vote;
Empty set (0.01 sec)
mysql> INSERT INTO poll_vote (poll_id,candidate_id,vote_count) VALUES(14,2,1)
-> ON DUPLICATE KEY UPDATE vote_count = vote_count + 1;
Query OK, 1 row affected (0.01 sec)
mysql> SELECT * FROM poll_vote;
+---------+--------------+------------+
| poll_id | candidate_id | vote_count |
+---------+--------------+------------+
| 14      | 2            | 1          |
+---------+--------------+------------+
1 row in set (0.01 sec)
mysql> INSERT INTO poll_vote (poll_id,candidate_id,vote_count) VALUES(14,2,1)
-> ON DUPLICATE KEY UPDATE vote_count = vote_count + 1;
Query OK, 2 rows affected (0.00 sec)
mysql> SELECT * FROM poll_vote;
+---------+--------------+------------+
| poll_id | candidate_id | vote_count |
+---------+--------------+------------+
| 14      | 2            | 2          |
+---------+--------------+------------+
1 row in set (0.00 sec)
c——示例

mysql> SELECT * FROM catalog_list;
+-----------+-------------+--------------------------+
| last_name | first_name  | street                   |
+-----------+-------------+--------------------------+
| Isaacson  | Jim         | 515 Fordam St., Apt. 917 |
| Baxter    | Wallace     | 57 3rd Ave.              |
| McTavish  | Taylor      | 432 River Run            |
| Pinter    | Marlene     | 9 Sunset Trail           |
| BAXTER    | WALLACE     | 57 3rd Ave.              |
| Brown     | Bartholomew | 432 River Run            |
| Pinter    | Marlene     | 9 Sunset Trail           |
| Baxter    | Wallace     | 57 3rd Ave., Apt 102     |
+-----------+-------------+--------------------------+

mysql> select count(*) as total_rows,
-> count(distinct last_name, first_name) as distince_names,
-> count(*) - count(distinct last_name, first_name) as duplicate_names
-> from catalog_list;
+------------+----------------+-----------------+
| total_rows | distince_names | duplicate_names |
+------------+----------------+-----------------+
|          8 |              5 |               3 |
+------------+----------------+-----------------+
查询出重复的行:

mysql> select count(*) as repetitions, last_name, first_name
-> from catalog_list
-> group by last_name, first_name
-> having repetitions > 1;
+-------------+-----------+------------+
| repetitions | last_name | first_name |
+-------------+-----------+------------+
|           3 | Baxter    | Wallace    |
|           2 | Pinter    | Marlene    |
+-------------+-----------+------------+
2、从表中删除重复

(Recipe 14.4. Eliminating Duplicates from a Table)

<a>、Removing duplicates using table replacement

<b>、Removing duplicates by adding an index

<c>、Removing duplicates of a particular row

a——示例

mysql> CREATE TABLE tmp LIKE catalog_list;
mysql> ALTER TABLE tmp ADD PRIMARY KEY (last_name, first_name);
mysql> INSERT IGNORE INTO tmp SELECT * FROM catalog_list;
mysql> SELECT * FROM tmp ORDER BY last_name, first_name;
+-----------+-------------+--------------------------+
| last_name | first_name  | street                   |
+-----------+-------------+--------------------------+
| Baxter    | Wallace     | 57 3rd Ave.              |
| Brown     | Bartholomew | 432 River Run            |
| Isaacson  | Jim         | 515 Fordam St., Apt. 917 |
| McTavish  | Taylor      | 432 River Run            |
| Pinter    | Marlene     | 9 Sunset Trail           |
+-----------+-------------+--------------------------+
mysql> DROP TABLE catalog_list;
mysql> RENAME TABLE tmp TO catalog_list;
b——示例

mysql> ALTER IGNORE TABLE catalog_list
-> ADD PRIMARY KEY (last_name, first_name);
mysql> SELECT * FROM catalog_list ORDER BY last_name, first_name;
+-----------+-------------+--------------------------+
| last_name | first_name  | street                   |
+-----------+-------------+--------------------------+
| Baxter    | Wallace     | 57 3rd Ave.              |
| Brown     | Bartholomew | 432 River Run            |
| Isaacson  | Jim         | 515 Fordam St., Apt. 917 |
| McTavish  | Taylor      | 432 River Run            |
| Pinter    | Marlene     | 9 Sunset Trail           |
+-----------+-------------+--------------------------+
c——示例

+-------+
| color |
+-------+
| blue  |
| green |
| blue  |
| blue  |
| red   |
| green |
| red   |
+-------+

mysql> DELETE FROM t WHERE color = 'blue' LIMIT 2;
mysql> DELETE FROM t WHERE color = 'green' LIMIT 1;
mysql> DELETE FROM t WHERE color = 'red' LIMIT 1;
mysql> SELECT * FROM t;
+-------+
| color |
+-------+
| blue  |
| green |
| red   |
+-------+

3、删除“语义重复”但行数据不重复的行

(Recipe 14.5. Eliminating Duplicates from a Self-Join Result)

mysql> SELECT YEAR(s1.statehood) AS year,
-> s1.name AS name1, s1.statehood AS statehood1,
-> s2.name AS name2, s2.statehood AS statehood2
-> FROM states AS s1 INNER JOIN states AS s2
-> ON YEAR(s1.statehood) = YEAR(s2.statehood) AND s1.name != s2.name
-> ORDER BY year, s1.name, s2.name;
+------+----------------+------------+----------------+------------+
| year | name1          | statehood1 | name2          | statehood2 |
+------+----------------+------------+----------------+------------+
| 1787 | Delaware       | 1787-12-07 | New Jersey     | 1787-12-18 |
| 1787 | Delaware       | 1787-12-07 | Pennsylvania   | 1787-12-12 |
| 1787 | New Jersey     | 1787-12-18 | Delaware       | 1787-12-07 |
| 1787 | New Jersey     | 1787-12-18 | Pennsylvania   | 1787-12-12 |
| 1787 | Pennsylvania   | 1787-12-12 | Delaware       | 1787-12-07 |
| 1787 | Pennsylvania   | 1787-12-12 | New Jersey     | 1787-12-18 |
...
| 1912 | Arizona        | 1912-02-14 | New Mexico     | 1912-01-06 |
| 1912 | New Mexico     | 1912-01-06 | Arizona        | 1912-02-14 |
| 1959 | Alaska         | 1959-01-03 | Hawaii         | 1959-08-21 |
| 1959 | Hawaii         | 1959-08-21 | Alaska         | 1959-01-03 |
+------+----------------+------------+----------------+------------+
由上数据分析:

1787年有3个州(Delaware,New Jersey,Pennsylvania),两两配对应该是3个才对。

但是这里却又6条数据,一半重复(语义上)!

将“语义重复”调整为“行数据重复”

+------+----------------+------------+----------------+------------+
| year | name1          | statehood1 | name2          | statehood2 |
+------+----------------+------------+----------------+------------+
| 1787 | Delaware       | 1787-12-07 | New Jersey     | 1787-12-18 |
| 1787 | New Jersey     | 1787-12-18 | Delaware       | 1787-12-07 |
也就是:

如果,name1 < name2,成立保持不变

如果,name1 < name2,不成立。name1 与 name2 互换;statehood1 与 statehood2 互换。

mysql> SELECT YEAR(s1.statehood) AS year,
-> IF(s1.name<s2.name,s1.name,s2.name) AS name1,
-> IF(s1.name<s2.name,s1.statehood,s2.statehood) AS statehood1,
-> IF(s1.name<s2.name,s2.name,s1.name) AS name2,
-> IF(s1.name<s2.name,s2.statehood,s1.statehood) AS statehood2
-> FROM states AS s1 INNER JOIN states AS s2
-> ON YEAR(s1.statehood) = YEAR(s2.statehood) AND s1.name != s2.name
-> ORDER BY year, name1, name2;
+------+----------------+------------+----------------+------------+
| year | name1          | statehood1 | name2          | statehood2 |
+------+----------------+------------+----------------+------------+
| 1787 | Delaware       | 1787-12-07 | New Jersey     | 1787-12-18 |
| 1787 | Delaware       | 1787-12-07 | New Jersey     | 1787-12-18 |
| 1787 | Delaware       | 1787-12-07 | Pennsylvania   | 1787-12-12 |
| 1787 | Delaware       | 1787-12-07 | Pennsylvania   | 1787-12-12 |
| 1787 | New Jersey     | 1787-12-18 | Pennsylvania   | 1787-12-12 |
| 1787 | New Jersey     | 1787-12-18 | Pennsylvania   | 1787-12-12 |
...
| 1912 | Arizona        | 1912-02-14 | New Mexico     | 1912-01-06 |
| 1912 | Arizona        | 1912-02-14 | New Mexico     | 1912-01-06 |
| 1959 | Alaska         | 1959-01-03 | Hawaii         | 1959-08-21 |
| 1959 | Alaska         | 1959-01-03 | Hawaii         | 1959-08-21 |
+------+----------------+------------+----------------+------------+
再用 DISTINCT 语句删除重复:

mysql> SELECT DISTINCT YEAR(s1.statehood) AS year,
-> IF(s1.name<s2.name,s1.name,s2.name) AS name1,
-> IF(s1.name<s2.name,s1.statehood,s2.statehood) AS statehood1,
-> IF(s1.name<s2.name,s2.name,s1.name) AS name2,
-> IF(s1.name<s2.name,s2.statehood,s1.statehood) AS statehood2
-> FROM states AS s1 INNER JOIN states AS s2
-> ON YEAR(s1.statehood) = YEAR(s2.statehood) AND s1.name != s2.name
-> ORDER BY year, name1, name2;
+------+----------------+------------+----------------+------------+
| year | name1          | statehood1 | name2          | statehood2 |
+------+----------------+------------+----------------+------------+
| 1787 | Delaware       | 1787-12-07 | New Jersey     | 1787-12-18 |
| 1787 | Delaware       | 1787-12-07 | Pennsylvania   | 1787-12-12 |
| 1787 | New Jersey     | 1787-12-18 | Pennsylvania   | 1787-12-12 |
...
| 1912 | Arizona        | 1912-02-14 | New Mexico     | 1912-01-06 |
| 1959 | Alaska         | 1959-01-03 | Hawaii         | 1959-08-21 |
+------+----------------+------------+----------------+------------+

4、选择支持事务的存储引擎

(Recipe 15.1. Choosing a Transactional Storage Engine)

mysql> SHOW ENGINES\G
*************************** 1. row ***************************
Engine: MyISAM
Support: DEFAULT
Comment: Default engine as of MySQL 3.23 with great performance
*************************** 2. row ***************************
Engine: MEMORY
Support: YES
Comment: Hash based, stored in memory, useful for temporary tables
*************************** 3. row ***************************
Engine: InnoDB
Support: YES
Comment: Supports transactions, row-level locking, and foreign keys
*************************** 4. row ***************************
Engine: BerkeleyDB
Support: YES
Comment: Supports transactions and page-level locking
...
创建表:

CREATE TABLE t1 (i INT) ENGINE = InnoDB;
CREATE TABLE t2 (i INT) ENGINE = BDB;
修改表:

ALTER TABLE t ENGINE = InnoDB;
5、使用 SQL 执行事务

(Recipe 15.2. Performing Transactions Using SQL)

mysql> CREATE TABLE t (i INT) ENGINE = InnoDB;
mysql> START TRANSACTION;
mysql> INSERT INTO t (i) VALUES(1);
mysql> INSERT INTO t (i) VALUES(2);
mysql> COMMIT;
mysql> SELECT * FROM t;
+------+
| i    |
+------+
|    1 |
|    2 |
+------+
关闭自动提交:

mysql> CREATE TABLE t (i INT) ENGINE = InnoDB;
mysql> SET autocommit = 0;
mysql> INSERT INTO t (i) VALUES(1);
mysql> INSERT INTO t (i) VALUES(2);
mysql> COMMIT;
mysql> SELECT * FROM t;
+------+
| i    |
+------+
|    1 |
|    2 |
+------+
开启自动提交:

mysql> SET autocommit = 1;
6、在程序中使用事务(Java 示例)

try
{
conn.setAutoCommit (false);
Statement s = conn.createStatement ();
// move some money from one person to the other
s.executeUpdate ("UPDATE money SET amt = amt - 6 WHERE name = 'Eve'");
s.executeUpdate ("UPDATE money SET amt = amt + 6 WHERE name = 'Ida'");
s.close ();
conn.commit ();
conn.setAutoCommit (true);
}
catch (SQLException e)
{
System.err.println ("Transaction failed, rolling back.");
Cookbook.printErrorMessage (e);
// empty exception handler in case rollback fails
try
{
conn.rollback ();
conn.setAutoCommit (true);
}
catch (Exception e2) { }
}

7、存储过程

Navicat 中创建存储过程注意事项:

<a>、在Navicat里面不需要写头,直接从BEGIN开始即可。

<b>、过程名将在保存的时候填写,参数则可以填在代码定义框下面的参数输入框中,如果要使用返回值,则可以设定类型为FUNCTION,回传框即可填写。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息