1. 程式人生 > >MySQL開發技巧 第二禪(子查詢中匹配兩個值、解決同屬性多值過濾的問題、計算累進稅的問題)

MySQL開發技巧 第二禪(子查詢中匹配兩個值、解決同屬性多值過濾的問題、計算累進稅的問題)

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

    mysql子查詢的使用場景及其好處

        1、什麼是子查詢?
            當一個查詢是另一個查詢的條件時,稱之為子查詢(可以在curd中)

        2、常見的子查詢使用場景
            (寫在前面,一般都選擇用連線來替換子查詢,因為子查詢的效能不好)
            使用子查詢可以避免由於子查詢中的資料產生的重複

    最簡單的子查詢示例(單列)
        1、mysql> select user_name from a where id in (select user_id from c);
            +-----------+
            | user_name |
            +-----------+
            | 唐僧           |
            | 孫悟空       |
            | 豬八戒       |
            | 沙悟淨       |
            +-----------+
            4 rows in set (0.00 sec)
                            ------ 子查詢查出的值是沒重複的 (不明白看下面的示例)
        2、mysql> select * from c;
            +----+---------+-------------+-------+
            | id     | user_id   | timestr          | kills |
            +----+---------+-------------+-------+
            |  1     |              2 | 2013-01-10  |    10 |
            |  2     |              2 | 2013-01-01  |     2 |
            |  3     |              2 | 2013-01-05  |    12 |
            |  4     |              4 | 2013-01-10  |     3 |
            |  5     |              4 | 2013-01-11  |     5 |
            |  6     |              3 | 2013-01-06  |     1 |
            |  7     |              3 | 2013-01-11  |    20 |
            |  8     |              1 | 2013-01-12  |    10 |
            |  9     |              1 | 2013-01-07  |    17 |
            +----+---------+-------------+-------+
            9 rows in set (0.00 sec)
                            ------- ID 出現了多次 但是子查詢之後多個id只有一個結果名


        以上情況闡明瞭子查詢不會重複出現值得情況 下面寫出連線查詢的結果顯示
        3、mysql> select a.user_name from a join c on a.id = c.user_id;
            +-----------+
            | user_name |
            +-----------+
            | 孫悟空        | 
            | 孫悟空        |
            | 孫悟空        |
            | 沙悟淨        |
            | 沙悟淨        |
            | 豬八戒        |
            | 豬八戒        |
            | 唐僧            |
            | 唐僧            |
            +-----------+
            9 rows in set (0.00 sec)
                            ------- 明顯 連線查詢出現的結果是有重複的,但是為了達到與子查詢同等的情況 在連線查詢的情況下使用 distinct 來實現去重 下面是示例
            連線實現去重的結果方法
        4、mysql> select distinct a.user_name from a join c on a.id = c.user_id;
            +-----------+
            | user_name |
            +-----------+
            | 孫悟空        |
            | 沙悟淨        |
            | 豬八戒        |
            | 唐僧            |
            +-----------+
            4 rows in set (0.00 sec)

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

        6、總結:子查詢的結果是可以避免因子查詢的重複而重複的,而子查詢的效率不高,採用連線之後需要用distinc    t就能達到去重同樣的效果
            在將子查詢轉換為連線查詢時一定要注意資料之間是一對多還是多對一的關係,如果是一對多或者多對一 一定要對結果集進行去重處理 才能達到結果

    如何在子查詢中實現多列過濾(多列)
        1、需求是 查詢出每一個取經人打怪最多的日期,並列出取經人的姓名,打怪最多ed日期和打怪的數量
        mysql> select user_id,max(kills) as nums from c group by user_id;
            +---------+------+
            | user_id    | nums |
            +---------+------+
            |               1 |      17 |
            |               2 |     12 |
            |               3 |    20 |
            |               4    |    5 |
            +---------+------+
            4 rows in set (0.00 sec)
                            ----- 運用max查詢出了不同id擊殺怪物最多的id與個體擊殺的數量

        2、mysql> select a.user_name,c.timestr,c.kills from a join c on a.id = c.user_id join (select user_id,max(kills) as cnt from c group by user_id) c on c.user_id = c.user_id and b.kills = c.cnt;
            +-----------+------------+-------+
            | user_name | timestr          | kills |
            +-----------+------------+-------+
            | 孫悟空        | 2013-01-05 |    12 |
            | 沙悟淨        | 2013-01-11 |     5 |
            | 豬八戒        | 2013-01-11 |    20 |
            | 唐僧            | 2013-01-07 |    17 |
            +-----------+------------+-------+
            4 rows in set (0.00 sec)
                            ----- 查詢出每個人打怪最多的日期


    多列過濾的使用場景
        MySQL中獨有的多列過濾方式(其中的where就是過濾)
            mysql> select a.user_name,c.timestr,c.kills from a join c on a.id = c.user_id where(c.user_id,c.kills) in (select user_id,max(kills) from c group by user_id);
            +-----------+------------+-------+
            | user_name | timestr         | kills |
            +-----------+------------+-------+
            | 孫悟空        | 2013-01-05 |    12 |
            | 沙悟淨        | 2013-01-11 |     5 |
            | 豬八戒        | 2013-01-11 |    20 |
            | 唐僧            | 2013-01-07 |    17 |
            +-----------+------------+-------+
            4 rows in set (0.00 sec)


二、解決同屬性多值過濾的問題

    建立一個新的表結構 填充資料
        mysql> select * from d;
        +----+-----------+-----------+-------------+
        | id     | user_name | skill            |  skill_level |
        +----+-----------+-----------+-------------+
        |  1     | 唐僧           | 緊箍咒         |           5 |
        |  2     | 唐僧           | 打坐            |           4 |
        |  3     | 唐僧           | 唸經            |           5 |
        |  4     | 唐僧           | 變化            |           0 |
        |  5     | 豬八戒        | 變化           |           4 |
        |  6     | 豬八戒        | 騰雲           |           3 |
        |  7     | 豬八戒        | 浮水           |           5 |
        |  8     | 豬八戒        | 唸經           |           0 |
        |  9     | 豬八戒        | 緊箍咒       |           0 |
        | 10     | 孫悟空        | 變化           |           5 |
        | 11     | 孫悟空         | 騰雲          |           5 |
        | 12     | 孫悟空        | 浮水           |           3 |
        | 13     | 孫悟空        | 唸經           |           2 |
        | 14      | 孫悟空       | 請神           |           5 |
        | 15      | 沙僧           | 變化           |           2 |
        | 16      | 沙僧           | 騰雲           |           2 |
        | 17      | 沙僧            | 浮水          |           4 |
        | 18      | 沙僧            | 唸經          |           4 |
        | 19      | 沙僧            | 緊箍咒      |            1 |
        | 20     | 孫悟空         | 緊箍咒      |           0 |
        +----+-----------+-----------+-------------+
        20 rows in set (0.00 sec)

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


    如何查詢出同時具有 變化 和 唸經 同時level大於0的人
        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 c.user_id = b.user_id and c.skill = '變化' where b.skill_level > and c.skill_level > 0;

    使用join查詢        
        mysql> select a.user_name,b.skill,c.skill,d.skill from user1 a join user1_skill b on a.id = b.user_id and b.skill = '唸經' join user1_skills c on c.user_id = b.user_id and c.skill = '變化' join user1_skills d on d.user_id = c.user_id and d.skill='騰雲' where b.skill_level > 0 and c.skill_level > 0 and d.skill_level > 0;


        mysql> select a.user_name,b.skill,c.skill,d.skill,e.skill from user1 a left join user1_skill b on a.id = b.user_id and b.skill = '唸經' and b.skill_level > 0 left join user1_skill c on a.id = c.user_id and c.skill = '變化' and s.skill_level > 0 left join user1_skill d on a.id = d.user_id and d.skill = '騰雲' and d.skill_level > 0 left join user1_skill e on a.id = e.user_id and e.skill = '浮水' and e .skill_level > 0;


        mysql> select a.user_name,b.skill,c.skill,d.skill,e.skill from user1 a left join user1_skill b on a.id = b.user_id and b.skill = '唸經' and b.skill_level > 0 left join user1_skill c on a.id = c.user_id and c.skill = '變化' and s.skill_level > 0 left join user1_skill d on a.id = d.user_id and d.skill = '騰雲' and d.skill_level > 0 left join user1_skill 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;

    使用Group by 實現多屬性查詢   遇到多屬性選擇取決一屬性可以使用group by
        group by 應用於分組,對同一組合進行一個分組和排列組合
        select a.user_name form user1 a join user1_skills b on b.user_id = a.id where b.skill in ('唸經','變化','騰雲','浮水') and b.skill_level > 0 group by a.user_name having count(*) >= 2;

三、如何計算累進稅的問題

    什麼是累進稅? 
        累進稅就是不同的基數使用不同的比率。比如個人所得稅
    使用SQL計算個人所得稅
        表1
            mysql> select * from a;
            +----+-----------+-----------------+----------+
            | id     | user_name | over                     | money    |
            +----+-----------+-----------------+----------+
            |  1     | 唐僧           | 旃檀功德佛           | 35000.00 |
            |  2     | 孫悟空       | 淨壇使者               | 28000.00 |
            |  3     | 豬八戒       | 鬥戰神佛               | 15000.00 |
            |  4     | 沙悟淨       | 金身羅漢               | 8000.00  |
            +----+-----------+-----------------+----------+
            4 rows in set (0.00 sec)
        表2
            mysql> select * from tax;
            +----+----------+--------------+--------+
            | id     | low           | high                 | rate   |
            +----+----------+--------------+--------+
            |  1     | 0.00          | 1500.00          | 3.00%  |
            |  2     | 1500.00    | 4500.00        | 10.00% |
            |  3     | 4500.00   | 9000.00        | 20.00% |
            |  4     | 9000.00   | 35000.00      | 25.00% |
            |  5     | 35000.00 | 55000.00      | 30.00% |
            |  6     | 55000.00 | 80000.00      | 35.00% |
            |  7    | 80000.00 | 999999999.00 | 45.00% |
            +----+----------+--------------+--------+
            7 rows in set (0.00 sec)


    如何計算累進稅
        可以使用join實現工資對不同納稅區間的匹配
            mysql> select a.user_name,money,low,high,rate from a join tax b on a.money > b.low order by user_name; 
            +-----------+----------+----------+----------+--------+
            | user_name | money      | low            | high          | rate   |
            +-----------+----------+----------+----------+--------+
            | 唐僧            | 35000.00 | 0.00         | 1500.00    | 3.00%  |
            | 唐僧            | 35000.00 | 1500.00   | 4500.00    | 10.00% |
            | 孫悟空      | 28000.00   | 0.00         | 1500.00     | 3.00%  |
            | 孫悟空     | 28000.00    | 1500.00   | 4500.00     | 10.00% |
            | 沙悟淨     | 8000.00       | 0.00         | 1500.00     | 3.00%  |
            | 沙悟淨     | 8000.00       | 35000.00 | 55000.00 | 30.00% |
            | 沙悟淨     | 8000.00       | 1500.00    | 4500.00    | 10.00% |
            | 沙悟淨     | 8000.00       | 55000.00 | 80000.00 | 35.00% |
            | 沙悟淨     | 8000.00       | 4500.00    | 9000.00   | 20.00% |
            | 豬八戒     | 15000.00      | 1500.00    | 4500.00   | 10.00% |
            | 豬八戒     | 15000.00      | 0.00          | 1500.00    | 3.00%  |
            +-----------+----------+----------+----------+--------+
            11 rows in set (0.00 sec)
                        ------    顯示匹配薪資區間

            使用LEAST函式取得區間內最小的資料
                拓展知識網址 https://www.yiibai.com/mysql/greatest-least.html
                LEAST()函式是GREATEST()函式的相反函式。其目的是為了從列表值返回至少值項(N1,N2,N3,等等)。下面的例子顯示了LEAST()函式適當的使用和輸出:    
                    l>SELECT LEAST(3,5,1,8,33,99,34,55,67,43);
                    +---------------------------------------------------------+
                    | LEAST(3,5,1,8,33,99,34,55,67,43)              |
                    +---------------------------------------------------------+
                    | 1                                                                        |
                    +---------------------------------------------------------+
                    1 row in set (0.00 sec)


            列出所有範稅額校價
            mysql> select a.user_name,a.money,b.low,b.high,least(a.money-b.low,b.high-b.low) as curmoney,rate from a join tax b on a.money>b.low order by user_name,low;

            結果寫法
            mysql> select user_name,sum(curmoney*rate) from (select user_name,money,low,high,least(money-low,high-low) as curmoney,rate from a join tax b on a.money > b.low) a group by user_name;