1. 程式人生 > >MySQL的開發技巧3

MySQL的開發技巧3

參考:

1、 MySQL開發技巧

2、 MySQL開發技巧2

3、MySQL開發技巧3

1、如何在子查詢中測試兩個值

2、如何解決多屬性查詢問題

3、如何計算累進稅類問題

子查詢使用的場景

  • 使用子查詢可以避免由於子查詢中資料產生的重複

例子:有哪些人在取經過程中打了怪

mysql> set names 'GBK';
Query OK, 0 rows affected (0.00 sec)

mysql> select user_name
    -> from user1
    -> where id in(select user_id from user_kills);
+-----------+
| user_name |
+-----------+
| 孫悟空          |
| 沙僧        |
| 豬八戒         |
+-----------+
3 rows in set (0.01 sec)

-- 子查詢的表user_kills中有重複的資料。
mysql> select * from user_kills;
+----+---------+---------------------+-------+
| id | user_id | timestr             | kills |
+----+---------+---------------------+-------+
|  1 |       2 | 2013-01-10 00:00:00 |    10 |
|  2 |       2 | 2013-02-01 00:00:00 |     2 |
|  3 |       2 | 2013-02-05 00:00:00 |    12 |
|  4 |       4 | 2013-01-10 00:00:00 |     3 |
|  5 |       4 | 2013-02-11 00:00:00 |     5 |
|  6 |       4 | 2013-02-06 00:00:00 |     1 |
|  7 |       3 | 2013-01-11 00:00:00 |    20 |
|  8 |       3 | 2013-02-12 00:00:00 |    10 |
|  9 |       3 | 2013-02-07 00:00:00 |    17 |
+----+---------+---------------------+-------+
9 rows in set (0.00 sec)

-- 如果使用連線,就會有重複資料

mysql> select user_name
    -> from user1
    -> left join user_kills on user1.id = user_kills.user_id;
+-----------+
| user_name |
+-----------+
| 唐僧         |
| 孫悟空          |
| 孫悟空          |
| 孫悟空          |
| 沙僧        |
| 沙僧        |
| 沙僧        |
| 豬八戒         |
| 豬八戒         |
| 豬八戒         |
+-----------+
10 rows in set (0.00 sec)

-- 如果使用連線,就會有重複資料--》解決方法:distinct


mysql> select distinct user_name
    -> from user1
    -> left join user_kills on user1.id = user_kills.user_id;
+-----------+
| user_name |
+-----------+
| 唐僧         |
| 孫悟空          |
| 沙僧        |
| 豬八戒         |
+-----------+
4 rows in set (0.01 sec)


  • 使用子查詢更符合語意,更好理解

如何在子查詢中匹配兩個值

查詢出每一個取經人打怪最多的日期,並列出取經人的姓名、打怪最多的日期和打怪的數量。

mysql> -- S1-取出user_id及最多的打怪數量
mysql> select user_id,MAX(kills) as max_cnt
    -> from user_kills
    -> GROUP BY user_id;
+---------+---------+
| user_id | max_cnt |
+---------+---------+
|       2 |      12 |
|       3 |      20 |
|       4 |       5 |
+---------+---------+
3 rows in set (0.00 sec)

mysql> -- S2-取經人的姓名、打怪最多的日期和打怪的數量
mysql> select a.user_name,b.timestr,kills
    -> from user1 a
    -> join user_kills b on a.id = b.user_id
    -> join ( select user_id,max(kills) as max_cnt
    ->         from user_kills
    ->         group by user_id
    -> ) c on b.user_id = c.user_id and b.kills = c.max_cnt
    -> ;
+-----------+---------------------+-------+
| user_name | timestr             | kills |
+-----------+---------------------+-------+
| 豬八戒         | 2013-02-05 00:00:00 |    12 |
| 沙僧        | 2013-02-11 00:00:00 |     5 |
| 孫悟空          | 2013-01-11 00:00:00 |    20 |
+-----------+---------------------+-------+
3 rows in set (0.01 sec)

mysql>

方法2:多列過濾。

MySQL中獨有的多列過濾方式

mysql> -- 查詢出每一個取經人打怪最多的日期,並列出取經人的姓名、打怪最多的日期和打怪的數量。
mysql> -- 多列過濾
mysql> SELECT a.user_name,b.timestr,kills
    -> from user1 a
    -> join user_kills b on a.id=b.user_id
    -> WHERE (b.user_id,b.kills) IN (
    ->       SELECT user_id,max(kills)
    ->       FROM user_kills
    ->       GROUP BY user_id
    -> );
+-----------+---------------------+-------+
| user_name | timestr             | kills |
+-----------+---------------------+-------+
| 豬八戒         | 2013-02-05 00:00:00 |    12 |
| 沙僧        | 2013-02-11 00:00:00 |     5 |
| 孫悟空          | 2013-01-11 00:00:00 |    20 |
+-----------+---------------------+-------+
3 rows in set (0.00 sec)

如何解決同一屬性的多值過濾

什麼是同一屬性的多值過濾

新增一個技能表

mysql> --
mysql> -- Table structure for table `user1_skills`
mysql> --
mysql> CREATE TABLE user1_skills (
    ->   id INT UNSIGNED NOT NULL AUTO_INCREMENT,
    ->   user_id  INT UNSIGNED NOT  NULL,
    ->   skill VARCHAR(10) NULL,
    ->   skill_level INT UNSIGNED NOT NULL DEFAULT 0,
    ->   PRIMARY KEY  (id)
    -> )ENGINE=InnoDB DEFAULT CHARSET=utf8;
Query OK, 0 rows affected (0.01 sec)

INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(1,'緊箍咒',5);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(1,'打坐',4);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(1,'唸經',5);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(1,'變化',0);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(2,'變化',4);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(2,'騰雲',3);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(2,'浮水',5);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(2,'唸經',0);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(2,'緊箍咒',0);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(3,'變化',5);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(3,'騰雲',5);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(3,'浮水',3);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(3,'唸經',2);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(3,'請神',5);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(3,'緊箍咒',0);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(3,'緊箍咒',0);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(4,'變化',2);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(4,'騰雲',2);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(4,'浮水',4);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(4,'唸經',1);
INSERT INTO user1_skills (user_id,skill,skill_level) VALUES(4,'緊箍咒',0);

如何查詢出同時具有變化和唸經這兩項技能的取經人?



mysql> select a.user_name,b.skill,b.skill_level
    -> from user1 a
    -> join user1_skills b on a.id = b.user_id
    -> where skill in('變化','唸經') and skill_level >0;
+-----------+-------+-------------+
| user_name | skill | skill_level |
+-----------+-------+-------------+
| 唐僧         | 唸經     |           5 |
| 豬八戒         | 變化    |           4 |
| 孫悟空          | 變化    |           5 |
| 孫悟空          | 唸經     |           2 |
| 沙僧        | 變化    |           2 |
| 沙僧        | 唸經     |           1 |
+-----------+-------+-------------+
6 rows in set (0.00 sec)

-- 使用join

mysql> select a.user_name,b.skill,c.skill
    -> from user1 a
    -> join user1_skills b on a.id = b.user_id and b.skill='唸經'
    -> join user1_skills c on b.user_id = c.user_id and c.skill='變化'
    -> where b.skill_level >0 and c.skill_level >0;
+-----------+-------+-------+
| user_name | skill | skill |
+-----------+-------+-------+
| 孫悟空          | 唸經     | 變化    |
| 沙僧        | 唸經     | 變化    |
+-----------+-------+-------+
2 rows in set (0.00 sec)


mysql> -- 如何查詢出同時具有變化和唸經、騰雲,這3項技能的取經人?
mysql> -- 使用join
mysql> select a.user_name,b.skill,c.skill,d.skill
    -> from user1 a
    -> join user1_skills b on a.id = b.user_id and b.skill='唸經'
    -> join user1_skills c on b.user_id = c.user_id and c.skill='變化'
    -> join user1_skills d on c.user_id = d.user_id and d.skill='騰雲'
    -> where b.skill_level >0 and c.skill_level >0 and d.skill_level>0;
+-----------+-------+-------+-------+
| user_name | skill | skill | skill |
+-----------+-------+-------+-------+
| 孫悟空          | 唸經     | 變化    | 騰雲      |
| 沙僧        | 唸經     | 變化    | 騰雲      |
+-----------+-------+-------+-------+
2 rows in set (0.00 sec)

改進,使用LEFT JOIN代替 JOIN

mysql> -- 如何查詢出同時具有變化和唸經、騰雲,這3項技能的取經人?
mysql> -- 改進:使用left join
mysql> select a.user_name,b.skill,c.skill,d.skill,e.skill
    -> from user1 a
    -> left join user1_skills b on a.id = b.user_id and b.skill='唸經' and b.skill_level >0
    -> left join user1_skills c on a.id = c.user_id and c.skill='變化' and c.skill_level >0
    -> left join user1_skills d on a.id = d.user_id and d.skill='騰雲' and d.skill_level >0
    -> left join user1_skills e on a.id = e.user_id and e.skill='浮水' and e.skill_level >0
    -> ;
+-----------+-------+-------+-------+-------+
| user_name | skill | skill | skill | skill |
+-----------+-------+-------+-------+-------+
| 唐僧         | 唸經     | NULL  | NULL  | NULL  |
| 孫悟空          | 唸經     | 變化    | 騰雲      | 浮水     |
| 沙僧        | 唸經     | 變化    | 騰雲      | 浮水     |
| 豬八戒         | NULL  | 變化    | 騰雲      | 浮水     |
+-----------+-------+-------+-------+-------+
4 rows in set (0.00 sec)

 是擁有兩種及以上技能?

mysql> --  是擁有兩種及以上技能?
mysql> select a.user_name,b.skill,c.skill,d.skill,e.skill
    -> from user1 a
    -> left join user1_skills b on a.id = b.user_id and b.skill='唸經' and b.skill_level >0
    -> left join user1_skills c on a.id = c.user_id and c.skill='變化' and c.skill_level >0
    -> left join user1_skills d on a.id = d.user_id and d.skill='騰雲' and d.skill_level >0
    -> left join user1_skills e on a.id = e.user_id and e.skill='浮水' and e.skill_level >0
    -> where (case when b.skill is not null then 1 else 0 end)
    -> + (case when c.skill is not null then 1 else 0 end)
    -> + (case when d.skill is not null then 1 else 0 end)
    -> + (case when e.skill is not null then 1 else 0 end)>=2
    -> ;
+-----------+-------+-------+-------+-------+
| user_name | skill | skill | skill | skill |
+-----------+-------+-------+-------+-------+
| 孫悟空          | 唸經     | 變化    | 騰雲      | 浮水     |
| 沙僧        | 唸經     | 變化    | 騰雲      | 浮水     |
| 豬八戒         | NULL  | 變化    | 騰雲      | 浮水     |
+-----------+-------+-------+-------+-------+
3 rows in set (0.00 sec)

使用GROUP方法解決問題

mysql> -- 使用GROUP方法解決問題
mysql> select a.user_name
    -> from user1 a
    -> join user1_skills b on a.id = b.user_id
    -> where b.skill in('唸經','變化','騰雲','浮水') and b.skill_level >0
    -> GROUP By a.user_name
    -> HAVING count(*) >=2
    -> ;
+-----------+
| user_name |
+-----------+
| 孫悟空          |
| 沙僧        |
| 豬八戒         |
+-----------+
3 rows in set (0.00 sec)

如何計算累進稅類問題

什麼是累進稅?——最常見:個人所得稅

全月應納稅所得額

稅率

速算扣除數(元)

全月應納稅所得額不超過1500元

3%

0

全月應納稅所得額超過1500元至4500元

10%

105

全月應納稅所得額超過4500元至9000元

20%

555

全月應納稅所得額超過9000元至35000元

25%

1005

全月應納稅所得額超過35000元至55000元

30%

2755

全月應納稅所得額超過55000元至80000元

35%

5505

全月應納稅所得額超過80000元

45%

13505

mysql> ALTER TABLE user1 ADD money float   NULL;
Query OK, 4 rows affected (0.03 sec)
Records: 4  Duplicates: 0  Warnings: 0

mysql> desc user1;
+-----------+------------------+------+-----+---------+----------------+
| Field     | Type             | Null | Key | Default | Extra          |
+-----------+------------------+------+-----+---------+----------------+
| id        | int(10) unsigned | NO   | PRI | NULL    | auto_increment |
| user_name | varchar(30)      | NO   | MUL | NULL    |                |
| over      | varchar(50)      | YES  |     | NULL    |                |
| mobile    | varchar(100)     | YES  |     | NULL    |                |
| money     | float            | YES  |     | NULL    |                |
+-----------+------------------+------+-----+---------+----------------+
5 rows in set (0.01 sec)

-- 更新 user1表的money欄位
update user1 set money = 35000 where id =1;
update user1 set money = 15000 where id =2;
update user1 set money = 28000 where id =3;
update user1 set money = 8000 where id =4;

--
-- Table structure for table `taxRate`
--
CREATE TABLE taxRate (
  id INT UNSIGNED NOT NULL AUTO_INCREMENT,
  low float NOT  NULL,
  high float NOT NULL,
  rate float NOT NULL,
  tax_money float NULL,
  PRIMARY KEY  (id)
)ENGINE=InnoDB DEFAULT CHARSET=utf8;


-- 插入 taxRate
insert taxRate (low,high,rate) VALUES(0,1500,0.03);
insert taxRate (low,high,rate) VALUES(1500,4500,0.10);
insert taxRate (low,high,rate) VALUES(4500,9000,0.20);
insert taxRate (low,high,rate) VALUES(9000,35000,0.25);
insert taxRate (low,high,rate) VALUES(35000,55000,0.30);
insert taxRate (low,high,rate) VALUES(55000,80000,0.35);
insert taxRate (low,high,rate) VALUES(80000,999999999.00,0.45);



mysql> select user_name,money from user1;
+-----------+-------+
| user_name | money |
+-----------+-------+
| 唐僧         | 35000 |
| 豬八戒         | 15000 |
| 孫悟空          | 28000 |
| 沙僧        |  8000 |
+-----------+-------+
4 rows in set (0.00 sec)

mysql> select * from taxRate;
+----+-------+------------+------+-----------+
| id | low   | high       | rate | tax_money |
+----+-------+------------+------+-----------+
|  1 |     0 |       1500 | 0.03 |      NULL |
|  2 |  1500 |       4500 |  0.1 |      NULL |
|  3 |  4500 |       9000 |  0.2 |      NULL |
|  4 |  9000 |      35000 | 0.25 |      NULL |
|  5 | 35000 |      55000 |  0.3 |      NULL |
|  6 | 55000 |      80000 | 0.35 |      NULL |
|  7 | 80000 | 1000000000 | 0.45 |      NULL |
+----+-------+------------+------+-----------+
7 rows in set (0.00 sec)

 

使用JON實現工資對不同納稅區間的匹配

mysql> -- 查詢稅類
mysql> select a.user_name,money,low,high,rate
    -> from user1 a
    -> join taxRate b on a.money > b.low
    -> order by a.user_name,b.id
    ->
    -> ;
+-----------+-------+------+-------+------+
| user_name | money | low  | high  | rate |
+-----------+-------+------+-------+------+
| 唐僧         | 35000 |    0 |  1500 | 0.03 |
| 唐僧         | 35000 | 1500 |  4500 |  0.1 |
| 唐僧         | 35000 | 4500 |  9000 |  0.2 |
| 唐僧         | 35000 | 9000 | 35000 | 0.25 |
| 孫悟空          | 28000 |    0 |  1500 | 0.03 |
| 孫悟空          | 28000 | 1500 |  4500 |  0.1 |
| 孫悟空          | 28000 | 4500 |  9000 |  0.2 |
| 孫悟空          | 28000 | 9000 | 35000 | 0.25 |
| 沙僧        |  8000 |    0 |  1500 | 0.03 |
| 沙僧        |  8000 | 1500 |  4500 |  0.1 |
| 沙僧        |  8000 | 4500 |  9000 |  0.2 |
| 豬八戒         | 15000 |    0 |  1500 | 0.03 |
| 豬八戒         | 15000 | 1500 |  4500 |  0.1 |
| 豬八戒         | 15000 | 4500 |  9000 |  0.2 |
| 豬八戒         | 15000 | 9000 | 35000 | 0.25 |
+-----------+-------+------+-------+------+
15 rows in set (0.00 sec)

 

mysql> -- 查詢稅類區間
mysql> select a.user_name,money,low,high,least(money-low,high-low) as curmoney,rate
    -> from user1 a
    -> join taxRate b on a.money > b.low
    -> order by a.user_name,b.id
    -> ;
+-----------+-------+------+-------+----------+------+
| user_name | money | low  | high  | curmoney | rate |
+-----------+-------+------+-------+----------+------+
| 唐僧         | 35000 |    0 |  1500 |     1500 | 0.03 |
| 唐僧         | 35000 | 1500 |  4500 |     3000 |  0.1 |
| 唐僧         | 35000 | 4500 |  9000 |     4500 |  0.2 |
| 唐僧         | 35000 | 9000 | 35000 |    26000 | 0.25 |
| 孫悟空          | 28000 |    0 |  1500 |     1500 | 0.03 |
| 孫悟空          | 28000 | 1500 |  4500 |     3000 |  0.1 |
| 孫悟空          | 28000 | 4500 |  9000 |     4500 |  0.2 |
| 孫悟空          | 28000 | 9000 | 35000 |    19000 | 0.25 |
| 沙僧        |  8000 |    0 |  1500 |     1500 | 0.03 |
| 沙僧        |  8000 | 1500 |  4500 |     3000 |  0.1 |
| 沙僧        |  8000 | 4500 |  9000 |     3500 |  0.2 |
| 豬八戒         | 15000 |    0 |  1500 |     1500 | 0.03 |
| 豬八戒         | 15000 | 1500 |  4500 |     3000 |  0.1 |
| 豬八戒         | 15000 | 4500 |  9000 |     4500 |  0.2 |
| 豬八戒         | 15000 | 9000 | 35000 |     6000 | 0.25 |
+-----------+-------+------+-------+----------+------+
15 rows in set (0.00 sec)


mysql> -- 查詢每人稅類總額
mysql> select user_name,sum(curmoney*rate)
    -> from(
    ->   select a.user_name,money,low,high,least(money-low,high-low) as curmoney,rate
    ->   from user1 a
    ->   join taxRate b on a.money > b.low
    -> ) a
    -> GROUP BY user_name
    -> ;
+-----------+--------------------+
| user_name | sum(curmoney*rate) |
+-----------+--------------------+
| 唐僧         |  7745.000016875565 |
| 孫悟空          |  5995.000016875565 |
| 沙僧        | 1045.0000138953328 |
| 豬八戒         |  2745.000016875565 |
+-----------+--------------------+
4 rows in set (0.00 sec)