Apache Hiveにハマり続けている毎日

私自身はHiveなんていらないんですよ...テキストファイルを自前でパースして集計したり統計取ったり,データを学習させるのが仕事なんですが,自分でプログラム組んだ方が早いので.
でも,どうしてもSQLライクなアクセス方法は,会社では捨てられないです.なので,入ってくるデータをせっせとHiveに入れているわけです.

そこで今日,ハマったのはたくさんあるのですが,大きくは二点.


シェルスクリプトとシェルコマンドラインの違いに気をつけよう

Sqoopで,MySQLからHiveにスキーマだけ転送しようとしました.
普段はシェルスクリプトの中に,次のようなコマンドを埋め込んでいました.

sqoop import --connect "jdbc:mysql://mysqlserver:3306/testDB" --username user1 --password pass1 --query "SELECT * FROM users WHERE \$CONDITIONS" --split-by id --target-dir users --hive-table users --hive-database test --hive-import --hive-drop-import-delims --fields-terminated-by \001

ところが,今回,単発作業だったので,bashの上で同じコマンドを打ち込みました.

$ sqoop import --connect "jdbc:mysql://mysqlserver:3306/testDB" --username user1 --password pass1 --query "SELECT * FROM users WHERE \$CONDITIONS" --split-by id --target-dir users --hive-table users --hive-database test --hive-import --hive-drop-import-delims --fields-terminated-by \001

結果を確認.

$ hive --database=test -e "select * from users limit 1"
id name
NULL NULL

入ってない...
ファイルを直接確認.

$ hdfs dfs -cat /user/hive/warehouse/test.db/users/* | head -1
1^Afoo

入っています.

分かったのは,データベースのプロパティを確認したとき.

$ hive --database=test -e "describe extended users"
(中略)
parameters:{serialization.format=0, line.delim= , field.delim=0})

CTRL-Aのはずのfield.delimが数字のゼロになっている...bashコマンドラインエスケープを解釈しちゃいました.
解釈しないように,シングルクオートするのが正解.

$ sqoop import --connect "jdbc:mysql://mysqlserver:3306/testDB" --username user1 --password pass1 --query "SELECT * FROM users WHERE \$CONDITIONS" --split-by id --target-dir users --hive-table users --hive-database test --hive-import --hive-drop-import-delims --fields-terminated-by '\001'

非表示文字なので,こう出るのが正解.

$ hive --database=stockgear -e "describe extended aishipr_items"|cat -t
(中略)
parameters:{serialization.format=^A, line.delim=^I , field.delim=^A})

catを通さないと,空白になる感じ.


予約語に気をつけよう

Hiveは思ったより予約語が多いです.

SqoopでMySQL(MariaDB)からHDFSに転送したデータを,Hiveのパーティションを追加したテーブルを作ろうと思ってこんなクエリを打ちました.

$ hive --database=test -e "LOAD DATA LOCAL INPATH '/tmp/test' INTO TABLE items PARTITION(dt='2014-06-07')" 

ところが,こんなエラーが出ます.

FAILED: ParseException line 1:43 mismatched input 'items' expecting Identifier near 'TABLE' in table name

Frequently Asked Questions | Treasure Data

The following are the reserved keywords in Hive:

TRUE, FALSE, ALL, AND, OR, NOT, LIKE, ASC, DESC, ORDER, BY, GROUP, WHERE,
FROM, AS, SELECT, DISTINCT, INSERT, OVERWRITE, OUTER, JOIN, LEFT, RIGHT,
FULL, ON, PARTITION, PARTITIONS, TABLE, TABLES, TBLPROPERTIES, SHOW, MSCK,
DIRECTORY, LOCAL, TRANSFORM, USING, CLUSTER, DISTRIBUTE, SORT, UNION, LOAD,
DATA, INPATH, IS, NULL, CREATE, EXTERNAL, ALTER, DESCRIBE, DROP, REANME, TO,
COMMENT, BOOLEAN, TINYINT, SMALLINT, INT, BIGINT, FLOAT, DOUBLE, DATE,
DATETIME, TIMESTAMP, STRING, BINARY, ARRAY, MAP, REDUCE, PARTITIONED,
CLUSTERED, SORTED, INTO, BUCKETS, ROW, FORMAT, DELIMITED, FIELDS, TERMINATED,
COLLECTION, ITEMS, KEYS, LINES, STORED, SEQUENCEFILE, TEXTFILE, INPUTFORMAT,
OUTPUTFORMAT, LOCATION, TABLESAMPLE, BUCKET, OUT, OF, CAST, ADD, REPLACE,
COLUMNS, RLIKE, REGEXP, TEMPORARY, FUNCTION, EXPLAIN, EXTENDED, SERDE, WITH,
SERDEPROPERTIES, LIMIT, SET, TBLPROPERTIES

itemsなんて,どんなデータベースでも出そうな単語が予約語になってました...
解決方法は,

If you encounter a reserved keyword issue in your query, please wrap the infringing keyword with ‘`’ as shown below.

SELECT `keyword_column`, COUNT(1) AS cnt FROM table_name GROUP BY `keyword_column`

とのこと.

$ hive --database=test -e "LOAD DATA LOCAL INPATH '/tmp/test' INTO TABLE \`items\` PARTITION(dt='2014-06-07')" 

こんな感じで解決出来ました.これもbashコマンドラインと,スクリプトエスケープキャラクタの個数なんかが変わるので,気をつけて下さい.