面試(涉及技術一)


一、Linux&Shell

1.1 Linux常用高級命令

  1)top:查看內存

  2)df -h:查看磁盤存儲情況

  3)iotop:查看磁盤IO讀寫情況(sudo yum install iotop)

  4)iotop -o:查看較高的磁盤IO讀寫程序

  5)netstat -nlpt | grep 端口號:查看端口占用情況

  6)uptime:查看報告系統運行時長及平均負載

  7)pa -aux:查看進程

1.2 Shell常用工具及寫過的腳本

1.2.1 常用工具

  1)awk

  2)sed

  3)cut

  4)sort

1.2.2 用shell寫過哪些腳本

  1)集群啟動,分發腳本

  2)數倉與Mysql的導入導出

  3)數倉層級內部的導入

1.2.3 Shell中提交了一個腳本,進程號已經不知道了,但是需要kill掉這個進程,怎么操作?

ssh $i "ps -ef | grep file-flume-kafka | grep -v grep | awk '{print \$2}' | xargs kill"

1.2.4 Shell中單引號和雙引號的區別

  1)單引號不取變量值

  2)雙引號取變量值

  3)反引號`,執行引號中的命令

  4)雙引號內部嵌套單引號,取出變量值

  5)單引號內部嵌套雙引號,不取出變量值

二、Hadoop

2.1 Hadoop常用端口號

  1)訪問HDFS:9870

  2)查看MR執行情況:8088

  3)客戶端訪問集群端口:8020

  4)歷史服務器:19888

2.2 Hadoop配置文件以及簡單的Hadoop集群搭建

2.2.1 配置文件

  1)core-site.xml

  2)hdfs-site.xml

  3)mapred-site.xml

  4)yarn-site.xml

  5)workers

2.2.2 簡單的集群搭建過程

  1)JDK的安裝

  2)配置SSH免密登錄

  3)配置hadoop核心文件

  4)格式化namenode

2.2.3 HDFS讀寫流程

2.2.3.1 讀流程

  1)首先HDFS客戶端創建Distributed FileSystem

  2)然后想NameNode請求下載文件

  3)NameNode接收到請求后給客戶端返回目標文件的元數據

  4)客戶端接收到元數據后創建輸入流FSDataInputStream

  5)根據獲取到的元數據信息開始向DataNode發送讀請求

  6)DataNode收到讀請求后開始傳輸數據至客戶端

  7)傳輸完成后客戶端關閉輸入流FSDataInputStream

2.2.3.2 寫流程

  1)首先客戶端創建Distributed FileSystem

  2)然后客戶端想NameNode請求上傳文件

  3)NameNode收到寫請求后向客戶端響應可以上傳文件

  4)客戶端收到可以上傳文件的響應后開始向NameNode請求上傳相應的塊文件,並獲取DataNode信息

  5)NameNode收到請求后返回DataNode節點信息給客戶端,表示這些DataNode節點可以存儲數據

  6)客戶端得到DataNode節點信息后開始創建輸出流FSDataOutputStream

  7)客戶端根據獲取到的DataNode節點信息找到相應的DataNode,並向DataNode請求建立Block傳輸通道

  8)DataNode接收到請求后建立通道,並響應給客戶端

  9)客戶端收到DataNode響應后開始傳輸數據

  10)傳輸完成后客戶端向NameNode發送傳輸已完成的信息

  11)客戶端關閉輸出流FSDataOutputStream

2.2.4 HDFS小文件處理

2.2.4.1 有什么影響?

  一個文件塊的元數據信息占用NameNode150字節的內存,如果有1億個小文件,則需要占用的元數據內存容量 = 1億*150字節

  若一台主句內存是128G,則能存儲的文件塊數量 = 128 * 1024*1024*1024byte/150字節 = 9億文件塊

2.2.4.2 怎么解決?

  1)采用har歸檔,將小文件歸檔

  2)采用CombineTextInputFormat,合並小文件

  3)開啟JVM重用(會一直占用使用到的task卡槽,直到任務完成后才釋放)

2.2.5 Shuffle及優化

2.2.5.1 Shuffle過程

  1)客戶端submit()之前,獲取待處理的數據信息,然后根據獲取到的信息以及相關的參數屬性配置,得到任務規划信息

  2)客戶端計算出任務規划信息后開始submit(),提交相關資源至Yarn上

  3)NodeManager中的AppMaster根據客戶端提交的信息開始計算MapTask的數據

  4)MapTask開始執行,創建輸入流TextInputFormat讀取客戶端上的文本數據

  5)MapTask讀取完成后數據開始進入map()方法

  6)再通過outputCollector數據收集器將數據輸送到環形緩沖區

  7)環形緩沖區會先對數據進行分區,然后排序,環形緩沖區默認100M內存,數據量達到80%后反向

  8)在分區、排序的過程中,內存不足時數據會溢寫至磁盤,完成分區、排序后將內存中的數據以及溢寫產生的數據文件進行歸並排序

  9)歸並排序完成后對文件進行Combin合並(此時數據在各個分區中且分區內有序)

  10)MapTask任務結束后,ReduceTask開始將各個MapTask產生的數據文件下載到內存中,若內存不足則下載到磁盤

  11)然后將各個MapTask結束后產生的數據文件進行合並,然后進行歸並排序、分組

  12)完成之后數據進入Reduce()方法

  13)最后通過TextOutputFormat將數據輸出

2.2.5.2 優化

  1)Map階段

    (1)增大環形緩沖區大小,由100M擴大到200M

    (2)增大環形緩沖區溢寫的比例,有80%擴大到90%

    (3)減少對溢寫文件的歸並排序次數

    (4)不影響實際業務的前提下,采用Combine提前合並,減少I/O傳輸帶來的網絡資源消耗

  2)Reduce階段

    (1)合理設置Map和Reduce數:太少,會導致Task等待,延長處理時間;太多,會導致 Map、Reduce任務間競爭資源,造成處理超時等錯誤

    (2)設置Map、Reduce共存:調整slowstart.completedmaps參數,使Map運行到一定程度后,Reduce也開始運行,減少Reduce的等待時間

    (3)能不使用Reduce就不使用:因為Reduce在用於連接數據集的時候將會產生大量的網絡消耗

    (4)增加每個Reduce去Map中拿數據的並行度

    (5)集群性能可以的情況下,增加Reduce端存儲數據內存的大小

  3)IO傳輸

    (1)Map輸入端采用lzo壓縮

    (2)Map輸出端采用snappy或lzo壓縮

    (3)reduce輸出端看具體需求,若作為下一個MR的數據就需要考慮切片,若永久保存考慮壓縮率比較大的gzip壓縮

  4)整體優化

    (1)NodeManager默認內存8G,需要根據服務器實際配置靈活調整,例如128G內存,配置為100G左右,yarn.nodemanager.resource.memory-mb

    (2)單任務默認內存8G,需要根據該任務的數據量靈活調整,例如128m數據,配置1G內存,yarn-scheduler.maximum-allocation-mb

    (3)控制分配給MapTask的內存上限,默認內存大小為1G,若數據量是128m,正常不需要調整內存,若數據量大於128m,可以增加至4~5G

    (4)控制分配給ReduceTask的內存上限,默認內存大小為1G,若數據量是128m,正常不需要調整內存,若數據量大於128m,可以增加ReduceTask內存大小為4~5G

    (5)控制MapTask堆內存大小

    (6)控制ReduceTask堆內存大小

    (7)增加MapTask和ReduceTask的CPU核數

    (8)增加每個Container的CPU核數和內存大小

    (9)在hdfs-site.xml中配置多磁盤

    (10)設置NameNode的工作線程池數量,若集群規模為8台,此參數設置為41,該值可以通過python命令計算出來

2.2.6 Yarn工作機制

  1)YarnRunner向ResourceManager申請獲取一個Application資源提交路徑以及一個Application_id

  2)ResourceManager向YarnRunner返回Application資源提交路徑以及一個Application_id

  3)YarnRunner根據從ResourceManager中獲取到的Application資源提交路徑以及Application_id,將Job運行所需資源提交至HDFS

  4)YarnRunner提交完資源后向ResourceManager申請運行MRAppMaster

  5)ResourceManager收到請求后將該請求初始化為一個Task任務,並將該Task任務放入Capacity調度隊列中

  6)ResourceManager向NodeManager派發Task任務,NodeManager獲取到Task任務后創建Contrainer容器

  7)MRAppMaster運行成功后,開始從HDFS上下載Job運行相關資源至本地

  8)MRAppMaster向ResourceManager申請運行MapTask容器(任務存放在Container容器中)

  9)ResourceManager接收到請求后選擇一個空閑的NodeManager節點,在該節點上創建Container容器以運行MapTask

  10)容器創建完成后MRAppMaster向該容器發送程序啟動運行腳本,MapTask開始執行

  11)當MapTask運行結束后MRAppMaster向ResourceManager申請運行ReduceTask程序

  12)ReduceTask程序運行后開始向MapTask獲取分區數據

  13)ReduceTask運行結束后,MRAppMaster向ResourceManager請求注銷自己

2.2.7 Yarn調度器

2.2.7.1 三類Hadoop調度器

  1)FIFO(先進先出調度器)

  2)Capacity Scheduler(Apache默認容量調度器)

  3)Fair Scheduler(CDH默認公平調度器)

2.2.7.2 調度器區別

  1)FIFO調度器:支持單隊列、先進先出

  2)容量調度器:支持多隊列、保證先進去的任務優先執行

  3)公平調度器:支持多隊列、保證每個任務公平享有隊列資源

2.2.7.3 在生產環境下如何選擇調度器?

  大廠一般服務器性能很強並且對並發度要求比較高,所以選擇公平調度器

  中小公司集群服務器性能資源等不太充裕,所以選擇容量調度器

2.2.7.4 在生產環境下如何創建隊列?

  1)調度器默認就一個default隊列,不能滿足生產要求

  2)按照框架分類,將每個框架的任務放入指定隊列(hive、spark、flink)

  3)按照業務模塊分類,將任務放入指定隊列(登錄、注冊、購物、收藏、下單、支付)

2.2.7.5 創建多隊列的好處?

  1)擔心員工不小心,寫遞歸死循環代碼,把所有資源全部耗盡

  2)實現任務的降級使用,特殊時期保證重要的任務隊列資源充足

2.2.8 項目經驗之基准測試

  搭建完Hadoop集群后需要對HDFS讀寫性能和MR計算能力進行測試,測試的Jar包在Hadoop的share文件下

2.2.9 Hadoop宕機

  1)若MR造成的系統宕機,此時要控制Yarn同時運行的任務數和每個任務申請的最大內存。調整參數yarn.scheduler.maximum-allocation-mb(單個任務可申請的最多物理內存量,默認8192M)

  2)若寫入文件過快造成NameNode宕機,此時要調高Kafka的存儲大小,控制從Kafka到HDFS的寫入速度。調整batchsize參數,控制Flume每批次拉取數據量的大小

2.2.10 Hadoop解決數據傾斜

  1)提前在Map階段將小文件進行combiner合並,減少網絡IO傳輸(若導致數據傾斜的key大量分布在不同的map中時,此方式不是很有效)

  2)導致數據傾斜的key大量分布在不同的mapper

    (1)局部聚合+全局聚合:先添加隨機前綴進行局部聚合,再去掉隨機前綴進行全局聚合

    (2)增加Reduce數,提升並行度

    (3)自定義分區,將key均勻分配至不同的Reduce中

2.2.11 集群資源分配參數(項目中遇到的問題)

  集群有30台機器,跑MR任務的時候發現5個map任務全都分配到了同一台機器上,這個可能是由於什么原因導致的嗎?

  解決方案:yarn.scheduler.fair.assignmultiple 這個參數,默認是關閉的,將該參數設置為true,以控制一個nodemanager里Container的數量

<property> 
    <name>yarn.scheduler.fair.assignmultiple</name> 
    <value>true</value> 
    <discription>是否允許NodeManager一次分配多個容器</discription> 
</property> 
<property> 
    <name>yarn.scheduler.fair.max.assign</name> 
    <value>20</value> 
    <discription>如果允許一次分配多個,一次最多可分配多少個,這里按照一個最小分配yarn.scheduler.minimum-allocation-mb4gb來計算總共內存120/4=30給20即可</discription> 
</property>

三、Zookeeper

3.1 選舉機制

  半數機制:2n + 1,安裝奇數台

    1)10台服務器:3台

    2)20台服務器:5台

    3)100台服務器:11台

3.2 常用命令

  1)ls

  2)get

  3)create

3.3 Paxos算法

  Paxos算法一種基於消息傳遞且具有高度容錯特性的一致性算法。

  分布式系統中的節點通信存在兩種模型:共享內存(Shared memory)和消息傳遞(Messages passing)。

  基於消息傳遞通信模型的分布式系統,不可避免的會發生以下錯誤:進程可能會慢、被殺死或者重啟,消息可能會延遲、丟失、重復,在基礎Paxos場景中,先不考慮可能出現消息篡改即拜占庭錯誤的情況。Paxos算法解決的問題是在一個可能發生上述異常的分布式系統中如何就某個值達成一致,保證不論發生以上任何異常,都不會破壞決議的一致性。

3.4 CAP法則

  1)強一致性

  2)高可用性

  3)分區容錯性

四、Sqoop

4.1 sqoop參數

/opt/module/sqoop/bin/sqoop import \
--connect \
--username \
--password \
--target-dir \
--delete-target-dir \
--num-mappers \
--fields-terminated-by   \
--query   "$2" ' and $CONDITIONS;'

4.2 sqoop導入導出Null存儲一致性問題

  Hive中的Null在底層是以“\N”來存儲,而MySQL中的Null在底層就是Null。

  為了保證數據兩端的一致性,在導出數據時采用:

    --input-null-string

    --input-null-non-string

  兩個參數,導入數據時采用:

    --null-string

    --null-non-string

4.3 sqoop數據導出一致性問題

4.3.1 場景

  Sqoop在導出數據至Mysql時,使用4個Map任務,在此過程中有2個任務失敗,但另外2個任務已經成功將數據導入MySQL,而此時老板正好看到了這個殘缺的報表數據。開發工程師發現任務失敗后,會調試問題並最終將全部數據正確的導入MySQL,而后面老板再次看報表數據,發現本次看到的數據與之前的不一致,這種情況是生產環境中不允許發生的

4.3.2 解決方案

  --staging table:指定暫存表來作為導出數據的輔助表,暫存表中的數據最終在單個事務中移動到目標表

sqoop export 
--connect jdbc:mysql://192.168.137.10:3306/user_behavior 
--username root 
--password 123456 
--table app_cource_study_report 
--columns watch_video_cnt,complete_video_cnt,dt 
--fields-terminated-by "\t" 
--export-dir "/user/hive/warehouse/tmp.db/app_cource_study_analysis_${day}" -
-staging-table app_cource_study_report_tmp
--clear-staging-table --input-null-string '\N'

4.4 sqoop底層運行的任務是什么?

  只有MapTask,沒有ReduceTask的任務,默認4個有MapTask任務

4.5 sqoop一天導入多少數據?

  一天60萬日活,產生5多萬訂單,平均每人5個訂單,每天500多M左右的業務數據,所以Sqoop需要將500M左右的數據導入數倉

4.6 sqoop數據導出的時候一次執行多長時間?

  凌晨一點開始執行,一般執行時間在半個小時左右,取決於數據量的大小(11:11,6:18等活動執行時間在1個小時左右)

4.7 sqoop導入數據時發生數據傾斜時解決方案

  1)split-by:按照某一列來切分表的工作單元

  2)num-mappers:啟動N個map來並行導入數據,默認4個

4.8 sqoop數據導出Parquet(項目中遇到的問題)

  Ads層數據往MySql中導入數據時,若用了orc(Parquet),需轉化成text格式

  1)創建臨時表,把Parquet中表數據導入到臨時表,把臨時表導出到目標表用於可視化

  2)ads層建表的時候就不要建Parquet表

五、Hive

5.1 Hive的架構

5.2 Hive和數據庫的比較

  1)數據庫存儲位置:Hive存儲在HDFS中,數據庫存儲在本地文件系統

  2)數據更新:Hive中不建議對數據進行改寫

  3)執行延遲:Hive執行延遲較高

  4)數據規模:Hive能支持大規模的數據計算

5.3 內部表和外部表

5.3.1 兩種表在刪除數據時的區別

  內部表:元數據和原始數據全部刪除

  外部表:只刪除元數據

5.3.2 在生產環境下,什么時候創建內部表,什么時候創建外部表?

  絕大部分都是使用外部表,只有臨時表才會創建內部表

5.4 四個By的區別

  1)Order By:全局排序,只有一個Reduce

  2)Sort By:分區內排序

  3)Distrbute By:類似MR中的Partition分區,結合Sort By使用,分區完后再對分區中的數據排序

  4)Cluster By:當Distrbute By 和 Sort By 字段相同時,可以使用Cluster By,但排序規則只有升序排序

5.5 系統函數

  1)date_add:加日期

  2)date_sub:減日期

  3)date_format:格式化日期

  4)last_day:本月最后一天

  5)collect_set:列轉行,聚合

  6)get_json_object:解析Json數據

  7)NVL(表達式1,表達式2):第一個表達式的值不為空,則返回第一個表達式的值;第一個表達式的值為空,則返回第二個表達式的值

5.6 自定義UDF、UDTF函數

5.6.1 在項目中是否定義過UDF、UDTF函數,用它們處理了什么問題,定義的步驟是什么?

  1)用UDF解析公共字段,用UDTF解析事件字段

  2)自定義UDF:繼承UDF,重寫evaluate方法

  3)自定義UDTF:繼承GenericUDTF,重寫initialize()、process()、close()

5.6.2 為什么要自定義UDF或UDTF?

  1)可以自己埋點Log打印日志,出現異常時方便調試

  2)可以增加第三方依賴,比如:地圖、ip解析、json嵌套

5.6.3 創建函數步驟

  1)打包

  2)上傳至HDFS

  3)在hive客戶端創建函數

5.7 窗口函數

5.7.1 排序函數

  1)rank() 排序相同時會重復,但總數不變

  2)dense_rank() 排序相同時會重復,總數會減少

  3)row_number() 根據順序排序

5.7.2 over()

  1)current row:當前行

  2)n preceding:往前n行數據

  3)n following:往后n行數據

  4)unbounded:起始點,unbounded preceding 表示起始點,unbounded following表示終點

  5)lag(col,n):往前第n行數據

  6)lead(col,n):往后第n行數據

  7)ntile(n):把有序分區中的行分發到指定數據的組中,各個組有編號,編號從1開始,對於每一行,NTILE返回此行所屬的組的編號。注意:n必須為int類型

5.7.3 手寫SQL

5.7.3.1 寫出SQL

  1)表名:macro_index_data

  2)字段名:數據期(年月)(occur_period)、地區代碼(area_code)、指標代碼(index_code)、指標類型(增速、總量)(index_type)、指標值(index_value)、數據更新時間(update_time)。說明:羅湖區的區划代碼為440305000000GDP指標代碼為gmjj_jjzl_01、指標類型的枚舉值分別是增速(TB)、總量(JDZ)

  3)請寫出,20204個季度中GDP的增速都超過羅湖區同期的區有哪些

  4)SQL如下

--最后比較指標,若都大於即為所求的區
select t2.area_code
from (
    --先求出羅湖區2020年四季度的GDP指標
    select area_code,
           sum(case when month('occur_period') between 1 and 3 then index_value else 0 end) `one`,   --一季度GDP指標
           sum(case when month('occur_period') between 4 and 6 then index_value else 0 end) `two`,   --二季度GDP指標
           sum(case when month('occur_period') between 7 and 9 then index_value else 0 end) `three`, --三季度GDP指標
           sum(case when month('occur_period') between 10 and 12 then index_value else 0 end) `four` --四季度GDP指標
    from macro_index_data
    where area_code = '440305000000' --羅湖區
    and index_code = 'gmjj_jjzl_01'  --GDP指標
    and index_type = 'TB'            --增速
    and year('occur_period') = 2020    --2020年
    group by area_code
) t1
join (
    --再求出其它區2020年四季度的GDP指標
    select area_code,
           sum(case when month('occur_period') between 1 and 3 then index_value else 0 end) `one`,   --一季度GDP指標
           sum(case when month('occur_period') between 4 and 6 then index_value else 0 end) `two`,   --二季度GDP指標
           sum(case when month('occur_period') between 7 and 9 then index_value else 0 end) `three`, --三季度GDP指標
           sum(case when month('occur_period') between 10 and 12 then index_value else 0 end) `four` --四季度GDP指標
    from macro_index_data
    where area_code <> '440305000000' --其它區
    and index_code = 'gmjj_jjzl_01'  --GDP指標
    and index_type = 'TB'            --增速
    and year('occur_period') = 2020    --2020年
    group by area_code
) t2 on t2.one > t1.one 
and t2.two > t1.two 
and t2.three > t1.three 
and t2.four > t1.four;

5.7.3.2 寫出SQL

   1)表1:t_syrkxxb (實有人口信息表),字段名:姓名(xm)、證件號碼(zjhm)、證件類型(zjlx)、出生日期(csrq)、居住地址(jzdz)、所在街道(jdmc)、所在社區(sqmc)、聯系電話(lxdh)、更新時間(gxsj)

  2)表2:t_hsjcqkb (核酸檢測情況表),字段名:姓名(xm)、證件號碼(zjhm)、證件類型(zjlx)、檢測機構(jcjg)、檢測時間(jcsj)、報告時間(bgsj)、檢測結果(jcjg)

  3)說明:實有人口信息表中,因網格統計的時候一人有多處房產或者在多地有居住過的,會有多條數據,僅取最新一條記錄;核酸檢測情況表中,同一人在同一天內不同檢測機構檢測多次的算多次檢測,同一人在同一天內同一檢測機構檢測多次的只算最后一次

  4)請寫出各街道已參與核酸檢測總人數、今日新增人數、已檢測人數占總人口數的比例;

select t6.jdmc `所在街道`,
       t8.jiance_person_num `已參與核酸檢測總人數`,
       t7.add_num `今日新增人數`,
       t6.person_num `已檢測人數占總人口數的比例`
from (
    --各街道總人口數
    select jdmc,count(*) `person_num`
    from t_syrkxxb
    group by jdmc
) t6
join (
    --各街道今日新增人數:以前沒有檢測過的用戶
    select t4.jdmc,count(*) `add_num`
    from (
        select zjhm,zjlx,jdmc
        from t_hsjcqkb
        where date_format('jcsj','yyyy-MM-dd') = '2021-07-29'
    ) t4
    left join (
        --求出檢測過的用戶
        select zjhm,zjlx
        from t_hsjcqkb
        group by zjhm,zjlx
    ) t5 on t4.zjhm = t5.zjhm 
    and t4.zjlx = t5.zjlx
    where t5.zjhm is null
    group by jdmc    --按照所在街道分組
) t7 on t6.jdmc = t7.jdmc
join (
    --求出各街道已參與核酸檢測總人數
    select t1.jdmc,count(*) `jiance_person_num`
    from (
        --先將實有人口表按更新時間排序后過濾出最新的記錄
        select t1.*
        from (
            select *,row_number() over(partition by zjhm order by gxsj desc) `rank_gxsj`
            from t_syrkxxb
            group by zjhm    --以證件號碼分組
        ) t1
        where rank_gxsj = '1'
    ) t2
    join (
        --求出檢測過的用戶
        select zjhm,zjlx
        from t_hsjcqkb
        group by zjhm,zjlx
    ) t3 on t2.zjhm = t3.zjhm and t2.zjlx = t3.zjlx
    group by t1.jdmc --以街道分組
) t8 t6.jdmc = t8.jdmc;

  5)已參與2次以上核酸檢測人數、2次以上核酸今日新增數、已檢測2次以上人數占總人口數的比例

  6)已參與3次以上核酸檢測人數、3次以上核酸今日新增數、已檢測3次以上人數占總人口數的比例

5.7.3.3 寫出SQL語句

  1)表名:t_patent_detail (專利明細表)

  2)表字段:專利號(patent_id)、專利名稱(patent_name)、專利類型(patent_type)、申請時間(aplly_date)、授權時間(authorize_date)、申請人(apply_users)

  3)說明:同一個專利,可以有1到多個申請人,多人之間按分號隔開。本表記錄數約1萬條。例如:

  4)請寫出hive查詢語句,各類型專利top 10申請人,以及對應的專利申請數

--各類型專利top 10申請人,以及對應的專利申請數
select t1.apply_name `申請人`,count(*) `專利申請數`,rank() over(order by count(*) desc) `專利數排名`
from (
    --先將申請人字段炸裂
    select d.*, t1.coll `apply_name`
    from t_patent_detail d
    lateral view explode(split(apply_users,';')) t1 as coll
) t1
group by t1.apply_name;    --按照申請人分組

5.7.3.4 寫出SQL

  1)核額流水表hee,字段名:ds(日期分區,20200101,每個分區有全量流水)、sno(每個ds內主鍵,流水號)、uid(用戶id)、is_nsk_apply(是否核額申請,取0或1)、is_pass_rule(是否通過規則,取0或1)、is_obtain_qutoa(是否授信成功,取值0或1)、quota(授信金額)、update_time(更新時間,2020-11-14 08:12:12)

  2)借據表jieju,字段名:ds(日期分區,20200101,每個分區有全量借據號)、duebill_idid(借據號,每個日期分區內的主鍵)、uid(用戶id)、prod_type(產品名稱,XX貸、YY貸、ZZ貸)、putout_date(發放日期,2020-10-10 00:10:30)、putout_amt(發放金額)、balance(借據余額)、is_buding(狀態是否不良,取0或1)、owerduedays(逾期天數)

  3)輸出模型表moxin,字段名:ds(日期分區,20200101,增量表,部分流水記錄有可能更新)、sno(流水號,主鍵)、create_time(創建日期,2020-10-10 00:30:12)、uid(用戶id)、content(Json格式key值名稱為V01-V06,value值取值為0和1)、update_time(更新日期,2020-10-10 00:30:12)

  4)基於核額流水表和借據表統計如下指標的當日新增、昨日新增、歷史累計

    (1)申請戶數

select t1.count(*) `當日新增用戶數`
from (
    select uid
    from hee
    where ds = '20210729'    --當日新增
    and is_nsk_apply = 1    --已申請核額
    group by uid
) t1

select t1.count(*) `昨日新增用戶數`
from (
    select uid
    from hee
    where ds = '20210728'    --昨日新增
    and is_nsk_apply = 1    --已申請核額
    group by uid
) t1

select t1.count(*) `歷史累計`
from (
    select uid
    from hee
    where ds <= '20210729'    --歷史累計
    and is_nsk_apply = 1    --已申請核額
    group by uid
) t1

    (2)規則通過戶數

select t1.count(*) `當日新增規則通過戶數`
from (
    select uid
    from hee
    where ds = '20210729'    --當日新增
    and is_nsk_apply = 1    --已申請核額
    and is_pass_rule = 1    --已通過規則
    group by uid
) t1

select t1.count(*) `昨日新增規則通過戶數`
from (
    select uid
    from hee
    where ds = '20210728'    --昨日新增
    and is_nsk_apply = 1    --已申請核額
    and is_pass_rule = 1    --已通過規則
    group by uid
) t1

select t1.count(*) `歷史累計規則通過戶數`
from (
    select uid
    from hee
    where ds <= '20210729'    --歷史累計
    and is_nsk_apply = 1    --已申請核額
    and is_pass_rule = 1    --已通過規則
    group by uid
) t1

    (3)核額成功戶數

select t1.count(*) `當日新增核額成功戶數`
from (
    select uid
    from hee
    where ds = '20210729'    --當日新增
    and is_nsk_apply = 1    --已申請核額
    and is_pass_rule = 1    --已通過規則
    and is_obtain_qutoa = 1 --已授信成功
    group by uid
) t1

select t1.count(*) `昨日新增核額成功戶數`
from (
    select uid
    from hee
    where ds = '20210728'    --昨日新增
    and is_nsk_apply = 1    --已申請核額
    and is_pass_rule = 1    --已通過規則
    and is_obtain_qutoa = 1 --已授信成功
    group by uid
) t1

select t1.count(*) `歷史累計`
from (
    select uid
    from hee
    where ds <= '20210729'    --歷史累計
    and is_nsk_apply = 1    --已申請核額
    and is_pass_rule = 1    --已通過規則
    and is_obtain_qutoa = 1 --已授信成功
    group by uid
) t1

    (4)授信金額

select sum(quota)from hee
where ds = '20210729'    ----當日新增
and is_nsk_apply = 1    --已申請核額
and is_pass_rule = 1    --已通過規則
and is_obtain_qutoa = 1 --已授信成功

select sum(quota)from hee
where ds = '20210728'    ----當日新增
and is_nsk_apply = 1    --已申請核額
and is_pass_rule = 1    --已通過規則
and is_obtain_qutoa = 1 --已授信成功

select sum(quota)from hee
where ds <= '20210729'    ----歷史累計
and is_nsk_apply = 1    --已申請核額
and is_pass_rule = 1    --已通過規則
and is_obtain_qutoa = 1 --已授信成功

    (5)平均核額

select avg(quota)from hee
where ds = '20210729'    --當日新增
and is_nsk_apply = 1    --已申請核額
and is_pass_rule = 1    --已通過規則
and is_obtain_qutoa = 1 --已授信成功

select avg(quota)from hee
where ds = '20210728'    --昨日新增
and is_nsk_apply = 1    --已申請核額
and is_pass_rule = 1    --已通過規則
and is_obtain_qutoa = 1 --已授信成功

select avg(quota)from hee
where ds < '20210729'    --歷史累計
and is_nsk_apply = 1    --已申請核額
and is_pass_rule = 1    --已通過規則
and is_obtain_qutoa = 1 --已授信成功

    (6)發放金額

select sum(putout_amt)from jieju
where ds = '20210729'    --當日新增

select sum(putout_amt)from jieju
where ds = '20210728'    --昨日新增

select sum(putout_amt)from jieju
where ds <= '20210728'    --歷史累計

    (7)戶均發放金額

select avg(putout_amt) `當日戶均發放金額`
from jieju
where ds = '20210729'

select avg(putout_amt) `昨日戶均發放金額`
from jieju
where ds = '20210728'

select avg(putout_amt) `歷史累計戶均發放金額`
from jieju
where ds <= '20210729'

  5)基於借據表,統計SQL

    (1)XX貸(在貸客戶數、在貸余額、不良余額、余額不良率、不良客戶數、客戶不良率)

select count(*) `在貸客戶數`
from (
    select uid
    from jieju
    where ds = '20210729' 
    and prod_type = 'XX貸'
    group by uid
) t1

select sum(balance) `在貸余額`
from jieju
where ds = '20210729' 
and prod_type = 'XX貸'

select sum(balance) `不良余額`
from jieju
where ds = '20210729' 
and prod_type = 'XX貸'
and is_buding = 1

余額不良率=不良余額/在貸余額

select count(*) `不良客戶數`
from (
    select uid
    from jieju
    where ds = '20210729' 
    and prod_type = 'XX貸'
    and is_buding = 1
    group by uid
) t1

客戶不良率=不良客戶數/在貸客戶數

    (2)YY貸(在貸客戶數、在貸余額、不良余額、余額不良率、不良客戶數、客戶不良率)

select count(*) `在貸客戶數`
from (
    select uid
    from jieju
    where ds = '20210729' 
    and prod_type = 'YY貸'
    group by uid
) t1

select sum(balance) over(partition by ds) `在貸余額`
from jieju
where ds = '20210729' 
and prod_type = 'YY貸'

select sum(balance) over(partition by ds) `不良余額`
from jieju
where ds = '20210729' 
and prod_type = 'YY貸'
and is_buding = 1

余額不良率=不良余額/在貸余額

select count(*) `不良客戶數`
from (
    select uid
    from jieju
    where ds = '20210729' 
    and prod_type = 'YY貸'
    and is_buding = 1
    group by uid
) t1

客戶不良率=不良客戶數/在貸客戶數

    (3)ZZ貸(在貸客戶數、在貸余額、不良余額、余額不良率、不良客戶數、客戶不良率)

select count(*) `在貸客戶數`
from (
    select uid
    from jieju
    where ds = '20210729' 
    and prod_type = 'ZZ貸'
    group by uid
) t1

select sum(balance) over(partition by ds) `在貸余額`
from jieju
where ds = '20210729' 
and prod_type = 'ZZ貸'

select sum(balance) over(partition by ds) `不良余額`
from jieju
where ds = '20210729' 
and prod_type = 'ZZ貸'
and is_buding = 1

余額不良率=不良余額/在貸余額

select count(*) `不良客戶數`
from (
    select uid
    from jieju
    where ds = '20210729' 
    and prod_type = 'ZZ貸'
    and is_buding = 1
    group by uid
) t1

客戶不良率=不良客戶數/在貸客戶數

  6)基於借據表,統計SQL

    (1)逾期1~30天(戶數、余額、逾期率)

select count(distinct uid) `戶數`,sum(balance) `余額`,round((sum(10to30) / count(*)),2) `逾期率`
from (
    select uid,
           balance,
           case when owerduedays between 1 and 30 then 1 else 0    `10to30`
    from jieju
) t1

    (2)逾期30~90天(戶數、余額、逾期率)

select count(distinct uid) `戶數`,sum(balance) `余額`,round((sum(30to90) / count(*)),2) `逾期率`
from (
    select uid,
           balance,
           case when owerduedays between 30 and 90 then 1 else 0    `30to90`
    from jieju
) t1

    (3)逾期90天以上(戶數、余額、逾期率)

select count(distinct uid) `戶數`,sum(balance) `余額`,round((sum(90day) / count(*)),2) `逾期率`
from (
    select uid,
           balance,
           case when owerduedays > 90 then 1 else 0    `90day`
    from jieju
) t1

    (4)逾期合計(戶數、余額、逾期率)

select count(distinct uid) `戶數`,sum(balance) `余額`,round((sum(1day) / count(*)),2) `逾期率`
from (
    select uid,
           balance,
           case when owerduedays > 1 then 1 else 0 `1day`
    from jieju
) t1

    (5)不良合計(戶數、余額、逾期率)

select count(distinct uid) `戶數`,sum(balance) `余額`,round((sum(1day) / count(*)),2) `逾期率`
from (
    select uid,
           balance,
           case when owerduedays > 1 then 1 else 0    `1day`
    from jieju
    where is_buding = 1
) t1

  7)基於模型輸出表,統計SQL(value值為1即為命中)

    (1)統計日期20201010,統計指標V01(命中戶數、命中率)

select count(*) `user_num`,sum(mingzhon) `mingzhon_num`,round(mingzhon_num / user_num,2) `命中率`
from (
    select uid,(case when get_json_object('content','$.V01') == 1 then 1 else 0 end) `mingzhon`
    from moxin
    where ds = '20201010'
) t1

    (2)統計日期20201010,統計指標V02(命中戶數、命中率)

select count(*) `user_num`,sum(mingzhon) `mingzhon_num`,round(mingzhon_num / user_num,2) `命中率`
from (
    select uid,(case when get_json_object('content','$.V02') == 1 then 1 else 0 end) `mingzhon`
    from moxin
    where ds = '20201010'
) t1

    (3)類推即可。。。

  8)基於借據表統計指標,請提供Vintage統計SQL(mobX指的是發放后第X月末的不良余額/發放月金額)

    (1)發放月份2019-10(發放金額、MOB1、MOB2、MOB3、MOB4、MOB5、MOB6、MOB7、MOB8、MOB9、MOB10、MOB11、MOB12)

--MOB1,一個月后再來查詢
select sum(bad_monery) `不良余額`,sum(putout_amt) `發放月金額`
from (
    select (case when is_buding = 1 then balance else 0 end) `bad_monery`,putout_amt
    from jieju
    where date_format('ds','yyyy-MM') = '2019-10'    --數據都按天存放,過濾出當月的即可
    and date_format('putout_date','yyyy-MM') = '2019-10'
) t1

--MOB2,兩個月后再來查詢
select sum(bad_monery) `不良余額`,sum(putout_amt) `發放月金額`
from (
    select (case when is_buding = 1 then balance else 0 end) `bad_monery`,putout_amt
    from jieju
    where date_format('ds','yyyy-MM') = '2019-10'    --數據都按天存放,過濾出當月的即可
    and date_format('putout_date','yyyy-MM') = '2019-10'
) t1

    (2)發放月份2019-11(發放金額、MOB1、MOB2、MOB3、MOB4、MOB5、MOB6、MOB7、MOB8、MOB9、MOB10、MOB11、MOB12)

--MOB1,一個月后再來查詢
select sum(bad_monery) `不良余額`,sum(putout_amt) `發放月金額`
from (
    select (case when is_buding = 1 then balance else 0 end) `bad_monery`,putout_amt
    from jieju
    where date_format('ds','yyyy-MM') = '2019-11'    --數據都按天存放,過濾出當月的即可
    and date_format('putout_date','yyyy-MM') = '2019-11'
) t1

--MOB2,兩個月后再來查詢
select sum(bad_monery) `不良余額`,sum(putout_amt) `發放月金額`
from (
    select (case when is_buding = 1 then balance else 0 end) `bad_monery`,putout_amt
    from jieju
    where date_format('ds','yyyy-MM') = '2019-11'    --數據都按天存放,過濾出當月的即可
    and date_format('putout_date','yyyy-MM') = '2019-11'
) t1

5.7.3.5 請把下面的HQL語句改為其他方式實現

  1)請優化:

select a.key,a.value
from a
where a.key not in (select b.key from b)

  2)已優化:

select t1.key,t1.value
from (
    select a.key,a.value
    from a
) t1
right join (
    select b.key
    from b
) t2 on t1.key = t2 .key
where t1.key is null;

  3)請優化:

select a.key,a.value
from a
where a.key in (select b.key from b)

  4)已優化:

select t1.key,t1.value
from (
    select a.key,a.value
    from a
) t1
join (
    select b.key
    from b
) t2 on t1.key = t2 .key;

5.7.3.6 寫出SQL

  1)student表,student(id,num,name,class),字段說明:num(學號)、name(學生姓名)、class(班級)

  2)course_score表,course_score(student_num,course,score),字段說明:student_num(學號)、courese(科目)、score(分數)

  3)查詢班級為"2020"的所有學生科目"PE"的平均分

select avg(score) `班級為2020的所有學生科目"PE"的平均分`
from (
    select *
    from student
    where class = '2020'
) t1
join (
    select student_num,score
    from course_score
    where courese = 'PE'
) t2 on t1.num = t2.student_num

  4)查詢所有科目大於80分的學生的姓名

select num,name
from student s
join (
    select student_num
    from course_score
    group by student_num
    having score > 80
) t1 on s.num = t1.student_num;
select s.num,s.name
from student s
left join (
    select student_num
    from course_score
    where score <= 80
    group by student_num
) t1 on s.num = t1.student_num
where t1.student_num is null;

  5)60分以上合格,查出所有學生合格與不合格科目的總數,格式為(student_num,passed,failed)

select student_num,
       sum(case when score >= 60 then 1 else 0 end) `passed`,
       sum(case when score < 60 then 1 else 0 end) `failed`
from course_score
group by student_num

5.7.3.7  寫出SQL

  1)一張理財項目的表大致如下:begin_date代表買入一筆的時間,end_date代表將其賣出的時間,求用戶歷史最大的持倉筆數

  2)請寫出SQL

select user_id,
       max(num) max_num
from (
    select id,
           user_id,
           dt,
           sum(p) over(partition by user_id order by dt) num
    from (
        select id,
               user_id,
               begin_date dt,
               1 p
        from test
        union
        select id,
               user_id,
               end_date dt,
               -1 p
        from test
    ) t1
) t2
group by user_id;

5.7.3.8 寫出SQL

  1)訂單表A(user int,order_id int,time datetime),查詢每個用戶按訂單時間排序標號

  2)請寫出SQL

select user,
       order_id,
       time,
       row_number() over(partition by user order by time asc) `order_id`
from A

5.7.3.9 寫出SQL

  1)一張用戶交易表order,其中有userid(用戶ID)、amount(消費金額)、paytime(支付時間),查出每個用戶第一單的消費金額

  2)如下:

select userid,amount
from (
    select userid,amount,paytime,row_number() over(partition by userid order by paytime asc) `rank`
    from order
) t1
where t1.rank = 1;

5.7.3.10 寫出SQL

  1)用戶行為表tracking_log,字段有userid(用戶ID)evt(用戶行為),evt_time(用戶操作時間)

  2)計算每天的訪客數和他們的平均操作次數

select count(distinct userid) `user_num`, round(count(evt) / user_num,2) `平均操作次數`
from tracking_log
where date_format('evt_time','yyyy-MM-dd') = '2021-07-30'

  3)統計每天符合以下條件的用戶數:A操作之后是B操作,AB操作必須相鄰(兩種寫法:lag() 函數或 lead() 函數)

select count(userid) `A操作之后是B操作,AB操作必須相鄰的用戶數`
from (
    select userid,sum(case when (evt = 'B' && evtOne = 'A') then 1 else 0 end) `evt_sum`
    from (
        select userid,
               evt,        --用戶行為
               lag(evt,1) over(partition by userid order by evt_time) `evtOne`    --往前一行的用戶行為
        from tracking_log
        where date_format('evt_time','yyyy-MM-dd') = '2021-07-30'
    ) t1
    group by userid
) t2
where t2.evt_sum >=1;
select count(userid) `A操作之后是B操作,AB操作必須相鄰的用戶數`
from (
    select userid,sum(case when (evt = 'A' && evtOne = 'B') then 1 else 0 end) `evt_sum`
    from (
        select userid,
               evt,        --用戶行為
               lead(evt,1) over(partition by userid order by evt_time) `evtOne`    --往后一行的用戶行為
        from tracking_log
        where date_format('evt_time','yyyy-MM-dd') = '2021-07-30'
    ) t1
    group by userid
) t2
where t2.evt_sum >=1;

5.7.3.11 有一個表A:班級,姓名,性別,身高,求每個班級身高最高的數據(兩種寫法)

select A.*
from A
join (
    select class,max(height) `max_height`
    from A
    group by class
) t1 on A.class = t1.class
and A.height = t1.height;
select class,
       name,
       sex,
       height
from (
    select class,
           name,
           sex,
           height,
           rank() over(partition by class order by height desc) `rank`
    from A
) t1 
where t1.rank = 1;

5.7.3.12 寫出SQL

  1)user表,用戶訪問數據:

userId    visitDate    visitCount
u01    2017/1/21    5
u02    2017/1/23    6
u03    2017/1/22    8
u04    2017/1/20    3
u01    2017/1/23    6
u01    2017/2/21    8
U02    2017/1/23    6
U01    2017/2/22    4

  2)使用SQL統計出每個用戶的累積訪問次數

select userId,
       mn
       mn_count    `當前月訪問次數`,
       sum(mn_count) over(partition by userId order by mn) `累計訪問次數`
from (
    --統計每個用戶每個月的訪問次數
    select userId,
           mn,
           sum(visitCount) `mn_count`
    from (
        --先將訪問時間格式化
        select userId,
               date_format(regexp_replace(visitDate,'/','-'),'yyyy-MM') mn,
               visitCount
        from user
    ) t1
    group by userId,mn
) t2;

  3)Scores表,數據如下:

+----+-------+
| Id | Score |
+----+-------+
| 1  | 3.50  |
| 2  | 3.65  |
| 3  | 4.00  |
| 4  | 3.85  |
| 5  | 4.00  |
| 6  | 3.65  |
+----+-------+

  4)按照分數從高到低排序

select id,score,rank() over(order by score desc) `排名`
from scores

5.7.3.13 寫出SQL(此題我也不太會,答案僅供參考,非權威)

  1)停車表user_parking_record,字段 user_id(stringdate(yyyy-MM-dd),start_time(hh:mm:ss)end_time(hh:mm:ss)community_id(string)

  2)分時統計各小區用戶停車數

 

select community_id,
       date,
       hour(dt) hour_dt
       max(sum_p) max_cat
from (
    select user_id,
           date,
           dt,
           community_id,
           sum(p) over(partition by community_id order by dt) sum_p
    from (
        select user_id,
               date,
               concat(date,start_time) dt,
               community_id,
               1 p
        from user_parking_record
        union
        select user_id,
               date,
               concat(date,end_time) dt,
               community_id,
               -1 p
        from user_parking_record
    ) t1
) t2
group by community_id,date,hour_dt

 

  3)統計各小區用戶停車高峰時段Top3

  4)各用戶近兩周內最大連續停車天數

  5)若非連續停車天數不超過1天,也可做連續停車,求各小區最大連續停車天數的用戶數分布情況

5.7.3.14 寫出SQL

  1)score表結構:uid,subject_id,score

  2)找出所有科目成績都大於某一學科平均成績的學生

select uid
from (
    select uid,
           if(score>avg_score,0,1) flag
    from (
        --先求出各個學科的平均成績
        select uid,
               score,
               avg(score) over(partition by subject_id) avg_score
        from score
    )t1
)t2
group by uid
having sum(flag)=0;

5.7.3.15 寫出HQL

  1)訪問日志存儲的表名為Visit,訪的用戶iduser_id被訪問的店鋪名稱為shop

  2)求每個店鋪的UV(訪客數)

select shop,count(distinct user_id) `訪客數`
from Visit
group by shop

  3)求每個店鋪訪問次數top3的訪客信息,輸出店鋪名稱、訪客id、訪問次數

select t2.shop,
       t2.user_id,
       t2.user_count
from (
    select shop,
           user_id,
           user_count,
           rank() over(partition by shop order by user_count) `rank`
    from (
        select shop,user_id,count(user_id) `user_count`
        from Visit
        group by shop,user_id
    ) t1 
) t2
where t2.rank <= 3;

5.7.3.16 寫出HQL

  1)ORDER表字段:Date,Order_id,User_idamount(數據樣例:2017-01-01,10029028,1000003251,33.57

  2)給出 2017年每個月的訂單數、用戶數、總成交金額

select month('date'),count(order_id) `訂單數`,count(distinct user_id) `用戶數`,sum(amount) `總成交金額`
from order
where year('date') = '2017'
group by month('date')

  3)給出201711月的新客數(指在11月才有第一筆訂單)

select count(distinct user_id) `新客數`
from (
    select *
    from order
    where date_format('date','yyyy-MM') = '2017-11'
) t1
left join (
    select *
    from order
    where date_format('date','yyyy-MM') < '2017-11'
) t2 on t1.user_id = t2.user_id
where t2.user_id is null;

5.7.3.17 寫出HQL

  1)表user_age,數據集:

日期     用戶    年齡
2019-02-11,test_1,23
2019-02-11,test_2,19
2019-02-11,test_3,39
2019-02-11,test_1,23
2019-02-11,test_3,39
2019-02-11,test_1,23
2019-02-12,test_2,19
2019-02-13,test_1,23
2019-02-15,test_2,19
2019-02-16,test_2,19

  2)求所有用戶和活躍用戶的總數及平均年齡(活躍用戶指連續兩天都有訪問記錄的用戶)

select sum(user_total_count),
       sum(user_total_avg_age),
       sum(twice_count),
       sum(twice_count_avg_age)
from (
    select 0 `user_total_count`,
           0 `user_total_avg_age`,
           count(*) `ct`,
           cast(sum(age)/count(*) as decimal(10,2)) `twice_count_avg_age`
    from (
        select user_id,
               min(age) `age`
        from (
            select user_id,
                   min(age) `age`
            from (
                select user_id,
                       age,
                       date_sub(dt,rank) `flag`
                from (
                    select dt,
                       user_id,
                       min(age) `age`,
                       rank() over(partition by user_id order by dt) `rank`
                    from user_age
                    group by dt,user_id
                ) t1
            ) t2
            group by user_id,flag
            having count(*) >= 2
        ) t3 
        group by user_id
    ) t4
    union all
    select count(*) `user_count`,
           cast((sum(age)/count(*)) as decimal(10,1)),
           0 `twice_count`,
           0 `twice_count_avg_age`
    from (
        select user_id,
               min(age) `age`
        from user_age 
        group by user_id
    ) t5
) t6;

5.7.3.18 寫出HQL

  1)ordertable,字段(購買用戶:userid,金額:money,購買時間:paymenttime(格式:2017-10-01),訂單idorderid

  2)求所有用戶中在今年10月份第一次購買商品的金額

select userid,
       money,
       paymenttime,
       orderid
from (
select userid,
       money,
       paymenttime,
       orderid,
       row_number() over(partition by userid order by paymenttime desc) `rank`
from ordertable
where date_format('paymenttime','yyyy-MM') = '2017-10'
) t1
where t1.rank = 1

5.7.3.19 寫出HQL

  1)數據:

時間                    接口                 ip地址
2016-11-09 112205  /api/user/login    110.23.5.33
2016-11-09 112310  /api/user/detail   57.3.2.16
.....
2016-11-09 235940  /api/user/login    200.6.5.166

  2)119號下午14點(14-15點),訪問api/user/login接口的top10ip地址

select address_id,count(*) `ip_count`
frrom address
where date_format('date','MM-dd HH') = '11-09 14'
and url = 'api/user/login'
group by address_id
order by ip_count desc
limit 10

5.7.3.20 寫出HQL

  1)表信息

CREATE TABLE `account`
(   `dist_id` int11DEFAULT NULL COMMENT '區組id',
    `account` varchar100DEFAULT NULL COMMENT '賬號',
    `gold` int11DEFAULT 0 COMMENT '金幣');

  2)查詢各自區組的money排名前十的賬號(分組取前10

select dist_id,
       account,
       gold,
       rank
from (
select dist_id,
       account,
       gold,
       row_number() over(partition by dist_id order by gold desc) `rank`
from account
) t1
where t1.rank <= 10

5.7.3.21 寫出HQL

  1)會員表(member),字段:memberid(會員id,主鍵)credits(積分)

  2)銷售表(sale),字段:memberid(會員id,外鍵)購買金額(MNAccount

  3)退貨表(regoods),字段memberid(會員id,外鍵)退貨金額(RMNAccount

  4)業務說明

    (1)銷售表中的銷售記錄可以是會員購買,也可以是非會員購買。(即銷售表中的memberid可以為空)

    (2)銷售表中的一個會員可以有多條購買記錄

    (3)退貨表中的退貨記錄可以是會員,也可是非會員

    (4)一個會員可以有一條或多條退貨記錄

  5)分組查出銷售表中所有會員購買金額,同時分組查出退貨表中所有會員的退貨金額,把會員id相同的購買金額-退款金額得到的結果更新到表會員表中對應會員的積分字段(credits

insert into table member
(
    select t1.memberid,
           (mnaccount - rmnaccount) `credits`
    from (
        select memberid,
               sum(mnaccount) `mnaccount`
        from sale
        where memberid is not null
        group by memberid
    ) t1
    join (
        select memberid,
               sum(rmnaccount) `rmnaccount`
        from regoods
        where memberid is not null
        group by memberid
    ) t2 on t1.memberid = t2.memberid
);

5.7.3.22 寫出HQL

  1)student表信息如下:

自動編號   學號  姓名 課程編號 課程名稱 分數
1     2005001 張三   0001   數學   69
2     2005002 李四   0001   數學   89
3     2005001 張三   0001   數學   69

  2)刪除除了自動編號不同, 其他都相同的學生冗余信息

delete from student
where 自動編號 not in (
    select min(自動編號)
    from student
    group by 學號,姓名,課程編號,課程名稱,分數
)

5.7.3.23  一個叫team的表,里面只有一個字段name,一共有4條紀錄,分別是a,b,c,d,對應四個球隊,現在四個球隊進行比賽,用一條sql語句顯示所有可能的比賽組合

select a.name,
       b.name
from team a, team b
where a.name < b.name

5.7.3.24 寫出HQL

  1)怎么把這樣一個

year   month amount
1991   1     1.1
1991   2     1.2
1991   3     1.3
1991   4     1.4
1992   1     2.1
1992   2     2.2
1992   3     2.3
1992   4     2.4
查成這樣一個結果 year m1  m2  m3 m4 1991 1.1 1.2 1.3 1.4 1992 2.1 2.2 2.3 2.4

  2)如下:

select year,
       (select amount from aa m where m.month = 1 and m.year = aa.year) `m1`,
       (select amount from aa m where m.month = 2 and m.year = aa.year) `m2`,
       (select amount from aa m where m.month = 3 and m.year = aa.year) `m3`,
       (select amount from aa m where m.month = 4 and m.year = aa.year) `m4`,
from aa
group by year

5.7.3.25 復制表(只復制結構,源表名:a,新表名:b)

select * into b
from a
where 1<>1    --where 1=1 表示拷貝表結構和數據內容

5.7.3.26  寫出HQL

  1)原表:

courseid coursename score
1         java      70
2         oracle    90
3         xml       40
4         jsp       30
5         servlet   80

  2)想要的結果:

courseid coursename score  mark
1          java     70    pass
2          oracle   90    pass
3          xml      40    fail
4          jsp      30    fail
5          servlet  80    pass

  3)寫出答案:

select courseid,
       coursename,
       score,
       (case when score >= 60 then "pass" else "fail" end) `mark`
from score
select courseid,
       coursename,
       score,
       if(score >= 60,"pass","fail") `mark`
from score

5.7.3.27 寫出HQL

  1)表名:購物信息

購物人      商品名稱     數量
A            甲          2
B            乙          4
C            丙          1
A            丁          2
B            丙          5

  2)求所有購入商品為兩種或兩種以上的購物人記錄

select *
from shoop s
join (
    select user
    from shoop
    group by user
    having count(*) > 2
) t1 on s.suer = t1.user;

5.7.3.28 寫出HQL

  1)info 表

date     result
2005-05-09 win
2005-05-09 lose 
2005-05-09 lose 
2005-05-09 lose 
2005-05-10 win 
2005-05-10 lose 
2005-05-10 lose

  2)如果要生成下列結果, 該如何寫sql語句?

  date    win lose
2005-05-09  2   2 
2005-05-10  1   2

  3)答案

select date,
       sum(case when result = "win" then 1 else 0 end) `win`
       sum(case when result = "lose" then 1 else 0 end) `lose`
from info
group by date

5.7.3.29 訂單表order,字段有:order_id(訂單ID), user_id(用戶ID),amount(金額), pay_datetime(付費時間),channel_id(渠道ID),dt(分區字段)

  1)在Hive中創建這個表

create external table order(
    order_id int,
    user_id int,
    amount double,
    pay_datetime timestamp,
    channel_id int
) partitioned by (dt string)
row format delimited fields terminated by '\t';

  2)查詢dt=‘2021-08-04‘里每個渠道的訂單數,下單人數(去重),總金額

select channel_id,
       count(order_id) `訂單數`,
       count(distinct user_id) `下單人數`,
       sum(amount) `總金額`
from order
where dt = '2021-08-04'
group by channel_id

  3)查詢dt=‘2021-08-04‘里每個渠道的金額最大3筆訂單

select channel_id,
       order_id,
       amount,
       rank
from (
    select channel_id,
           order_id,
           amount,
           rank() over(partition by channel_id order by amount desc) `rank`
    from order
    where dt = '2021-08-04'
    group by channel_id,order_id,amount
) t1
where t1.rank <= 3

  4)有一天發現訂單數據重復,請分析原因

    訂單屬於業務數據,在關系型數據庫中不會存在數據重復,hive建表時也不會導致數據重復,所以我推測是在數據遷移時,失敗導致重復遷移,從而出現數據冗余的情況

5.7.3.30 寫出SQL

  1)t_order訂單表

order_id,//訂單id
item_id, //商品id
create_time,//下單時間
amount//下單金額

  2)t_item商品表

item_id,//商品id
item_name,//商品名稱
category//品類

  3)t_item商品表

item_id,//商品id
item_name,//名稱
category_1,//一級品類
category_2,//二級品類

  4)最近一個月,銷售數量最多的10個商品

select item_id,
       item_num,
       rank() over(partition by item_id order by item_num desc) `rank`
from (
    select item_id,count(*) `item_num`
    from t_order
    where date_sub('2021-08-06',30) <= 'create_time'
    group by item_id
) t1
where rank <= 10;

  5)最近一個月,每個種類里銷售數量最多的10個商品(一個訂單對應一個商品 一個商品對應一個品類)

select category,
       item_id,
       item_sum,
       rank
from (
    select category,
           item_id,
           sum(item_num) `item_sum`,
           rank() over(partition by category order by item_num desc) `rank`
    from (
        select t1.item_id,
               t1.item_num,
               t2.category
        from (
            select item_id,
                   count(*) `item_num`
            from t_order
            where date_sub('2021-08-06',30) <= 'create_time'
            group by item_id
        ) t1
        join (
            select item_id,
                   item_name,
                   category
            from t_item
        ) t2 on t1.item_id = t2.item_id
    ) t3
    group by category,item_id
) t4 
where rank <= 10;

5.7.3.31 計算平台的每一個用戶發過多少日記、獲得多少點贊數

5.7.3.32 處理產品版本號

  1)需求A:找出T1表中最大的版本號

select v1,max(v2)
from (
    select t1.v_id `v1`,  --版本號
           tmp.v_id `v2`  --
    from t1
    lateral view explode(v2) tmp as v2
)

  2)思路:列轉行,切割版本號,一列變三列(主版本號  子版本號 階段版本號)

select v_id,    --版本號
       max(split(v_id,".")[0]) v1,  --主版本不會為空
       max(if(split(v_id,".")[1]="",0,split(v_id,".")[1]))v2,   --取出子版本並判斷是否為空,並給默認值
       max(if(split(v_id,".")[2]="",0,split(v_id,".")[2]))v3    --取出階段版本並判斷是否為空,並給默認值
from t1

  3)需求B:計算出如下格式的所有版本號排序,要求對於相同的版本號,順序號並列

select v_id,
       rank() over(partition by v_id order by v_id) `seq`
from t1

5.8 Hive優化

5.8.1 MapJoin

  在Reduce階段完成Join,容易發生數據傾斜,可以在MapJoin階段將小表全部加載到內存中進行Join

5.8.2 行列過濾

  列處理:在select查詢時,只拿需要的列,如果有,盡量使用分區過濾,少用select *

  行處理:在分區裁剪中,當使用外關聯時,若將副表的過濾條件寫在where后面,則會先進行全表關聯,之后再過濾

5.8.3 列式存儲技術

5.8.4 分區技術

5.8.5 合理設置Map數

  1)mapred.min.split.size:數據的最小切割單元,默認值是1B

  2)mapred.min.split.size:數據的最大切割單元,默認256MB

  3)通過調整max可以起到調整map數的作用,減少max可以增加map數,增加max可以減少map數

5.8.6合理設置Reduce數

  1)過多的啟動和初始化Reduce會消耗時間和資源

  2)有多少個Reduce就會有多少個輸出文件,若生產了很多個小文件,則若這些小文件作為下一個任務的輸入,也會出現小文件過多的問題

  3)兩個原則

    (1)處理大數據量利用合適的Reduce數

    (2)單個Reduce任務處理數據量大小要合適

5.8.7 小文件如何產生?

  1)動態分區插入數據,產生大量小文件,導致map數劇增

  2)reduce數越多,小文件就越多

  3)數據源本身就包含大量小文件

5.8.8 小文件解決?

  1)使用CombineHiveInputFormat合並小文件

  2)merge

SET hive.merge.mapfiles = true;          -- 默認true,在map-only任務結束時合並小文件
SET hive.merge.mapredfiles = true;        -- 默認false,在map-reduce任務結束時合並小文件
SET hive.merge.size.per.task = 268435456;    -- 默認256M
SET hive.merge.smallfiles.avgsize = 16777216; -- 當輸出文件的平均大小小於16m該值時,啟動一個獨立的map-reduce任務進行文件merge

  3)JVM重用

set mapreduce.job.jvm.numtasks=10

5.8.9 不影響最終的業務邏輯情況下,開啟map端的combiner聚合

set hive.map.aggr=true;

5.8.10 文件壓縮

set hive.exec.compress.intermediate=true --啟用中間數據壓縮
set mapreduce.map.output.compress=true --啟用最終數據壓縮
set mapreduce.map.outout.compress.codec=…; --設置壓縮方式

5.8.11 采用tez引擎或Spark引擎

5.9 解決數據傾斜的方法

5.9.1 數據傾斜長啥樣?

5.9.2 數據傾斜如何產生?

  1)不同數據類型關聯產生數據傾斜

    (1)比如用戶表中user_id字段為string,log表中user_id字段int類型。當按照user_id進行兩個表的Join操作時,就會發生數據傾斜

    (2)解決方法:把數字類型轉為字符串類型

select * from
users a
left outer join 
logs b
on a.usr_id = cast(b.user_id as string)

  2)生產環境中會使用大量空值數據,這些數據進入到一個reduce中,導致數據傾斜

    解決方法:自定義分區,將為空的key轉變為字符串加隨機數或純隨機數,將因空值而造成傾斜的數據分布到多個Reducer。對於異常值如果不需要的話,最好是提前在where條件里過濾掉,這樣可以使計算量大大減少

5.9.3 解決數據傾斜的方法

  1)采用sum() group by的方式來替換count(distinct)完成計算,group by 優於distinct group

  2)在map階段進行Join

  3)開啟數據傾斜時負載均衡

    (1)先隨機分發處理,再按照key值進行group by

    (2)當選項設定為true,生成的查詢計划會有兩個MRJob。第一個MRJob中,Map的輸出結果集合會隨機分布到Reduce中,每個Reduce做部分聚合操作,並輸出結果,這樣處理的結果是相同的GroupBy Key有可能被分發到不同的Reduce中,從而達到負載均衡的目的;第二個MRJob再根據預處理的數據結果按照GroupBy Key分布到Reduce中(這個過程可以保證相同的原始GroupBy Key被分布到同一個Reduce中),最后完成最終的聚合操作

    (3)它使計算變成了兩個mapreduce,先在第一個中在shuffle過程partition時隨機給 key打標記,使每個key隨機均勻分布到各個reduce上計算,但是這樣只能完成部分計算,因為相同key沒有分配到相同reduce上。所以需要第二次的mapreduce,這次就回歸正常shuffle,但是數據分布不均勻的問題在第一次mapreduce已經有了很大的改善,因此基本解決數據傾斜。因為大量計算已經在第一次mr中隨機分布到各個節點完成

  4)設置多個reduce個數

    reduce個數的設定極大影響任務執行效率,不指定reduce個數的情況下,Hive會猜測確定一個reduce個數,基於以下兩個參數:

hive.exec.reducers.bytes.per.reducer(每個reduce任務處理的數據量,默認為1000^3=1G)
hive.exec.reducers.max(每個任務最大的reduce數,默認為999)

    計算reducer數的公式很簡單:N=min(參數2,總輸入數據量/參數1) ,即如果reduce的輸入(map的輸出)總大小不超過1G,那么只會有一個reduce任務

5.10 Hive里字段的分隔符用的是什么?為什么用\t?有遇到字段里邊有\t的情況嗎,怎么處理的?

  hive 默認的字段分隔符為ascii碼的控制符\001(^A),建表的時候用fields terminated by '\001'。注意:如果采用\t或者\001等為分隔符,需要要求前端埋點和javaEE后台傳遞過來的數據必須不能出現該分隔符,通過代碼規范約束。一旦傳輸過來的數據含有分隔符,需要在前一級數據中轉義或者替換(ETL)

hive-drop-import-delims:導入到hive時刪除 \n, \r, \001
hive-delims-replacement:導入到hive時用自定義的字符替換掉 \n, \r, \001

5.11 Tez引擎的優點

5.11.1 MR引擎

  多job串聯,基於磁盤,落盤的地方比較多。雖然慢,但一定能跑出結果。一般處理,周、月、年指標

5.11.2 Spark引擎

  雖然在Shuffle過程中也落盤,但是並不是所有算子都需要Shuffle,尤其是多算子過程,中間過程不落盤  DAG有向無環圖。 兼顧了可靠性和效率。一般處理天指標

5.11.3 Tez引擎

  完全基於內存,如果數據量特別大,慎重使用。容易OOM。一般用於快速出結果,數據量比較小的場景

5.12 MySQL元數據備份

  1)如數據損壞,可能整個集群無法運行,至少要保證每日零點之后備份到其它服務器兩個復本,使用Keepalived或者mycat

  2)MySQL utf8超過字節數問題

MySQL的utf8編碼最多存儲3個字節,當數據中存在表情號、特色符號時會占用超過3個字節數的字節,那么會出現錯誤 Incorrect string value: '\xF0\x9F\x91\x91\xE5\xB0...'

    (1)解決辦法:將utf8修改為utf8mb4

    (2)首先修改庫的基字符集和數據庫排序規則為utf8mb4_unicode_ci

    (3)再使用 SHOW VARIABLES LIKE '%char%'; 命令查看參數

5.13 Union和Union All 的區別

  1)union會將聯合的結果集去重,效率較union all差

  2)union all不會對結果集去重,效率高


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM