1. 程式人生 > 其它 >面試(涉及技術一)

面試(涉及技術一)

一、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.2Shell常用工具及寫過的指令碼

1.2.1 常用工具

  1)awk

  2)sed

  3)cut

  4)sort

1.2.2 用shell寫過哪些指令碼

  1)叢集啟動,分發指令碼

  2)數倉與Mysql的匯入匯出

  3)數倉層級內部的匯入

1.2.3Shell中提交了一個指令碼,程序號已經不知道了,但是需要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.2Hadoop配置檔案以及簡單的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.3Paxos演算法

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

  分散式系統中的節點通訊存在兩種模型:共享記憶體(Shared memory)和訊息傳遞(Messages passing)。

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

3.4CAP法則

  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一天匯入多少資料?

  一天100萬日活,產生10萬訂單,平均每人10個訂單,每天1G左右的業務資料,所以Sqoop需要將1G左右的資料匯入數倉

4.6 sqoop資料匯出的時候一次執行多長時間?

  每天晚上0:30開始執行,一般執行時間在40 -50分鐘,取決於資料量的大小(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

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)分時統計各小區使用者停車數

  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表資訊如下:

自動編號學號姓名 課程編號 課程名稱 分數
12005001張三  0001數學 69
22005002李四  0001數學 89
32005001張三  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不會對結果集去重,效率高