1. 程式人生 > 其它 >數倉工具—Hive複合資料型別(9)

數倉工具—Hive複合資料型別(9)

技術標籤:Hive資料倉庫大資料hive資料倉庫面試

複合資料型別

除了使用string之外的基礎資料型別,Hive中的列支援使用struct, map, array,union_type 等複合資料型別。

複合型別是指array_type、map_type、struct_type和union_type,一般在企業中使用 array_type和map_type會比較多一些。

資料型別描述語法示例
STRUCT和C語言中的struct或者"物件"類似,都可以通過"點"符號訪問元素內容。struct{‘John’, ‘Doe’}
MAPMAP是一組鍵-值對元素集合,使用key可以訪問元素。map(‘fisrt’, ‘John’, ‘last’, ‘Doe’)
ARRAY陣列是一組具有相同資料型別和名稱的變數的集合。Array(‘John’, ‘Doe’)

1. Array型別

ARRAY型別是由一系列相同資料型別的元素組成,這些元素可以通過下標來訪問。比如有一個ARRAY型別的變數fruits,它是由[‘apple’,‘orange’,‘mango’]組成,那麼我們可以通過fruits[1]來訪問元素orange,因為ARRAY型別的下標是從0開始的;

建立資料庫表,以array作為資料型別

create table person(name string,work_locations array<
string>) ROW FORMAT DELIMITED FIELDS TERMINATED BY '\t' COLLECTION ITEMS TERMINATED BY ',';

資料

biansutao beijing,shanghai,tianjin,hangzhou
linan changchu,chengdu,wuhan

入庫資料

LOAD DATA LOCAL INPATH '/home/hadoop/person.txt' OVERWRITE INTO TABLE person;

查詢

hive> select * from person;
biansutao       [
"beijing","shanghai","tianjin","hangzhou"] linan ["changchu","chengdu","wuhan"] Time taken: 0.355 seconds hive> select name from person; linan biansutao Time taken: 12.397 seconds hive> select work_locations[0] from person; changchu beijing Time taken: 13.214 seconds hive> select work_locations from person; ["changchu","chengdu","wuhan"] ["beijing","shanghai","tianjin","hangzhou"] Time taken: 13.755 seconds hive> select work_locations[3] from person; NULL hangzhou Time taken: 12.722 seconds hive> select work_locations[4] from person; NULL NULL Time taken: 15.958 seconds

Hive 中的陣列的訪問方式和Java 的一樣,欄位名[index],不同的是Hive中不會有下標越界的異常,如果發生了下標越界,只會返回NULL 而已,除了這樣通過下標的方式去訪問,Hive 還給我們提供了一個函式array_contains 來判斷數組裡面是否包含特定的值。

select * from person where array_contains(work_locations,'beijing');

image-20201225213000661

2. Map 型別

MAP包含key->value鍵值對,可以通過key來訪問元素。比如"userlist"是一個map型別,其中username是key,password是value;那麼我們可以通過userlist[‘username’]來得到這個使用者對應的password;

建立資料庫表

create table score(name string, score map<string,int>)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
COLLECTION ITEMS TERMINATED BY ','
MAP KEYS TERMINATED BY ':';

要入庫的資料

biansutao '數學':80,'語文':89,'英語':95
jobs '語文':60,'數學':80,'英語':99

入庫資料

LOAD DATA LOCAL INPATH '/home/hadoop/score.txt' OVERWRITE INTO TABLE score;

查詢

hive> select * from score;
biansutao       {"數學":80,"語文":89,"英語":95}
jobs    {"語文":60,"數學":80,"英語":99}
Time taken: 0.665 seconds
hive> select name from score;
jobs
biansutao
Time taken: 19.778 seconds
hive> select t.score from score t;
{"語文":60,"數學":80,"英語":99}
{"數學":80,"語文":89,"英語":95}
Time taken: 19.353 seconds
hive> select t.score['語文'] from score t;
60
89
Time taken: 13.054 seconds
hive> select t.score['英語'] from score t;
99
95
Time taken: 13.769 seconds

修改map欄位的分隔符

Storage Desc Params:	 	 
	colelction.delim    	##                  
	field.delim         	\t                  
	mapkey.delim        	=                   
	serialization.format	\t                  

可以通過desc formatted tableName查看錶的屬性。
hive-2.1.1中,可以看出colelction.delim,這裡是colelction而不是collection,hive裡面這個單詞寫錯了,所以還是要按照錯誤的來。

alter table t8 set serdepropertyes('colelction.delim'=',');

3. Struct 型別

STRUCT可以包含不同資料型別的元素。這些元素可以通過"點語法"的方式來得到所需要的元素,比如user是一個STRUCT型別,那麼可以通過user.address得到這個使用者的地址。有點類似Java 中的物件

建立資料表

CREATE TABLE test(id int,course struct<course:string,score:int>)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '\t'
COLLECTION ITEMS TERMINATED BY ',';

資料

1 english,80
2 math,89
3 chinese,95

入庫

LOAD DATA LOCAL INPATH '/home/hadoop/test.txt' OVERWRITE INTO TABLE test;

查詢

hive> select * from test;
OK
1       {"course":"english","score":80}
2       {"course":"math","score":89}
3       {"course":"chinese","score":95}
Time taken: 0.275 seconds
hive> select course from test;
{"course":"english","score":80}
{"course":"math","score":89}
{"course":"chinese","score":95}
Time taken: 44.968 seconds
select t.course.course from test t; 
english
math
chinese
Time taken: 15.827 seconds
hive> select t.course.score from test t;
80
89
95
Time taken: 13.235 seconds

**size(Map)函式:**可得map的長度。返回值型別:int

**map_keys(Map)函式:**可得map中所有的key; 返回值型別: array

**map_values(Map)函式:**可得map中所有的value; 返回值型別: array

判斷map中是否包含某個key值: array_contains(map_keys(t.params),‘k0’);

4. uniontype 型別

uniontype可以理解為泛型,同一時刻聯合體中的一個元素生效,uniontype中的元素共享記憶體,可以通過create_union內建函式建立uniontype:create_union(tag, val1, val2) tag是數字,0開始,必須小於後面引數的個數,怎麼理解呢,uniontype 型別的意思就是這個欄位可以儲存你定義的型別中的每一種都可以,例如我有一個欄位是 uni uniontype<int, double, string> 這就是說我uni 這個欄位可以儲存<int, double, string> 三種類型中的任意一種,但是同時只能存一個,不能說我同時存了一個欄位是<int, string> 因為這都不滿足第一正規化了。

開始之前我們先介紹一個函式create_union,你可以執行desc function extended create_union;來檢視一下這個函式的定義是什麼

+--------------------------------------------------------------------------------------------+
|                      tab_name                                                              |
+--------------------------------------------------------------------------------------------+
| create_union(tag, obj1, obj2, obj3, ...) - Creates a union with the object for given tag   |
| Example:                                                                                   |
|   > SELECT create_union(1, 1, "one") FROM src LIMIT 1;                                     |
|   one                                                                                      |
| Function class:org.apache.hadoop.hive.ql.udf.generic.GenericUDFUnion                       |
| Function type:BUILTIN                                                                      |
+--------------------------------------------------------------------------------------------+

這個函式就是返回一個uniontype 型別的值,就像date 函式返回一個date 型別的值一樣。那這個函式是怎麼用的呢,第一個引數你可以認為是陣列的下標,後面的全部引數你可以認為是陣列,返回的uniontype值就是陣列指定下標處的值,只不過這個返回值

還會帶一個下標

select  create_union(0,10,10.0, '10.1');

image-20201225223309798

此時返回值是一個類似map的結構,其中0是下標,10 是下標為0 處對應的值。

下面我們來一個完整的演示

create table uniontab(
    arr array<string>,
    mp MAP<INT, string>,
    stru struct<name:string, age:int>,
    uni uniontype<int, decimal(2,0), string>
)
row format 
  delimited fields terminated by ','
  collection items terminated by '#'
  map keys terminated by ':'
  lines terminated by '\n';

接下來我們插入一條資料,就像下面一樣,你就會看到報錯了

insert into uniontab select array('d','e','f'), map(1,'zjx',2,'wly'), named_struct('name', 'wly', 'age', 17), create_union(0,10.0, '10.1');

image-20201225223620836

Cannot convert column 3 from uniontype<int,string> to uniontype<int,decimal(2,0),string>. (state=42000,code=10044)

什麼意思呢,第三列uniontype<int,string> 不能轉換成 uniontype<int,decimal(2,0),string> ,啥意思呢我們查詢出來的,也就是create_union(0,10.0, ‘10.1’)的型別是uniontype<int,string> 它不能轉換成我們表定義的 uniontype<int,decimal(2,0),string> ,我們查詢出來的型別為什麼是uniontype<int,string>呢這是和我們的create_union 函式有關,就是我們create_union 函式的的第二個引數陣列中的資料個數和型別的順序必須和我們表定義的時候的型別的順序一致

insert into uniontab select array('d','e','f'), map(1,'zjx',2,'wly'), named_struct('name', 'wly', 'age', 17), create_union(0,2,10.0, '101.1');

上面這樣就對了

5. 不支援組合的複雜資料型別

我們有時候可能想建一個複雜的資料集合型別,比如下面的a欄位,本身是一個Map,它的key是string型別的,value是Array集合型別的。

建表

create table test1(id int,a MAP<STRING,ARRAY<STRING>>)
row format delimited fields terminated by '\t' 
collection items terminated by ','
MAP KEYS TERMINATED BY ':';

匯入資料

1 english:80,90,70
2 math:89,78,86
3 chinese:99,100,82

LOAD DATA LOCAL INPATH '/home/hadoop/test1.txt' OVERWRITE INTO TABLE test1;

這裡查詢出資料:

hive> select * from test1;
OK
1	{"english":["80"],"90":null,"70":null}
2	{"math":["89"],"78":null,"86":null}
3	{"chinese":["99"],"100":null,"82":null}

可以看到,已經出問題了,我們意圖是想"english":[“80”, “90”, “70”],實際上把90和70也當作Map的key了,value值都是空的。分析一下我們的建表語句,collection items terminated by ','制定了集合型別(map, struct, array)資料元素之間分隔符是", “,實際上map也是屬於集合的,那麼也會按照逗號分出3個key-value對;由於MAP KEYS TERMINATED BY ':'定義了map中key-value的分隔符是”:",第一個“english”可以準確識別,後面的直接把value置為"null"了。

總結

複雜型別一般用的不多,但是有時候還是會用,因為可能和歷史有關或者是數倉的建設者有關,因為複雜型別已經違揹我們對資料庫的定義,雖然數倉和資料庫不一樣,但是三正規化在表的定義上的影響長遠,大家在建表的時候可以思考一下自己真的需要這種複雜資料型別嗎

型別名稱建立函式訪問方式引入版本
Array陣列型別array(‘d’,‘e’,‘f’)下標0.14
Mapk-v 型別map(1,‘zjx’,2,‘wly’)欄位名稱0.14
Struct結構體型別named_struct(‘name’, ‘wly’, ‘age’, 17).0.0
Uniontype聯合型別create_union(1,10.0, ‘10.1’);0.7.0

Uniontype 這個型別不太好理解,網上資料也不多,所以我重點講解了一下,大家可以多體會體會。

針對複合資料型別也衍生出來了很多函式,可以在需要的時候可以自行查閱,後續我也會慢慢新增到這篇文章中去,不斷完善,大家如果覺得有什麼地方還需要完善可以指出來