CUBE SUGAR CONTAINER

技術系のこと書きます。

Apache Hive の ARRAY 型を試す

Apache Hive には ARRAY 型というデータタイプがある。 これは一般的なプログラミング言語でいえば配列に相当するもの。 ようするに、文字列や数値といったデータを一つのレコードに複数格納できる。 リレーショナルデータベースのアンチパターンであるジェイ・ウォークをするために用意されたようなものだ。 ただ、Apache Hive は JOIN の性能が高くないことから、こういったデータタイプを使わざるを得ない場合もあるということだろう。 使ってみたところ一般的なデータタイプとはちょっと違ってクセがあったので、それについてメモしておく。

使った環境は次の通り。 Apache Hive や Hadoop のインストール部分は省略する。

$ cat /etc/redhat-release 
CentOS Linux release 7.4.1708 (Core)
$ uname -r
3.10.0-693.5.2.el7.x86_64
$ hadoop version
Hadoop 2.8.3
Subversion https://git-wip-us.apache.org/repos/asf/hadoop.git -r b3fe56402d908019d99af1f1f4fc65cb1d1436a2
Compiled by jdu on 2017-12-05T03:43Z
Compiled with protoc 2.5.0
From source with checksum 9ff4856d824e983fa510d3f843e3f19d
This command was run using /home/vagrant/hadoop-2.8.3/share/hadoop/common/hadoop-common-2.8.3.jar
$ hive --version
Hive 2.3.2
Git git://stakiar-MBP.local/Users/stakiar/Desktop/scratch-space/apache-hive -r 857a9fd8ad725a53bd95c1b2d6612f9b1155f44d
Compiled by stakiar on Thu Nov 9 09:11:39 PST 2017
From source with checksum dc38920061a4eb32c4d15ebd5429ac8a

下準備

まずは Hive の CLI を起動しておく。

$ hive
hive> set hive.cli.print.header=true;

ARRAY 型について

ARRAY 型では、以下のように ARRAY でくくって複数の値をカンマ区切りで格納できる。

hive> SELECT ARRAY("a", "b");
OK
_c0
["a","b"]
Time taken: 8.74 seconds, Fetched: 1 row(s)

テーブルを作る

ARRAY 型を使ってテーブルを作るときは、中に入るデータタイプを指定する。

hive> CREATE TABLE users (
    >   name STRING,
    >   emails ARRAY<STRING>
    > );
OK
Time taken: 0.997 seconds

このように、ちゃんとテーブルができた。

hive> SHOW CREATE TABLE users;
OK
createtab_stmt
CREATE TABLE `users`(
  `name` string, 
  `emails` array<string>)
ROW FORMAT SERDE 
  'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe' 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.mapred.TextInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
  'hdfs://master:9000/user/hive/warehouse/users'
TBLPROPERTIES (
  'transient_lastDdlTime'='1514884223')
Time taken: 0.405 seconds, Fetched: 13 row(s)

レコードを追加する

レコードを追加するときは、ちょっとした一工夫が必要になる。

普通にデータを追加しようとしても、次のようにエラーになってしまう。

hive> INSERT INTO users VALUES ("Alice", ARRAY("alice@example.jp", "alice@example.com"));
FAILED: SemanticException [Error 10293]: Unable to create temp file for insert values Expression of type TOK_FUNCTION not supported in insert/values

データを追加するときは、先ほど SELECT 文であれば正しくデータが表示できたことを応用する。

hive> SELECT "Alice", ARRAY("alice@example.jp", "alice@example.com");
OK
_c0 _c1
Alice   ["alice@example.jp","alice@example.com"]
Time taken: 0.12 seconds, Fetched: 1 row(s)

INSERT INTO <table> に続けて、上記の SELECT 文を入れてやれば良い。

hive> INSERT INTO users
    >   SELECT "Alice", ARRAY("alice@example.jp", "alice@example.com");
...
OK
_c0 _c1
Time taken: 27.894 seconds

今度は、ちゃんと追加できた。

hive> SELECT * FROM users;
OK
users.name  users.emails
Alice   ["alice@example.jp","alice@example.com"]
Time taken: 1.308 seconds, Fetched: 1 row(s)

データを集計する

データを集計するときも一工夫が必要になる。

ひとまず、別のレコードも追加しておこう。

hive> INSERT INTO users
    >   SELECT "Bob", ARRAY("bob@example.net", "bob@example.org");
...
OK
_c0 _c1
Time taken: 20.907 seconds

テーブルには二人分のデータが入っている。 ただ、中身が ARRAY 型のままでは集計できない。

hive> SELECT emails FROM users;
OK
emails
["alice@example.jp","alice@example.com"]
["bob@example.net","bob@example.org"]
Time taken: 0.203 seconds, Fetched: 2 row(s)

そんなときは LITERAL VIEWexplode() 関数を使って、中身を展開してやる。 例えば以下では emails カラムの中身を email として展開している。

hive> SELECT *
    > FROM users
    > LATERAL VIEW explode(emails) users AS email;
OK
users.name  users.emails    users.email
Alice   ["alice@example.jp","alice@example.com"]  alice@example.jp
Alice   ["alice@example.jp","alice@example.com"]  alice@example.com
Bob ["bob@example.net","bob@example.org"] bob@example.net
Bob ["bob@example.net","bob@example.org"] bob@example.org
Time taken: 0.093 seconds, Fetched: 4 row(s)

展開できてしまえばこちらのもの。 例えばユーザがメールアドレスをいくつ持っているのか集計してみよう。 ユーザごとに GROUP BY した上で、展開した email カラムを COUNT() 関数にかける。

hive> SELECT
    >   name,
    >   COUNT(email) AS email_count
    > FROM users
    > LATERAL VIEW explode(emails) users AS email
    > GROUP BY name;
...
OK
name    email_count
Alice   2
Bob 2
Time taken: 27.477 seconds, Fetched: 2 row(s)

ばっちり。

外部ファイルからデータを読み込む

外部ファイルから ARRAY 型にデータを読み込むときはテーブルを作るときに一工夫が必要になる。 具体的には COLLECTION ITEMS TERMINATED BY を使って区切り文字を指定する。

hive> CREATE TABLE users (
    >     name STRING,
    >     emails ARRAY<STRING>
    > )
    > ROW FORMAT DELIMITED 
    > FIELDS TERMINATED BY ','  
    > COLLECTION ITEMS TERMINATED BY '$'
    > STORED AS TEXTFILE;
OK
Time taken: 0.051 seconds

読み込むデータを次のように用意しておく。

$ cat << 'EOF' > users.csv 
Alice,alice@example.jp$alice@example.com
Bob,bob@example.net$bob@example.org
EOF

LOAD DATA を使って上記のファイルを users テーブルに読み込む。

hive> LOAD DATA LOCAL INPATH '/home/vagrant/users.csv' INTO TABLE users;
Loading data to table default.users
OK
Time taken: 0.916 seconds

テーブルの中身を確認するとデータが読み込まれていることが分かる。

hive> SELECT * FROM users;
OK
users.name  users.emails
Alice   ["alice@example.jp","alice@example.com"]
Bob ["bob@example.net","bob@example.org"]
Time taken: 0.095 seconds, Fetched: 2 row(s)

HDFS 上のファイルについても確認しておこう。

hive> SHOW CREATE TABLE users;
OK
createtab_stmt
CREATE TABLE `users`(
  `name` string, 
  `emails` array<string>)
ROW FORMAT SERDE 
  'org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe' 
WITH SERDEPROPERTIES ( 
  'colelction.delim'='$', 
  'field.delim'=',', 
  'serialization.format'=',') 
STORED AS INPUTFORMAT 
  'org.apache.hadoop.mapred.TextInputFormat' 
OUTPUTFORMAT 
  'org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat'
LOCATION
  'hdfs://master:9000/user/hive/warehouse/users'
TBLPROPERTIES (
  'transient_lastDdlTime'='1514981348')
Time taken: 0.035 seconds, Fetched: 17 row(s)

すると、先ほどのファイルがそのまま保存されていることが分かる。

$ hdfs dfs -ls /user/hive/warehouse/users/
Found 1 items
-rwxrwxr-x   2 vagrant supergroup         77 2018-01-03 12:09 /user/hive/warehouse/users/users.csv
$ hdfs dfs -cat /user/hive/warehouse/users/users.csv
Alice,alice@example.jp$alice@example.com
Bob,bob@example.net$bob@example.org

いじょう。

まとめ

  • Apache Hive には一つのレコードで複数の値を扱うことができる ARRAY 型がある
  • ARRAY 型は JOIN を避けるためにジェイ・ウォークするときは有用と考えられる
  • ただし、操作方法にクセがあるので注意が必要となる
  • レコードを INSERT するときは SELECT と組み合わせる
  • データを集計するときは LITERAL VIEW と explode 関数を組み合わせる

プログラミング Hive

プログラミング Hive

  • 作者: Edward Capriolo,Dean Wampler,Jason Rutherglen,佐藤直生,嶋内翔,Sky株式会社玉川竜司
  • 出版社/メーカー: オライリージャパン
  • 発売日: 2013/06/15
  • メディア: 大型本
  • この商品を含むブログ (3件) を見る