MaxCompute SQL大數據公開數據集實戰

MaxCompute公開數據集簡介

目前阿里雲MaxCompute已經免費向全部用戶開放了多種公用數據集,包括:

  • 股票價格數據
  • 房產信息
  • 影視及其票房數據
  • TPC-DS數據集

這些公開數據現已免費開放給用戶,並已經完成了數據分析前序較複雜的數據獲取、上傳、清洗等過程,可以直接進入數據分析階段。因此可以直接使用這些數據集來進行一定的MaxCompute的學習以及數據的分析過程。具體的MaxCompute公開數據集介紹見:阿里雲MaxCompute(大數據)公開數據集---帶你玩轉人工智能

公開數據集開發環境

爲了使用上述的公開數據集,需要進行如下的步驟:

1. 獲取權限

所有的數據均被存儲在MaxCompute 產品中的public_data 項目空間中。首先,需要以項目空間的owner 或者管理員的身份,在自己的項目空間下,執行如下操作。執行完成後用戶項目空間下的所有成員均可讀取各公開數據集合。

add user ALIYUN$everyone;

2. 使用public_data項目空間下的表

用戶需要跨項目空間訪問數據,在數據工場中編輯SQL 時,必須在表明前指定項目名稱,例如:

select * from public_data.ods_enterprise_share_basic where ds = '20170114';

3. MaxCompute Studio開發環境

下面對public_data中公開數據集的操作主要使用MaxCompute Studio開發環境,具體的開發環境搭建見前序文章:MaxCompute基礎開發環境搭建 。

影視及票房數據集簡介

該公開數據集是所有公開數據集中結構和內容較簡單的一個,其中包括的數據信息爲:每日更新國內影視劇信息及票房數據信息 (後續會發現其實該數據集中的內容沒有持續更新)。具體介紹如下:

項目 public_data
表集合

dwd_product_movie_basic_info 電影基本信息

ods_product_movie_box 票房基本信息

更新週期 每日早10 點前更新。至2016 年12 月13 日開始更新,全量更新(這裏是官方的介紹,但實際內容好像有所出入,後面後進行查看)
 查詢示例  select * from public_data.dwd_product_movie_basic_info where ds ='20170112' limit 10;

dwd_ product_ movie_ basic_ info表結構

字段英文名

字段類型

描述

是否是分區列

movie_ name

STRING

電影名稱

 

dirctor

STRING

導演

 

scriptwriter

STRING

編劇

 

area

STRING

製片地區/國家

 

actors

STRING

主演

 

type

STRING

類型

 

movie_ length

STRING

電影長度

 

movie_ date

STRING

上映日期

 

movie_ language

STRING

語言

 

imdb_ url

STRING

imdb號

 

ds

STRING

日期

分區列

ods_product_movie_box表結構

字段英文名

字段類型

描述

是否是分區列

rank

STRING

排名

 

avgprice

STRING

平均票價

 

avppeople

STRING

場均人次

 

boxoffice

STRING

單日票房(萬)

 

boxoffice_ up

STRING

環比變化 (%)

 

irank

STRING

排名

 

movieday

STRING

上映天數

 

moviename

STRING

影片名

 

sumboxoffice

STRING

累計票房(萬)

 

womindex

STRING

口碑指數

 

ds

STRING

日期

分區列

影視及票房數據集SQL實戰

瞭解完該數據集的大致內容,下面使用MaxCompute SQL來初步探索一下這個數據集的內容。

查詢dwd_product_movie_basic_info表

查看電影基本信息表的記錄起止時間

由於dwd_product_movie_basic_info使用ds(日期列)來作爲分區信息,因此查看該表的分區信息就可以獲得整個數據集數據記錄的起止時間,命令如下;

show partitions public_data.dwd_product_movie_basic_info;

返回結果如下:

ds=20161227
ds=20170112
ds=20170113
ds=20170114
ds=20170115
ds=20170116
...
ds=20170629
ds=20170630
ds=20170701
ds=20170702
ds=20170703
ds=20170704
ds=20170705

因此可以看到該數據集的開始記錄的時間爲20161227,然後再從20170112開始進行每天連續增量更新直到20170705。此後則沒有繼續進行數據的更新。

所以對該表進行查詢過程中,查詢的日期範圍應該在這個時間返回內,否則無數據返回。引入使用如下命令查詢今天的電影信息則是沒有數據的:

-- 無該分區數據
select * from public_data.dwd_product_movie_basic_info where ds ='20190201' limit 10;

查詢每日上映電影數量

dwd_product_movie_basic_info表中使用ds作爲分區,下面統計每個分區(每天)記錄的電影數量:

-- 統計每天電影記錄的總數量
select ds, count(*) as ds_count from public_data.dwd_product_movie_basic_info group by ds;

返回結果如下:

ds	ds_count
+---+---------+
20170112	359927
20170113	360347
20170114	360693
20170115	360905
20170116	360905
20170117	361159
20170118	361545
20170119	361727
...
20170629	379581
20170630	379581
20170701	379581
20170702	379581
20170703	379581
20170704	379581
20170705	379581

這裏可以看到每天的電影記錄數量有30多萬條,想想一天內會有那麼多電影上映嗎?對這個數量有些疑惑,可以使用如下命令來驗證對所有時間的記錄數進一步統計,並與數據表的所有記錄數量進行對比,來驗證這裏的統計結果。

首先來對上述的結果進行進一步的統計:

-- 每天電影記錄數量的真實性,對所有的求和彙總
select sum(tmp.count) as total_num from (
  select ds, count(*) as count from public_data.dwd_product_movie_basic_info group by ds
) tmp;

然後在查詢該表中所有記錄的數量:

-- 統計該表中的數據總數
select count(*) from public_data.dwd_product_movie_basic_info;

兩者返回的結果均爲:

65430458

因此,可以看出來上述的按日期對電影數量的統計結果並沒有問題,但每天有30多萬的電影上映還是不太相信啊,畢竟對公開數據集的介紹裏說的這是國內的數據量。帶着這個疑問,我們來查看某一個日期的電影信息:

-- 查詢20170304分區中的10條數據
select * from public_data.dwd_product_movie_basic_info where ds ='20170304' limit 10;

返回結果如下:

可以看到這裏ds並不是電影上映的時間,而是數據進行分區記錄的時間,因此根據ds查詢的話,很多記錄的電影數據實際上是已經上映過的,因此這裏的數據那麼龐大就可以理解了。

根據電影上映日期進行查詢

通過上面的查詢可以看到ds分區列不是電影上映的時間,而是數據記錄的時間。因此可以根據電影上映時間對數據進行一些查詢。例如如下幾個查詢:

1. 查詢1990年到2020年,上映日期最近的20個的英文記錄片電影:

-- 查詢1990年到2020年,上映日期最近的20個的英文記錄片電影
select distinct *
from public_data.dwd_product_movie_basic_info
where movie_language='英文'
and type='紀錄片'
and movie_date between '1990-01-01' and '2020-01-01'
order by movie_date desc
limit 20;

這裏是一般的查詢方式,得到的結果如下;

可以看到這裏其實只是一條數據, 而distinct去重語句沒有生效的原因是,distinct根據所有列進行去重,而這裏數據的ds不同,因此去重不掉。而這裏在不同ds存在相同電影信息的原因是:一個電影具有一定的上映時間範圍,例如電影A從201901月上映到201906月下映,因此這段時間返回內,不同時間(ds)均有這個電影的記錄。

爲了去重這裏的數據,這裏可以將ds列從distinct中去除後進行重新查詢:

-- 查詢1990年到2020年,上映日期最近的20個的英文記錄片電影(去重需要過濾掉ds)
select distinct movie_name, dirctor, scriptwriter, area, actors, type, movie_length, movie_date, movie_language, imdb_url
from public_data.dwd_product_movie_basic_info
where movie_language='英文'
and type='紀錄片'
and movie_date between '1990-01-01' and '2020-01-01'
order by movie_date desc
limit 20;

得到的結果發現只有一條數據:

看來這個數據集中的記錄其實還是有一定問題的。

2. 查詢1990年到2020年,上映日期最近的20箇中文電影,並且除imdb_url可爲空之外,其他字段都不能爲空:

-- 查詢1990年到2020年,上映日期最近的20箇中文電影,並且除imdb_url可爲空之外,其他字段都不能爲空
select distinct movie_name, dirctor, scriptwriter, area, actors, type, movie_length, movie_date, movie_language
from public_data.dwd_product_movie_basic_info
where movie_language='中文'
and scriptwriter is not null and actors is not null and type is not null and movie_length is not null
and movie_date between '1990-01-01' and '2020-01-01'
order by movie_date desc
limit 20;

返回結果如下,看來滿足條件的數據只有19個:

 

查詢ods_product_movie_box表

查看票房記錄表中數據的起止日期

show partitions public_data.ods_product_movie_box;

返回結果如下:

ds=20170113
ds=20170114
ds=20170115
ds=20170116
ds=20170117
ds=20170118
ds=20170119
ds=20170120
ds=20170121
...
ds=20170801
ds=20170802
ds=20170803
ds=20170805
ds=20170806
ds=20170807
ds=20170808
ds=20170809
ds=20170810
ds=20170812

可以看到ods_product_movie_box表中的記錄開始於20170113,終止於20170812。記錄除最後一天外,均是每日連續記錄。但該表的記錄和dwd_product_movie_basic_info表中的記錄,在日期方面並不是在一個時間範圍內。

統計每天具有票房的電影記錄數

--查看每天有多少記錄了票房的電影
select ds, count(*) as ds_count from public_data.ods_product_movie_box group by ds;

返回結果如下:

ds	ds_count
+---+----+
20170113	10
20170114	10
20170115	10
20170116	10
20170117	10
20170118	10
20170119	10
...
20170803	10
20170805	10
20170806	10
20170807	10
20170808	10
20170809	10
20170810	10
20170812	10

可以看到每天電影票房的記錄只記錄排名前10的電影。可以查看某一天的電影票房數據如下:

select * from public_data.ods_product_movie_box where ds='20170115';

返回結果如下:

統計電影票房

1. 統計每日電影累計總票房

--查看每天(每分區)上映電影的總票房
select ds, sum(sumboxoffice) as totol_box from public_data.ods_product_movie_box group by ds;

返回結果如下:

ds	totol_box
+---+----------+
20170113	254059.0
20170114	262496.0
20170115	274466.0
20170116	324493.0
20170117	333592.0
20170118	335652.0
20170119	300577.0
20170120	280885.0
20170121	351980.0
20170122	361073.0
...
20170802	390234.0
20170803	323639.0
20170805	396451.0
20170806	376678.0
20170807	497773.0
20170808	534007.0
20170809	458423.0
20170810	480300.0
20170812	534594.0

2. 統計每日電影單日總票房

--單日總票房
select ds, sum(boxoffice) as daily_total_box from public_data.ods_product_movie_box group by ds;

返回結果如下:

ds	daily_total_box
+---+----------------+
20170113	1158.0
20170114	1032.0
20170115	570.0
20170116	414.0
20170117	431.0
20170118	492.0
20170119	465.0
20170120	624.0
20170121	401.0
20170122	380.0
...
20170802	4650.0
20170803	2083.0
20170805	1310.0
20170806	82.0
20170807	385.0
20170808	438.0
20170809	752.0
20170810	5154.0
20170812	1017.0

 3. 統計2017前半年,單日總票房最高的十天

select ds, sum(boxoffice) as daily_total_box 
from public_data.ods_product_movie_box
where ds between '20170101' and '20170630'
group by ds
order by daily_total_box desc
limit 10;

返回結果如下: 

根據票房信息進行查詢

1. 查詢2017年前半年累計票房最高的10個電影:

-- 查詢2017年前半年累計票房最高的10個電影
select distinct *
from (select * from public_data.ods_product_movie_box where ds between '20170101' and '20170630') tmp
order by sumboxoffice desc
limit 10;

返回結果如下:

這裏可以看到數據並沒有嚴格的根據sumboxoffice字段進行降序排序,具體的原因:是因爲這裏的sumboxoffice字段的類型爲string,因此在對其進行排序時,是根據字符串的字典序進行排序的,而不是數值的大小

分析該表的數據可知,該表中的記錄是每個電影在上映時間範圍內的每天的數據信息,因此如果需要查找2017年前半年累計票房最高的10個電影,需要根據電影名稱進行分組查詢。同時修復上面的字符串數組排序問題,命令如下:

-- 查詢2017年前半年累計票房最高的10個電影(子查詢中沒有對sumboxoffice進行類型轉換)
select distinct tmp.moviename, cast(tmp.max_sumboxoffice as bigint) as sumboxoffice_num, tmp.max_ds as end_time
from (
  select moviename, max(sumboxoffice) as max_sumboxoffice, max(ds) as max_ds 
  from public_data.ods_product_movie_box
  where ds between '20170101' and '20170630' 
  group by moviename
) tmp
order by sumboxoffice_num desc
limit 10;

返回結果如下:

但上述查詢的子查詢中沒有注意sumboxoffice的類型轉換問題, 下面在子查詢中加上max(cast(sumboxoffice as bigint))的強制類型轉換,並且去掉外層查詢的類型轉換:

-- 查詢2017年前半年,累計票房最高的10個電影(注意數據類型轉換)
select distinct tmp.moviename, tmp.max_sumboxoffice as sumboxoffice_num, tmp.max_ds as end_time
from (
  select moviename, max(cast(sumboxoffice as bigint)) as max_sumboxoffice, max(ds) as max_ds 
  from public_data.ods_product_movie_box
  where ds between '20170101' and '20170630' 
  group by moviename
) tmp
order by sumboxoffice_num desc
limit 10;

返回結果如下,這纔是正確的查詢結果: 

2. 查詢2017年前半年,單日票房最高的10個電影。(這裏不根據天來計算,例如某天單日票房第二高的電影A的票房,大於第二天單日票房第一高的電影B,那麼A依然被包含在結果集中,並且排序在B之前),命令如下:

select distinct moviename, cast(boxoffice as bigint) as max_boxoffic, ds
from (
  select * from public_data.ods_product_movie_box 
  where ds between '20170101' and '20170630'
) tmp
order by max_boxoffic desc
limit 10;

返回結果如下: 

例如這裏大鬧天竺在20170127單日票房中排名第2,但它的單日票房大於20170622排名第一的變形金剛5,此時大鬧天竺仍然在結果集中,並且排名在變形金剛5之前。

此外,此時的查詢結果仍然有一個問題,就是排名中有重複排名的電影。這是由於變形金剛5分別在20170622,23,24三天均排名第一,因此該電影在排行榜中出現了三次。

3. 查詢2017年前半年,每天單日票房最高的10個電影。

這裏的單日最高,是統計前半年中每日票房最高的10個電影,以及所在的日期。不同於上面的查詢,此時就不允許日期具有重複了,也就是每天中只能選取具有最高票房的一個電影參與排序。此時需要對ods_product_movie_box表進行自關聯的聯合查詢。命令如下,其中子查詢是根據ds進行分組,找到每日的最大單日票房max_boxofffice和對應的ds,然後外層查詢將子查詢作爲一個表,對ods_product_movie_box自身進行join操作,連接的條件便是單日票房max_boxofffice和對應的ds,最後得到查詢結果。

-- 查詢2017年前半年,每天單日票房最高的10個電影(需要根據天(ds)分組)
select distinct t1.moviename, t2.max_boxoffice as max_boxoffice_num, t2.ds
from public_data.ods_product_movie_box as t1
join (
  select max(cast(boxoffice as bigint)) as max_boxoffice, ds
  from public_data.ods_product_movie_box
  where ds between '20170101' and '20170630'
  group by ds
) t2 on t1.ds=t2.ds and t1.boxoffice=t2.max_boxoffice
ORDER by max_boxoffice_num desc
limit 10;

 返回結果如下:

可以看到此時,ds沒有重複的數據,並且“變形金剛5”仍然是佔據了20170622-24三天的最高單日票房。

4. 查詢2017年前半年平均票價最高的10個電影。

這裏的查詢思路與上述類似,由於是找到平均票價最高的10個電影,而票房記錄中存在一個電影的多個每日票價,因此這裏還是需要根據moviename維度來進行去重。子查詢根據moviename維度來查找平均票價最高的moviename和對應的max_avgprice,然後外層查詢進行join來查詢對應的moivename的上映日期,最後進行排序。

-- 查詢2017年前半年平均票價最高的10個電影
select t1.moviename, t1.max_avgprice, t2.ds 
from (
  select moviename, max(cast(avgprice as bigint)) as max_avgprice 
  from public_data.ods_product_movie_box
  group by moviename
) as t1
join public_data.ods_product_movie_box as t2
on t1.moviename=t2.moviename and t1.max_avgprice=t2.avgprice
where ds between '20170101' and '20170630'
order by max_avgprice desc
limit 10;

返回結果如下:

5. 查詢2017年前半年上映時間最長的10個電影。

這裏同樣需要使用子查詢,根據moviename進行分組得到moviename,max_movieday和min_ds(電影開始的上映時間),然後在通過外查詢進行排序。

-- 查詢2017年前半年上映時間最長的10個電影
select moviename, tmp.max_movieday as max_movieday_num, min_ds as start_ds
from (
  select moviename, max(cast(movieday as bigint)) as max_movieday, min(ds) as min_ds
  from public_data.ods_product_movie_box
  where ds between '20170101' and '20170630'
  group by moviename
) tmp
order by max_movieday_num desc
limit 10;

返回結果如下: 

這裏的上映時長有點不可思議啊, 暫時不知道是否是數據有問題。

6. 統計2017年上半年,上座率最高的10部電影。

-- 統計2017年上半年,上座率最高的10部電影
select t1.moviename, t1.max_avppeople, t2.ds, t2.movieday, t2.rank, t2.avgprice, t2.boxoffice
from (
  select moviename, max(cast(avppeople as bigint)) as max_avppeople
  from public_data.ods_product_movie_box
  where ds between '20170101' and '20170630'
  group by moviename
) t1
join public_data.ods_product_movie_box t2 on t1.moviename=t2.moviename and t1.max_avppeople=t2.avppeople
order by max_avppeople desc
limit 10;

得到的結果如下:

 

從返回的數據中可以看到,這裏上座人數最多的movieday很多都是負值,這裏大致猜測一下,應該是電影的未正式上映前的點映階段。例如這裏面比較熟悉的“銀河護衛隊2”,在點映階段的上座率最高,並且票價也很高。好奇看一下該電影的票房變化信息:

select moviename, movieday, rank, avgprice, avppeople, boxoffice, boxoffice_up, sumboxoffice, ds
from public_data.ods_product_movie_box
where moviename='銀河護衛隊2'
order by ds
limit 100;

返回結果如下: 

可以看到,該電影是在0501日點映,然後在五一黃金週拿到了不少的票房,在黃金週過去後票房開始回落。

查詢一個電影的每日票房信息變化

上述已經查詢了“銀河護衛隊2”的票房變化情況,下面查詢另一個電影(“乘風破浪”)從上映到下映之間的每日票房變化:

-- 查詢一個電影從上映到下映之間的每日票房變化
select moviename, movieday, rank, boxoffice, sumboxoffice, ds
from public_data.ods_product_movie_box
where moviename='乘風破浪'
order by ds
limit 100;

返回結果如下:

可以看到該電影一共上映了35天,從上映第一天到下映這個範圍單日排名變化、平均票價,場均人次,每日票房變化、票房浮動、以及累計票房變化。這樣就可以根據ds時間維度來進行一些數據可視化的工作。

電影信息表和票房表聯合查詢

查詢兩個表公有的數據

1. 查詢包含在兩個表中的電影數據的10條記錄:

-- 查詢包含在兩個表中的電影數據的10條記錄
select distinct movie_name, dirctor, scriptwriter, area, actors, type, movie_length, movie_date, movie_language
from public_data.dwd_product_movie_basic_info as t1
join public_data.ods_product_movie_box as t2 on t1.movie_name=t2.moviename
limit 10;

返回結果如下:

2. 查詢2017年前半年,累計票房最高的10個電影,並查看上映日期。

上面已經通過表ods_product_movie_box進行了該查詢的前半部分“查詢2017年前半年,累計票房最高的10個電影”,後面的電影上映時間則是需要在dwd_product_movie_basic_info表中獲取。因此這裏需要進行兩個表的join操作。

容易想到的是使用之前的查詢作爲子查詢,來join dwd_product_movie_basic_info表獲取movie_date即可,命令如下:

-- 查詢2017年前半年,累計票房最高的10個電影,並查看上映日期(存在影片重複)
select distinct t1.moviename, t1.sumboxoffice_num, t1.end_time, t2.movie_date
from (
  select distinct tmp.moviename, tmp.max_sumboxoffice as sumboxoffice_num, tmp.max_ds as end_time
  from (
    select moviename, max(cast(sumboxoffice as bigint)) as max_sumboxoffice, max(ds) as max_ds
    from public_data.ods_product_movie_box
    where ds between '20170101' and '20170630' group by moviename) tmp
  order by sumboxoffice_num desc
  limit 10
) t1
left join public_data.dwd_product_movie_basic_info as t2 on t1.moviename=t2.movie_name
order by t1.sumboxoffice_num desc
limit 10;

但此時得到的查詢結果爲:

可以看到最後的乘風破浪由於在 dwd_product_movie_basic_info表中存在多條記錄,因此這裏join之後會出現影片的重複。

爲了去掉dwd_product_movie_basic_info表中相同影片重複的記錄,需要對dwd_product_movie_basic_info表進行處理後在進行join操作:

-- 查詢2017年前半年,累計票房最高的10個電影,並查看上映日期(完整)
select distinct t1.moviename, t1.sumboxoffice_num, t1.end_time, t2.movie_date
from (
  select distinct tmp.moviename, tmp.max_sumboxoffice as sumboxoffice_num, tmp.max_ds as end_time
  from (
    select moviename, max(cast(sumboxoffice as bigint)) as max_sumboxoffice, max(ds) as max_ds
    from public_data.ods_product_movie_box
    where ds between '20170101' and '20170630' group by moviename) tmp
  order by sumboxoffice_num desc
  limit 10
  ) t1
  left join (
    select movie_name, max(movie_date) as movie_date
    from public_data.dwd_product_movie_basic_info
    group by movie_name
  ) as t2 on t1.moviename=t2.movie_name
order by t1.sumboxoffice_num desc
limit 10;

得到的正確查詢結果如下:

可以看到,此時的查詢結果去除了影片的重複,得到了正確的結果。此時也可以發現, dwd_product_movie_basic_info表中的影片數據其實不是很全面,很多影片的move_date字段都是缺失的,這應該是數據集在進行數據收集時的問題。

結語

上面對MaxCompute的公開數據集--電影及票房數據,進行了各種的SQL查詢。內容包括了比較常用的一些查詢過程,但仍可以使用更多的SQL來進行其他方面或維度的數據分析,有興趣的讀者可以繼續進行完善。

此外,MaxCompute還公開了其他的數據集供開發使用,這裏僅是選取了較爲簡單的一個。並且從上面的查詢過程可以看出,該數據集中特別是dwd_product_movie_basic_info表中的數據,並不是很完整,可能是數據獲取和清理時有一定的缺失,可以嘗試在其他公開數據集中進行更加深入的處理和分析操作。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章