1. 程式人生 > >MySQL開發技巧 第二禪(行轉列 列轉行、生成唯一的序列、刪除重複的資料)

MySQL開發技巧 第二禪(行轉列 列轉行、生成唯一的序列、刪除重複的資料)

一、如何進行行列或者列行的轉換

    行轉列的關鍵是union ,列轉行的關鍵是join

    需要進行行轉列的場景
        報表的設計
            (假設A表是一行一行的資料 分別是日期對應的銷售金額有多行。需要將其轉換為一列一列的資料 第一行是日期 第二行是銷售金額)
        彙總的顯示

    需求分析
        將A表結構轉至成B 再將B結構轉至成C
        mysql> select a.user_name as 姓名,c.kills as 打怪數 from a join c on a.id = c.user_id;
        A +-----------+-----------+
            | 姓名            | 打怪數       |
            +-----------+-----------+
            | 豬八戒        |               10 |
            | 豬八戒        |                 2 |
            | 豬八戒        |               12 |
            | 沙僧            |                 3 |
            | 沙僧            |                 5 |
            | 孫悟空        |                  1 |
            | 孫悟空        |               20 |
            | 唐僧            |               10 |
            | 唐僧            |                17 |
            +-----------+-----------+
            9 rows in set (0.00 sec)

        mysql> select a.user_name,sum(c.kills) from a join c on a.id = c.user_id group by a.user_name;
        B    +-----------+--------------+
            | user_name | sum(c.kills) |
            +-----------+--------------+
            | 唐僧            |           27 |
            | 孫悟空        |           21 |
            | 沙僧            |            8 |
            | 豬八戒        |           24 |
            +-----------+--------------+
            4 rows in set (0.00 sec)

        結果1 使用Cross Join—————交叉連線(類笛卡爾積的方法)
        mysql> select * from (select sum(c.kills) as '孫悟空' from a join c on a.id = c.user_id and a.user_name='孫悟空' gr    by a.user_name) a cross join (select sum(c.kills) as '豬八戒' from a join c on a.id = c.user_id and a.user_name='豬八戒' group by a.user_name) b cross join (select sum(c.kills) as '沙僧' from a join c on a.id = c.user_id and a.user_name='沙僧' group by a.user_name) c;
        C    +-----------+-----------+--------+
            | 孫悟空           |      豬八戒  | 沙僧      |
            +-----------+-----------+--------+
            |                21 |              24 |            8 |
            +-----------+-----------+--------+
            1 row in set (0.00 sec)

        結果2  使用case case when 條件 then 獲取結果 end
        mysql> select sum(case when a.user_name='豬八戒' then c.kills end) as 豬八戒, sum(case when a.user_name='沙僧' then c.kills end) as 沙僧,sum(case when a.user_name='孫悟空' then c.kills end) as 孫悟空 from a join c on a.id = c.user_id;
        C    +-----------+--------+-----------+
            | 豬八戒           | 沙僧       | 孫悟空     |
            +-----------+--------+-----------+
            |               24 |            8 |               21 |
            +-----------+--------+-----------+
            1 row in set (0.00 sec)

    單列轉多行場景介紹
        mysql> select a.user_name,replace(substring(substring_index(mobile,',',a.id),char_length(substring_index(mobile,',',a.id-1))+1),',','') as mobile from tb_sequence a cross join (select a.user_name,concat(mobile,',')as mobile,length(mobile)-length(replace(mobile,',',''))+1 size from a) a on a.id <= b.size;

        mysql> select a.user_name,concat(over,',')as mobile,length(over)-length(replace(over,',',''))+1 size from a;
            +-----------+--------------------------------------------+------+
            | user_name | mobile                                                                 | size |
            +-----------+--------------------------------------------+------+
            | 唐僧            | 213312,31231231,32121312321,                     |        3 |
            | 豬八戒        | 321324213,3214213,                                        |        2 |
            | 孫悟空        | 534223423,423523321,3242342342,432423423,  |    4 |
            | 沙僧            | 543123124,654654543,                                 |         2 |
            +-----------+--------------------------------------------+------+
            4 rows in set (0.00 sec)

    SQL: coalesce()函式
        ①用途:
            將空值替換成其他值
            返回第一個非空值
        ②表示式:
            COALESCE是一個函式, (expression_1, expression_2, ...,expression_n)依次參考各引數表示式,遇到非null值即停止並返回該值。如果所有的表示式都是空值,最終將返回一個空值。使用COALESCE在於大部分包含空值的表示式最終將返回空值。

            coalesce(item1,item2,item3)
            依次判斷item值,返回第一個不為null的值,若全為null,則返回null;
        ③例項:
            mysql> select a.id,a.user_name,a.over,coalesce(b.id,'無') as id,coalesce(b.user_name,'佚名') as 姓名, coalesce(b.over,'無業') as 職業 from a left join b on a.user_name = b.user_name;
    +----+-----------+-------------------------------------------+------+-----------+--------------+
    | id     | user_name | over                                                                  | id       | 姓名           | 職業                 |
    +----+-----------+-------------------------------------------+------+-----------+--------------+
    |  3     | 孫悟空       | 534223423,423523321,3242342342,432423423, | 1    | 孫悟空     | 齊天大聖      |
    |  1      | 唐僧           | 213312,31231231,32121312321,                   | 無   | 佚名       | 無業         |
    |  2     | 豬八戒       | 321324213,3214213,                                      | 無   | 佚名       | 無業          |
    |  4     | 沙僧           | 543123124,654654543,                                | 無   | 佚名       | 無業         |
    +----+-----------+-------------------------------------------+------+-----------+--------------+
    4 rows in set (0.00 sec)


二、如何生成唯一序列號(常見於票據的生成或者主鍵的生成)

       需要使用唯一序列號的場景
            1.資料庫的主鍵
                主鍵選擇時 儘可能的要小、唯一不重複
            2.業務序列號
                發票、車票、訂單

        序列號的生成
            1. MySQL中    : AUTO_INCERMENT
            2. SQLServer: IDENTITY/SEQUENCE
            3. Orale    : SEQUENCE
            4. PgSql    : SEQUENCE

        如何選擇生成序列號的方式
            1. 優先選擇系統提供的序列號選擇方式    (缺點是:容易產生空洞、在事物操作插入之後回滾 會佔用序列號)
            2. 在特殊情況下可以使用SQL方式生成序列號

        如何使用SQL語句建立特殊需求的序列號
            需求:生成訂單的序列號,並且訂單格式如下
                YYYYMMDDHHMMSSS。如201807200000002
            DECLARE v_cnt INT;
            DECLARE v_timestr INT;
            DECLARE v_timestr = DATE_FORMAT(NOW(),'%Y%m%d');
            SELECT ROUND(RAND()*100,0)+1 INTO v_cnt;
            START TRANSACTION;
                UPDATE order_seq(timestr,order_sn)+v_cnt WHERE timestr = v_timestr;
                IF ROW_COUNT() = 0 THEN
                    INSERT INTO order_seq(timestr,order_sn) VALUES(v_timestr,v_cnt);
                END IF;
                SELECT CONCAT(v_timestr,LPAD(order_sn,7,0)) AS order_sn
                    FROM order_seq WHERE timestr = v_timestr;
            COMMIT;


            UPDATE order_seq SET order_sn = order_sn + v_cnt WHERE timestr = v_timestr;
            IF ROW_COUNT() = 0 THEN
            INSERT INTO order_seq(timestr,order_sn) VALUES(v_timestr,v_cnt);
            END IF;
            SELECT CONCAT(v_timestr,LPAD(order_sn,7,0))AS order_sn
            FROM order_seq WHERE timestr = v_timestr;
            COMMIT;


三、如何刪除重複資料

    產生資料重複的原因
        1. 人為原因、如重複錄入資料、重複提交 等
        2. 系統原因、由於系統升級或設計的原因使原來可以重複的資料變為不能重複了。

    如何查詢資料是否重複?
        利用group by和having從句處理
            sql資料格式顯示
                mysql> select * from a;
                    +----+-----------+-------------------------------------------+
                    | id     | user_name | over                                      |
                    +----+-----------+-------------------------------------------+
                    |  1     | 唐僧            | 213312,31231231,32121312321,              |
                    |  2     | 豬八戒        | 321324213,3214213,                                |
                    |  3     | 孫悟空        | 534223423,423523321,3242342342,432423423, |
                    |  4     | 沙僧            | 543123124,654654543,                          |
                    |  5     | 唐僧            | 321321313,321312                                  |
                    |  6     | 唐僧            | 321313,32321                                             |
                    +----+-----------+-------------------------------------------+
                    6 rows in set (0.00 sec)
            1. 處理方式
                採用Group by 與 Having 檢視資料是否重複
                Having解釋: 如果需要對組函式的結果作為條件,那麼不能使用where子句,必須使用having子句。

            寫法1.(檢視重複資料的數量)
                mysql> select user_name,count(*) from a group by user_name having count(*) > 1;
                    +-----------+----------+
                    | user_name | count(*) |
                    +-----------+----------+
                    | 唐僧            |               3 |
                    +-----------+----------+
                    1 row in set (0.00 sec)

            2. 刪除簡單重複資料,對於相同資料保留ID最大的
                第一步:
                    mysql> select user_name,count(*) as 出現次數,max(id) as id from a GROUP BY user_name having count(*) > 1 ;
                        +-----------+--------------+------+
                        | user_name | 出現次數          |     id   |
                        +-----------+--------------+------+
                        | 唐僧            |                       3 |        6 |
                        | 孫悟空        |                       2  |        7 |
                        +-----------+--------------+------+
                                    2 rows in set (0.00 sec)        

                第二步:
                    mysql> delete a from a join(select user_name,count(*),max(id) as id from a GROUP BY user_name having count(*) > 1) b on a.user_name = b.user_name where a.id < b.id;
                        Query OK, 3 rows affected (0.00 sec)

                    mysql> select * from a;
                        +----+-----------+-----------------+
                        | id    | user_name | over                      |
                        +----+-----------+-----------------+
                        |  6    | 唐僧            | 321313,32321      |
                        |  7     | 孫悟空       | 321,321321      |
                        |  9     | 沙僧           | 423434,32432432 |
                        | 10    | 豬八戒       | 24324,43243223  |
                        +----+-----------+-----------------+
                                    4 rows in set (0.00 sec)

            3. 刪除複雜重複資料
                mysql> select * from a;
                            重複的資料在 over列中的321
                    +----+-----------+----------------+
                    | id    | user_name | over           |
                    +----+-----------+----------------+
                    |  6    | 唐僧           | 321,32321      |
                    |  7    | 孫悟空       | 321,321321     |
                    |  9    | 沙僧           | 423434,321,321 |
                    | 10    | 豬八戒      | 24324,43243223 |
                    +----+-----------+----------------+
                    4 rows in set (0.00 sec)

                刪除某個列中的重複資料