简介
Hive 是基于Hadoop 构建的一套数据仓库分析系统,它提供了丰富的SQL查询方式来分析存储在Hadoop 分布式文件系统中的数据,可以将结构化的数据文件映射为一张数据库表,并提供完整的SQL查询功能,可以将SQL语句转换为MapReduce任务进行运行,通过自己的SQL 去查询分析需要的内容,这套SQL 简称Hive SQL,使不熟悉mapreduce 的用户很方便的利用SQL 语言查询,汇总,分析数据。而mapreduce开发人员可以把己写的mapper和reducer 作为插件来支持Hive 做更复杂的数据分析。
它与关系型数据库的SQL 略有不同,但支持了绝大多数的语句如DDL(data definition language)、DML(Data Manipulation Language) 以及常见的聚合函数、连接查询、条件查询。HIVE不适合用于联机事务处理,也不提供实时查询功能。它最适合应用在基于大量不可变数据的批处理作业。
HIVE的特点:可伸缩(在Hadoop的集群上动态的添加设备),可扩展,容错,输入格式的松散耦合。
然后存储什么的不用说了,在hive简介里说的够多了,而且只要你熟悉一种数据库的话,大部分东西都是一样的,比如Primary Type之类的,大同小异。我们聊点儿不一样的。
1. 复杂类型Complex Type
复杂类型可以由初始类型(Primary Type)构建,也可以通过其他的复合类型构建,比如:
- Structs: the elements within the type can be accessed using the DOT (.) notation. For example, for a column c of type STRUCT {a INT; b INT}, the a field is accessed by the expression c.a
没什么好解释的,点操作来访问属性值
- Maps (key-value tuples): The elements are accessed using ['element name'] notation. For example in a map M comprising of a mapping from 'group' -> gid the gid value can be accessed using M['group']
映射取值的时候用M[],中括号。
- Arrays (indexable lists): The elements in the array have to be in the same type. Elements can be accessed using the [n] notation where n is an index (zero-based) into the array. For example, for an array A having the elements ['a', 'b', 'c'], A[1] retruns 'b'.
下标也是从0开始的数组,但是数组中的元素必须为同一类型。
2. 内置运算与操作函数
这种东西每个数据库都有,我搞个表格放在这里,供大家查阅。图像显示的太小,可将网页放大再看~
3. Hive SQL的功能
Hive SQL提供了类似于SQL的操作,它的操作对象为表和分区,包括:
- 可以用WHERE语句从表中过滤出行
- 可以用SELECT语句从表中选出特定的列
- 可以连接两个表
- 能够计算在表中已经排序好的列集合
- 能够将查询结果储存在另外一个表中
- 能够将一个表的内容下载到本地文件夹中
- 能够将查询结果存在HDFS路径下
- 能够操作表和分区:create,drop,alter
- 能够在map/reduce流程中插入脚本
4. 用法用例之:DDL操作
•建表 •删除表 •修改表结构 •创建/删除视图 •创建数据库 •显示命令
4.1 Creating Tables
CREATE TABLE page_view(viewTime INT, userid BIGINT,
page_url STRING, referrer_url STRING,
ip STRING COMMENT 'IP Address of the User')
COMMENT 'This is the page view table'
PARTITIONED BY(dt STRING, country STRING)
STORED AS SEQUENCEFILE;
这个例子中,表中的列被定义为特定的相应类型。Comment注释可以看作是对列层面的,也可以看作是整个表层面的。此外,PARTITIONED定义了分区列,分区列与表中的列是两回事,它并不是实际存在于表中。当用这种方式定义的时候,默认采用ASCII 001作为域界定符,而同时用新行作为行界定符。
当然,界定符还可以用参数来表示:
CREATE TABLE page_view(viewTime INT, userid BIGINT,
page_url STRING, referrer_url STRING,
ip STRING COMMENT 'IP Address of the User')
COMMENT 'This is the page view table'
PARTITIONED BY(dt STRING, country STRING)
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '1'
STORED AS SEQUENCEFILE;
如上表所示,现在的行界定符不可以被更改了,因为它采用的是Hadoop规定的定界符。
将表中的特定几行进行散列也是个不错的办法,这样会使得取样查询操作更加有效,但是如果不进行散列的话,上述操作也可以进行,但是就没有那么的高效。下面的语句在userid的列上进行了分桶散列:
CREATE TABLE page_view(viewTime INT, userid BIGINT,
page_url STRING, referrer_url STRING,
ip STRING COMMENT 'IP Address of the User')
COMMENT 'This is the page view table'
PARTITIONED BY(dt STRING, country STRING)
CLUSTERED BY(userid) SORTED BY(viewTime) INTO 32 BUCKETS
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '1'
COLLECTION ITEMS TERMINATED BY '2'
MAP KEYS TERMINATED BY '3'
STORED AS SEQUENCEFILE;
在上面这个语句中,通过对userid使用了32个桶散列来达到了聚集表的目的,每一个桶里的数据按照viewTime的升序进行排列。这样的组织结构能让使用者更好地在聚集列上进行取样操作。我们再来看看下面的例子:
CREATE TABLE page_view(viewTime INT, userid BIGINT,
page_url STRING, referrer_url STRING,
friends ARRAY<BIGINT>, properties MAP<STRING, STRING>
ip STRING COMMENT 'IP Address of the User')
COMMENT 'This is the page view table'
PARTITIONED BY(dt STRING, country STRING)
CLUSTERED BY(userid) SORTED BY(viewTime) INTO 32 BUCKETS
ROW FORMAT DELIMITED
FIELDS TERMINATED BY '1'
COLLECTION ITEMS TERMINATED BY '2'
MAP KEYS TERMINATED BY '3'
STORED AS SEQUENCEFILE;
通过这个例子我们再来总结一下Hive SQL中建表的几个注意事项:
- Comment可以用于列level也可以用于整个表level
- 分区与data不同,它并不真正的与data存在一块儿,它其实是一个目录
- cluster by规定了哪些列参与bucketing,并且规定了产生多少个bucket
- Delimited规定了hive表中的分隔符
- Stored as sequencefile指明了数据在hdfs中以二进制格式存储的
最后表名和列名是大小写敏感的
4.2 Browsing Tables and Partitions
SHOW TABLES; 列出数据仓库中所有已存在的表
SHOW TABLES 'page.*'; 列出所有以page开头的表
SHOW PARTITIONS page_view; 列出该表的素有分区,如果该表没有分区,那么就会报错
DESCRIBE page_view; 列出该表的列和列类型
DESCRIBE EXTENDED page_view; 列出该表的所有列和所有的属性,这会输出很多信息并且格式并不优美,常常用来调试
4.3 Altering Tables
ALTER TABLE old_table_name RENAME TO new_table_name;
对表重命名,如果新名称已经存在,则报错
ALTER TABLE old_table_name REPLACE COLUMNS (col1 TYPE, ...);
对已存在的表中的某一列进行重命名,确保类型的正确性
ALTER TABLE tab1 ADD COLUMNS (c1 INT COMMENT 'a new int column', c2 STRING DEFAULT 'def val');
对表新增一列
4.4 Dropping Tables and Partitions
DROP TABLE pv_users;
ALTER TABLE pv_users DROP PARTITION (ds='2008-08-08')
5. 加载数据
有很多种办法能够将数据加载进Hive表中,用户可以创建一个external table来指向HDFS内的某一特定的位置,在这种用法中用户可以通过HDFS中的put和copy命令,将一份文件拷贝到一个特定的位置,同时也能够创建一个指向该位置的表,该表中的所有相关信息必须和拷贝的文件保持一致。一旦拷贝结束后,用户可以转移这些数据并且可以将这些数据插入到别的Hive表中。例如:如果文件/tmp/pv_2008-06-08.txt 包含以逗号作为分隔符的page view(served on 2008-6-8),那么这个page view就要采用合适的partition策略来加载到page_view表中:
CREATE EXTERNAL TABLE page_view_stg(viewTime INT, userid BIGINT,
page_url STRING, referrer_url STRING,
ip STRING COMMENT 'IP Address of the User',
country STRING COMMENT 'country of origination')
COMMENT 'This is the staging page view table'
ROW FORMAT DELIMITED FIELDS TERMINATED BY '44' LINES TERMINATED BY '12'
STORED AS TEXTFILE
LOCATION '/user/data/staging/page_view';
hadoop dfs -put /tmp/pv_2008-06-08.txt /user/data/staging/page_view
FROM page_view_stg pvs
INSERT OVERWRITE TABLE page_view PARTITION(dt='2008-06-08', country='US')
SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, null, null, pvs.ip
WHERE pvs.country = 'US';
在上面这个例子中,nulls被插入到了目标表格中array和map类型的位置上,这个效果也可以用外部表来实现,前提是表中的格式符合要求。此外,Hive系统还支持从本地文件系统的文件中直接加载数据,前提是输入数据必须和表的格式相同。例如,如果文件/tmp/pv_2008-06-08_us.txt已经包含US数据(意思是us数据已经过滤好了),那么我们就没必要再像上面例子中那样做多余的过滤操作,而直接采用下面的形式即可:
LOAD DATA LOCAL INPATH /tmp/pv_2008-06-08_us.txt INTO TABLE page_view
PARTITION(date='2008-06-08', country='US')
在这个例子中输入文件/tmp/pv_2008-06-08_us.txt非常大,用户可能将数据进行分割进行并行计算(利用为Hive定制的第三方工具)。当文件在HDFS中就绪的时候,下面这条语句可以用来将数据加载到Hive标中。
LOAD DATA INPATH '/user/data/pv_2008-06-08_us.txt' INTO TABLE page_view
PARTITION(date='2008-06-08', country='US')
It is assumed that the array and map fields in the input.txt files are null fields for these examples.
6. 查询与插入数据
6.1 简单的查询
INSERT OVERWRITE TABLE user_active
SELECT user.*
FROM user
WHERE user.active = 1;
注意,与传统SQL不同的是,我们总是将结果插入到表中,稍后我们将展示怎样能够观察这些数据结果甚至是将它们存进本地文件。你同样可以通过Beeline或者Hive CLI运行下面的查询语句:
SELECT user.*
FROM user
WHERE user.active = 1;
此类操作将重写部分临时文件并且在客户端显示。
6.2 基于分区的查询
分区在查询中的使用自动取决于系统分区列的状态。例如,我们想得到域名 xyz.com 2008年3月的所有的page_view,可以使用如下语句:
INSERT OVERWRITE TABLE xyz_com_page_views
SELECT page_views.*
FROM page_views
WHERE page_views.date >= '2008-03-01' AND page_views.date <= '2008-03-31' AND
page_views.referrer_url like '%xyz.com';
注意到page_views.date在这里被使用到是因为上面的表中定义了: PARTITIONED BY(date DATETIME, country STRING) ;如果你将你的分区命名为别的名字,那么.date将不能正常的工作。
6.3 连接
为了获得2008年3月3号的人口统计数据(根据性别)的page_view表,用户需要连接page_view表和用户数据表中的userid那一列。这个目标可以通过如下语句达到:
INSERT OVERWRITE TABLE pv_users
SELECT pv.*, u.gender, u.age
FROM user u JOIN page_view pv ON (pv.userid = u.id)
WHERE pv.date = '2008-03-03';
为了实现外连接,用可以利用左连接,右连接或者全连接实现。例如,在上述情况下为了实现全外连接,对应的查询语句可以像下面那样写:
INSERT OVERWRITE TABLE pv_users
SELECT pv.*, u.gender, u.age
FROM user u FULL OUTER JOIN page_view pv ON (pv.userid = u.id)
WHERE pv.date = '2008-03-03';
如果要检查另外一个表中某个关键字的存在性,用户可以使用LEFT SEMI JOIN如下例所示
INSERT OVERWRITE TABLE pv_users
SELECT u.*
FROM user u LEFT SEMI JOIN page_view pv ON (pv.userid = u.id)
WHERE pv.date = '2008-03-03';
如果想连接多个表,可以使用如下语法:
INSERT OVERWRITE TABLE pv_friends
SELECT pv.*, u.gender, u.age, f.friends
FROM page_view pv JOIN user u ON (pv.userid = u.id) JOIN friend_list f ON (u.id = f.uid)
WHERE pv.date = '2008-03-03';
6.4 聚合
为了计算按所有用户的数量,我们可以这样写查询语句:
INSERT OVERWRITE TABLE pv_gender_sum
SELECT pv_users.gender, count (DISTINCT pv_users.userid)
FROM pv_users
GROUP BY pv_users.gender;
复合类型的聚合可以同时实现,两个聚合不能含有两个不同的列,也就是说下面的语句是可以实现的:
INSERT OVERWRITE TABLE pv_gender_agg
SELECT pv_users.gender, count(DISTINCT pv_users.userid), count(*), sum(DISTINCT pv_users.userid)
FROM pv_users
GROUP BY pv_users.gender;
然而这种操作是不允许的:
INSERT OVERWRITE TABLE pv_gender_agg
SELECT pv_users.gender, count(DISTINCT pv_users.userid), count(DISTINCT pv_users.ip)
FROM pv_users
GROUP BY pv_users.gender;
6.5 多表与文件插入
聚集或者简单的选择操作的结果在将来可能会送到多表中甚至会被送到HDFS文件中(可以用hdfs工具操作)。如果根据性别来分类,用户需要找到根据年龄来分类的page views,我们可以用下面的语句来实现:
FROM pv_users
INSERT OVERWRITE TABLE pv_gender_sum
SELECT pv_users.gender, count_distinct(pv_users.userid)
GROUP BY pv_users.gender
INSERT OVERWRITE DIRECTORY '/user/data/tmp/pv_age_sum'
SELECT pv_users.age, count_distinct(pv_users.userid)
GROUP BY pv_users.age;
第一条语句是将结果送到一个Hive表中,第二个结果是将结果送到HDFS文件中。
6.6 动态分区插入
在前面几个例子中,用户已经知道了哪个分区将会嵌入,而且在一条插入语句中只有一个分区可以被嵌入。如果你想加载进去多个分区的话,你必须使用multi-insert语句
FROM page_view_stg pvs
INSERT OVERWRITE TABLE page_view PARTITION(dt='2008-06-08', country='US')
SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, null, null, pvs.ip
WHERE pvs.country = 'US'
INSERT OVERWRITE TABLE page_view PARTITION(dt='2008-06-08', country='CA')
SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, null, null, pvs.ip
WHERE pvs.country = 'CA'
INSERT OVERWRITE TABLE page_view PARTITION(dt='2008-06-08', country='UK')
SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, null, null, pvs.ip
WHERE pvs.country = 'UK';
为了将某一天的所有国家分区数据全部加载,你必须要为每一个输入数据建一个插入语句。这样非常不方便因为你必须要提前知道输入数据中的国家列表,并且提前创建分区。如果某一天这个列表发生了变动,你必须改变你的插入DML语句,随之改变的,是创建的DDL语句。另外一个导致不方便的原因,是因为每一插入条语句要转化成一个MapReduce作业。
动态分区插入正是为了解决这个问题而设计的,它通过扫描全表输入数据,进而决定哪些分区被创建或者增加。这个新特征是在0.6.0的版本下新增的。在这种动态分区插入的方法中,输入的列数据被用来估计判断分区的哪一行应该被插入。如果某个分区没有被创建,那么就会自动创建该分区。利用这种特性,你只需要创建一条插入语句就能增加所有必要的分区。此外,既然只需要一个插入语句,就会只产生一个mapreduce作业,这将会显著的改善Hadoop集群的性能。
下面是一个用一条语句加载所有国家分区的例子:
FROM page_view_stg pvs
INSERT OVERWRITE TABLE page_view PARTITION(dt='2008-06-08', country)
SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url,
null, null, pvs.ip, pvs.country
关于multi-insert语句有一些语法上的差异性:
- 在PARTITION里出现了country,但是没有属性值和它进行关联。在这种情况下,country是一个动态分区列。另一方面,ds有一个关联值,意味着它是一个静态的分区列。如果一个列是动态分区列,那么这个列的值将从输入数据中获得。在目前的分区法则中,我们只允许动态分区列成为最后的列,因为分区列顺序决定着它的继承顺序(dt是根分区,而country就是子分区)。你不能像这样声明一个分区:(dt, country='US'),因为这意味着你需要更新所有的以任何日期开头并且以‘US’结尾的分区。
- 一个额外的列:pvs.country被增加到选中的区域。这是与动态分区列相关联的输入列。注意到你不需要为静态分区列增加一个输入列因为它的值在PARTITION中已经是确定了的。主要到动态分区列的值是根据排序来选择的,而不是列名称。同时作为选中语句的最后一列。
动态分区查询的语义:
- 当已经存在关于动态分区列的非空分区,(举个例子,country='CA'已经存在于根分区),原始的分区将会被覆盖如果动态分区插入方法插入了相同的值。这符合insert overwrite这一语义。尽管如此,如果分区数据CA不存在于输入数据,已存在的数据就不会被覆盖。
- 既然Hive的一个分区与HDFS的一个目录关联,那么分区值必须与HDFS的路径格式一致。任何一个字符在URL中都有特殊的意义。
- 如果输入列不是STRING类型的,那么它首先会被转化成STRING类型来构建HDFS路径。
- 如果输入的列数据为null或者一个空字符串,那么对应的行会被放到一个特殊的分区中,这个分区的名字被Hive变量hive.exec.default.partition.name决定。默认的名称是Hive_DEFAULT_PARTITION{}. 基本上来说这个分区包含了所有的“bad”行,也就是那些分区名称不合法的值。访问这些不合法的数据会得到警告:分区值可能会丢失或者被Hive_DEFAULT_PARTITION取代,如果你选择了Hive。JIRA HIVE-1309是一个用以解决“坏文件”的方式,它能够保持这些文件的分区值。
- 动态分区插入能够潜在的成为一个突起源,是因为它能够在短时间内产生巨大的分区。为了能够使你做好准备,我们定义了三个变量:
- hive.exec.max.dynamic.partitions.pernode (default value being 100)系统能够通过每个mapper或者reducer创建的最大动态分区数目。如果一个mapper或者一个reducer创建了超过阈值的分区,那么一个致命的错误将会被抛出,同时这个job也会被killed。
- hive.exec.max.dynamic.partitions (default value being 1000)定义了一个DML语句能够创建的最大动态分区。如果mapper或者reducer没有超过这个限制但是分区数已经超过了,那么在job的末尾,也就是在中间数据被转移到最终目的地之前会出现一个exception。
- hive.exec.max.created.files (default value being 100000)是mapper和reducer所能创建的最大文件数。不管何时只要一个新文件被创建,Hadoop计数器就会按照这个原则来更新。如果总数已经超过了默认值,会抛出致命错误,job会被停止。
- 我们要防止动态分区插入的另一个情况是,用户可能无意中指定所有的分区是动态分区,而不指定一个静态分区,而最初的意图是只覆盖一个根分区的子分区。我们定义了另一个参数hive.exec.dynamic.partition.mode=strict,以防止所有的动态分区情况。在严格的模式下,您必须指定至少一个静态分区。默认模式是严格的。此外,我们有一个参数hive.exec.dynamic.partition=true/false来控制是否允许动态分区。在Hive 0.9.0之前默认值是false,而在Hive 0.9.0之后就是true。
- 在Hive 0.6的版本中,将hive.merge.mapfiles设置为true或者将hive.merge.mapredfiles设置为true,动态分区插入失去作用,所以它内部关闭合并参数。在动态分区插入中的合并文件被支持在Hive 0.7中 (see JIRA HIVE-1307 for details).
最佳示例如下:
- 如上所述,一个mapper或者reducer可能会创建出很多的分区,一个致命的错误将会被抛出并且作业将被停止:
beeline> set hive.exec.dynamic.partition.mode=nonstrict;
beeline> FROM page_view_stg pvs
INSERT OVERWRITE TABLE page_view PARTITION(dt, country)
SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, null, null, pvs.ip,
from_unixtimestamp(pvs.viewTime, 'yyyy-MM-dd') ds, pvs.country;
...
2010-05-07 11:10:19,816 Stage-1 map = 0%, reduce = 0%
[Fatal Error] Operator FS_28 (id=41): fatal error. Killing the job.
Ended Job = job_201005052204_28178 with errors
...
一个mapper将会采用随机行的集合,这将很有可能导致不同的(dt, country)对会超过hive.exe.max.dynamic.partitions.pernode所规定的上限。一种曲线救国的方法是通过动态分区的列来组合这些行,同时将它们分布在动态分区将会被创建的reducers里面。在这个例子中不同的动态分区将会被显著的减少,上述的查询语句可以写成:
beeline> set hive.exec.dynamic.partition.mode=nonstrict;
beeline> FROM page_view_stg pvs
INSERT OVERWRITE TABLE page_view PARTITION(dt, country)
SELECT pvs.viewTime, pvs.userid, pvs.page_url, pvs.referrer_url, null, null, pvs.ip,
from_unixtimestamp(pvs.viewTime, 'yyyy-MM-dd') ds, pvs.country
DISTRIBUTE BY ds, country;
6.7 插入到本地文件
在某些情况下你想将输出写入到本地文件,所以你将数据导入到一张EXCEL表里,这样可以用如下方式来实现。
INSERT OVERWRITE LOCAL DIRECTORY '/tmp/pv_gender_sum'
SELECT pv_gender_sum.*
FROM pv_gender_sum;
6.8 取样
采样子句允许用户对数据的样本进行写查询,而不是对整个表进行查询。当前在按创建表语句的子句中的列中指定的列上进行了采样。在下面的例子中我们从32个buckets中选择了第三个bucket的pv_gender_sum表:上面的示例查询可以重写为
INSERT OVERWRITE TABLE pv_gender_sum_sample
SELECT pv_gender_sum.*
FROM pv_gender_sum TABLESAMPLE(BUCKET 3 OUT OF 32);
一般表取样语法可以写成这样:
TABLESAMPLE(BUCKET x OUT OF y)
y必须是在表创建期间特殊指明的一个表数目的divsor或者multiple。桶的选择决定于bucket_number模块Y是否等于X,所以在上面的例子tablesample条款如下:
TABLESAMPLE(BUCKET 3 OUT OF 16)
这条语句将会挑选出第三和第19个buckets。buckets的编号将从0开始。
另一方面这条语句将会挑选出第三个bucket:
TABLESAMPLE(BUCKET 3 OUT OF 64 ON userid)
我总感觉抽样这块儿有点儿混乱。
6.9 Union All
Hive语言同时也支持Union All,例如,我们假设有两个不同的表记录了某个用户发布的一个video和用户发表的评论,接下来的查询连接了一个用户的union all的一个结果,以便为所有发布的video和评论创建一个带有注释的流。
INSERT OVERWRITE TABLE actions_users
SELECT u.id, actions.date
FROM (
SELECT av.uid AS uid
FROM action_video av
WHERE av.date = '2008-06-03'
UNION ALL
SELECT ac.uid AS uid
FROM action_comment ac
WHERE ac.date = '2008-06-03'
) actions JOIN users u ON(u.id = actions.uid);
7.0 数组操作
表中的数组列可以按照以下方式进行操作:
CREATE TABLE array_table (int_array_column ARRAY<INT>);
假设pv.friends的类型是ARRAY<INT>(也就是说是一个整形数组), 用户可以通过数组下标获得任何一个元素,如下所示:
SELECT pv.friends[2]
FROM page_views pv;
上面的SELECT表达式取得了pv.friends的第三列。
用户同样也能够通过使用size函数取得数组的长度:
SELECT pv.userid, size(pv.friends)
FROM page_view pv;
7.1 Map(联合数组)操作
通过采用Hive语言原生支持的特点,用户还可以插入自己的自定义mapper和reducer的数据流。例如,为了运行一个自定义mapper脚本map_script和自定义reducer脚本reduce_script减速器-用户可以发出以下命令以改变条款嵌入映射和减速器脚本。
请注意,列将被转换为字符串,并在给用户脚本之前将其分隔为字符串,并且用户脚本的标准输出将被视为制表符分隔的字符串列。用户脚本可以输出调试信息到标准错误,这将在Hadoop的任务详细页面显示。
FROM (
FROM pv_users
MAP pv_users.userid, pv_users.date
USING 'map_script'
AS dt, uid
CLUSTER BY dt) map_output
INSERT OVERWRITE TABLE pv_users_reduced
REDUCE map_output.dt, map_output.uid
USING 'reduce_script'
AS date, count;
map取样的python文本
import sys
import datetime
for line in sys.stdin:
line = line.strip()
userid, unixtime = line.split('\t')
weekday = datetime.datetime.fromtimestamp(float(unixtime)).isoweekday()
print ','.join([userid, str(weekday)])
当然了,MAP和REDUCE对普遍的选择转移的过程来说,都是一种语法糖。内查找可以写成这样:
SELECT TRANSFORM(pv_users.userid, pv_users.date) USING 'map_script' AS dt,
uid CLUSTER BY dt FROM pv_users;
Schema-less map/reduce: 如果在USING map_script后面没有AS这个语句,Hive就会假设输出文本包含两个部分:key在第一个tab键前,value在这个tab键后面。注意到这跟指明了AS key, value的方式不一样,因为在那种情况下仅仅在第一个tab键和第二个tab键之间包含部分信息,如果有多个tab键的话。
使用这种方法,我们允许用户在不知道map输出的schema的情况下,移动旧的map/reduce文本。用户仍然需要知道这个reduce输出schema,因为它要跟表中的要插入的数据一一对应。
FROM (
FROM pv_users
MAP pv_users.userid, pv_users.date
USING 'map_script'
CLUSTER BY key) map_output
INSERT OVERWRITE TABLE pv_users_reduced
REDUCE map_output.dt, map_output.uid
USING 'reduce_script'
AS date, count;
Distribute By and Sort By: 与其指明cluster by,用户可以使用distribute by或者sort by,这样的话partition列和排序列就会不同,通常情况下partition列是排序列的前缀,但是这并不是必须的。
FROM (
FROM pv_users
MAP pv_users.userid, pv_users.date
USING 'map_script'
AS c1, c2, c3
DISTRIBUTE BY c2
SORT BY c2, c1) map_output
INSERT OVERWRITE TABLE pv_users_reduced
REDUCE map_output.c1, map_output.c2, map_output.c3
USING 'reduce_script'
AS date, count;
7.2 Co-Groups
在使用map/reduce的用户阵营中,cogroup是一个相当常见的操作,当用户需要从多个表中发送数据到一个reducer,这样的话行就会被按照表中特定列values的大小进行排列。通过使用UNION ALL操作和CLUSTER BY操作,这个目的可以通过下面的Hive查询语句实现。假设我们希望cogroup表actions_video和表action_comments中的行,在uid列上。然后将它们发送到reduce_script用户的reducer上,下面的语法可以用来达到目的。
FROM (
FROM (
FROM action_video av
SELECT av.uid AS uid, av.id AS id, av.date AS date
UNION ALL
FROM action_comment ac
SELECT ac.uid AS uid, ac.id AS id, ac.date AS date
) union_actions
SELECT union_actions.uid, union_actions.id, union_actions.date
CLUSTER BY union_actions.uid) map
INSERT OVERWRITE TABLE actions_reduced
SELECT TRANSFORM(map.uid, map.id, map.date) USING 'reduce_script' AS (uid, id, reduced_val);