mysql常用報表處理及數據遷移寫法SQL

熟悉一些常用的sql寫法便於工作中快速導出數據,本文不涉及到業務,所以對錶庫做了名字的修改,僅提供一些用法的說明。
以下直接舉例子並講解
 
1 單表批量數據遷移
場景:日誌遷移
具體實例:將test_log2日誌表2的數據全部遷移到test_log1日誌表1
sql:
INSERT INTO `xxx`.test_log1
(
    `operate_account_id`,
    `student_id`, `transfer_type`, `gmt_create`, `gmt_modify`
)
SELECT
    operate_account_id AS operate_account_id
    ,student_id AS student_id    
    ,1 AS transfer_type
    ,gmt_create AS gmt_create
    ,gmt_modify AS gmt_modify
FROM `xxx`.test_log2;
說明:數據量只有幾十萬的話,還是很輕鬆的,可以直接使用上面sql
 
2 聯表批量更新數據
場景:tab2表的數據
具體實例:需要把tab2表的最新記錄的insertTime更新到tab1的lastFollowTime
sql:
UPDATE `xxx`.tab1 t
    INNER JOIN (
        SELECT MAX(insertTime) AS insertTime,userId FROM `xx`.tab2
            WHERE type!=2 OR (type=2 and (content is not null and content != "" ))  GROUP BY userId
    ) f
    ON t.UserId = f.userId
    SET t.lastFollowTime = f.insertTime
    WHERE f.insertTime is not null
        AND (t.lastFollowTime IS NULL
            OR DATE_FORMAT(f.insertTime, '%Y-%m-%d %H:%i:%s') <  DATE_FORMAT(f.insertTime, '%Y-%m-%d %H:%i:%s')
        );
說明:這裏有兩個小技巧
- 第一個是left join子查詢裏面用分組+max找出當前userId最新的那條記錄(算是一個找最新記錄的小技巧)
- 第二個是DATE_FORMAT(f.insertTime, '%Y-%m-%d %H:%i:%s')進行比較,因爲`xx`.tab2的時間格式是這樣的"2019-09-10 17:57:48.793",所以“2019-09-10 17:57:48.793”會永遠大於“2019-09-10 17:57:48”,爲了過濾這些條件,就加了個DATE_FORMAT
 
3 多次left join聯表導出數據
場景:查出某個時間段帶有某些字段的報表數據
具體實例:查出2019/0.9/01至2019/10/01之間的數據,以下會說明一些查的技巧
sql:
SELECT
    # S點
    DATE_FORMAT(tc.start_date, "%Y/%m/%d %H:%i") AS "開始時間"
    ,DATE_FORMAT(date_add(tc.start_date, interval 1 hour), "%Y/%m/%d %H:%i") AS "結束時間"
    # A點
    ,dep.name AS "部門"
    # B點
    ,IF(aspc.payDate IS NOT NULL, "是", "否") AS "是否存在B點數據"
    ,IF(aspc.payDate IS NOT NULL, aspc.payDate, "") AS "B點數據對應時間"
    
FROM `xxxx`.`u_t_c_s` ut
    INNER JOIN `xxxx`.`t_c_s` tc ON ut.t_c_s_id = tc.id     
    INNER JOIN `xxx`.userinfo ui ON ui.Id = tc.user_id
    # 獲得A點數據
    INNER JOIN `xxxx`.account acc ON acc.userId = ui.Id
    INNER JOIN
    (
        SELECT a2.name AS groupName,a1.id AS groupId FROM `xxxx`.`dept` a1,`xxxx`.`dept` a2
            WHERE a1.pId = a2.id
    ) dep
    ON dep.groupId = acc.deptId
    # 獲得B點數據
    LEFT JOIN
    (
        SELECT MAX(IF(fitstDate IS NOT NULL, fitstDate, secondDate)) AS payDate, userId FROM `xxx`.table_b WHERE state=3 GROUP BY userId
    ) aspc
    ON aspc.userId = ut.user_id
    
WHERE tc.start_date >= '2019-09-01 00:00:00' AND tc.start_date <= '2019-10-01 00:00:00'
    AND tc.type IN (1, 2) AND tc.`status`=0 AND ut.`status`=0;    
說明:上面sql使用了點技巧
- S點的技巧:DATE_FORMAT用法,可以直接獲取當前時間一個鐘後時間,用法見:https://www.cnblogs.com/kitor/p/11099196.html
- A點技巧:獲取A點數據採用了dept自己跟自己連接的方式來處理,使用pId父Id關聯
- B點技巧:這裏採用了跟"2 聯表批量更新數據"類似的技巧,max+group by篩選出每一個userId對應最新的那條數據
額外:其中IINER JOIN是內連,必須要符合的條件(可用於篩選過濾條件),LEFT JOI是外連,用於關聯一些可能存在的數據
 
 
4 按範圍導出多個時間段的數量分佈
範圍也屬於條件的一種,可用CASE..WHEN,或者用區分INTERVAL這個範圍更簡單,事例如下:
INTERVAL用法:
SELECT
    INTERVAL(tmp.totalTime,5*60,10*60,20*60,30*60,40*60,50*60,60*60+1) AS TIME,
   COUNT(*)
FROM
(
    SELECT MAX(totalTime) AS totalTime,account FROM
  `xxxx`.`tab_a`
    WHERE
    id IN (
     # 這裏是一系列的子查詢
     SELECT xx FROM XXXXXXXX
  )
  GROUP BY account     
) tmp
GROUP BY TIME;
這裏的步驟是:
    - 紅色部分子查詢先用group by account 根據account分組,找出每個account對應的最大totalTime的那條記錄
    - 以上面作爲結果集返回,然後使用INTERVAL劃分區間,表示爲如下:
        - 區間爲:(tmp.totalTime < 5*60)、(tmp.totalTime >= 5*60 && tmp.totalTime < 10*60)、(tmp.totalTime >= 10*60 && tmp.totalTime < 20*60)....................
    - 然後COUNT(*)就可以把每個區間的數量統計出來
參考文章:https://www.cnblogs.com/bindot/p/interval.html
 
CASE..WHEN用法(作用與上面一致):
SELECT
CASE
    totalTime
    WHEN totalTime = 0 THEN
    '0分鐘'
    WHEN 5>totalTime >= 0 THEN
    '5分鐘'
    WHEN 10>totalTime >= 5 THEN
    '10分鐘'
    WHEN 20>totalTime >= 10 THEN
    '20分鐘'
    WHEN 30>totalTime >= 20 THEN
    '30分鐘'
    WHEN 40>totalTime >= 30 THEN
    '40分鐘'
    WHEN 50>totalTime >= 40 THEN
    '50分鐘'
    WHEN 60>totalTime >= 50 THEN
    '60分鐘'
  END AS time,
  count(*)
FROM
(
    SELECT MAX(totalTime) AS totalTime,account FROM
    `xxx`.`tab_a`
        WHERE
          id IN (
                SELECT MAX(totalTime) AS totalTime,account FROM
                  `xxxx`.`tab_a`
                    WHERE
                    id IN (
                     # 這裏是一系列的子查詢
                     SELECT xx FROM XXXXXXXX
                  )
                  GROUP BY account     
          )
)
GROUP BY
CASE
    totalTime
    WHEN totalTime = 0 THEN
  '0分鐘'
  WHEN 5>totalTime >= 0 THEN
    '5分鐘'
  WHEN 10>totalTime >= 5 THEN
    '10分鐘'
    WHEN 20>totalTime >= 10 THEN
    '20分鐘'
    WHEN 30>totalTime >= 20 THEN
    '30分鐘'
    WHEN 40>totalTime >= 30 THEN
    '40分鐘'
    WHEN 50>totalTime >= 40 THEN
    '50分鐘'
    WHEN 60>totalTime >= 50 THEN
    '60分鐘'  
  END;
 
總結:mysql性能畢竟還是不夠高,使用連接的時候並沒有使用hash連接,就像有一次我寫了一條很多left join的sql,結果使用mysql導出要半個鐘左右,後改改用分佈式的SQL查詢引擎(Presto),導出僅需十幾秒,類似這些工具也可以多采用。
 
 
更多待續~~~~~~~
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章