HiveQL:查询

10 篇文章 0 订阅

HiveQL:查询


注:该博客只是记录我学习 《Hive编程指南》 第六章的笔记,没有实际的操作,如果想查看相关练习,请看我之前的练习。

SELECT …FROM 语句

常见查询见我的博客:Hive练习。

当用户选择的列是集合数据类型时,Hive会使用 JSON 语法应用输出。

集合中的字符串时加上引号的,而基本数据类型的 STRING 值是不加引号的。

获取数组内的数据:数组索引是基于 0 的,所以获取第一个元素因该是:arr[0]

获取MAP内的数据:map里的数据是键值对形式:所以获取 value 数据方式:map[key]

获取STRUCT内的数据:STRUCT 内的数据展现形式和MAP 类似,但是它的获取形式是用 .struct.key

LIMIT 语句

Hive中也可以用 limit 来限制返回的行数。

在Hive优化中,可以开启一个配置属性,当使用limit语句时,其可以对源数据进行抽样,

[外链图片转存失败(img-ZvU8fSoj-1568856582805)(D:\学习笔记\Hive编程指南截图\保存图片\Hive中的表\10limit优化.jpg)]

什么情况下Hive可以避免进行MapReduce

Hive对某些情况的查询可以不用使用MapReduce,也就是所谓的本地模式。

  1. Hive可以简单读取表对应的存储目录的文件然后输出格式化内容到控制台

    select * from tablename;

  2. 对于 where 语句中过滤条件只是分区字段这种情况(无论是否使用limit语句限制输出记录条数)

  3. 如果属性 hive.exec.mode.local.auto=true; 的话,Hive还会尝试使用本地模式执行其他操作。

WHERE 语句

Hive 和Oracle一致,不能在where条件中使用当前的输出的列别名,这个问题可以通过嵌套一层select语句来解决。

关于浮点数比较

实际上我们可以说0.2对于FLOAT类型是0.2000001,而对于DOUBLE类型是0.200000000001。这是因为一个8个字节的DOUBLE值具有更多的小数位(也就是小数点后的位数)。当表中的FLOAT值通过Hive转换为DOUBLE值时,其产生的DOUBLE值是0.200000100000,这个值实际要比0.200000000001大。这就是为什么这个查询结果像是使用了>=而不是>了。
这个问题并非仅仅存在于Hive 中或Java中(Hive是使用Java实现的)。而是所有使用IEEE标准进行浮点数编码的系统中存在的一个普遍的问题。
Hive中有两种规避这个问题的方法。
首先,如果我们是从TEXTFILE文本文件(请参考第15章内容)中读取数据的话,也就是目前为止我们所假定使用的存储格式,那么Hive会从数据文件中读取字符串“0.2”,然后将其转换为一个真实的数字。**我们可以在表模式中定义对应的字段类型为DOUBLE 而不是FLOAT。这样我们就可以对deductions【‘Federal Taxesl这个DOUBLE值和0.2这个DOUBLE值进行比较。**不过,这种变化会增加我们查询时所需的内存消耗。同时,如果存储格式是二进制文件格式(如SEQUENCEFILE(第15章将会进行讨论))的话,我们也不能简单地进行这样的改变。
**第2个规避方案是显式地指出0.2为FLOAT类型的。**Java中有一个很好的方式能够达到这个目的:只需要在数值末尾加上字母F或(例如,0.2f)。不幸的是,Hive并不支持这种语法,这里我们必须使用cast操作符。
下面这个是修改后的查询语句,其将0.2类型转换为FLOAT类型了。通过这个修改,返回结果是符合预期的:
hive>SELECT name,salary,deductionst’Federal Taxes’】FRoM employees >WHERE deductionst’Federal Taxes’】>cast(0.2 AS FLOAT);Boss Man200000.00.3 Y Fred Finance 150000.00.3 注意cast操作符内部的语法:数值ASFLOAT。
实际上,还有第3种解决方案,即:和钱相关的都避免使用浮点数。

对浮点数进行比较时,需要保持极端谨慎的态度。要避免任何从窄类型隐式转换到更广泛类型的操作。

join 语句

Hive支持通常的SQL JOIN 语句,但是只支持等值连接(tabA.col1=tabB.col1,不支持tabA.col2>=tabB.col2)。

不过Pig提供了一个交叉生产功能,所以在Pig中是可以使用非等值连接的。

**Hive目前还不支持在 ON 子句中的谓词间使用 OR。**目前我想到的解决方案是使用 union all 来处理。

join优化

当对 3 个或者更多个表进行 join 连接时,如果每个 on 子句都使用相同的连接键的话,那么只会产生一个 MapReduce.job

Hive 同时假定查询中最后一个表是最大的那个表。在对每行记录进行连接操作时,它会尝试将其他表缓存起来,然后扫描最后那个表进行计算。因此,用户需要保证连续查询中的表的大小从左到右是依次增加的。
幸运的是,用户并非总是要将最大的表放置在查询语句的最后面的。这是因为Hive还提供了一个“标记”机制来显式地告之查询优化器哪张表是大表,使用方式如下:

SELECT/*+STREAMTABLE(s)*/s.ymd,s.symbol,s.price close,d.dividend FROM stocks s JOIN dividends d ON s.ymd=d.ymd AND s.symbol=d.symbol WHERE s.symbol='AAPL'

现在Hive将会尝试将表stocks作为驱动表,即使其在查询中不是位于最后面的。

LEFT SEMI-JOIN

LEFT SEMI-JOIN = LEFT OUTER JOIN 时只选择左表的数据。

SEMI-JOIN 比通常 INNER JOIN 要更加高效,原因:对于左表中一条指定的记录,在右边中一旦找到匹配的记录,Hive就会立即停止扫描。

map-side JOIN

如果所有表中只有一张表是小表,那么可以在最大的表通过mapper的时候将小表完全放到内存中。Hive可以在map端执行连接过程(称为map-sideJOIN),这是因为Hive可以和内存中的小表进行逐一匹配,从而省略掉常规连接操作所需要的reduce过程。即使对于很小的数据集,这个优化也明显地要快于常规的连接操作。其不仅减少了reduce过程,而且有时还可以同时减少map过程的执行步骤。
在Hive v0.7之前的版本中,如果想使用这个优化,需要在查询语句中增加一个标记来进行触发。如下面的这个内连接(INNERJOIN)的例子所示:

SELECT/*+MAPJOIN(d)*/s.ymd,s.symbol,s.price_close,d.dividend FROM stockss JOIN dividends d ON s.ymd=d.ymd AND s.symbol=d.symbol WHEREs.symbol='AAPL'

从Hive v0.7版本开始,废弃了这种标记的方式,不过如果增加了这个标记同样是有效的。如果不加上这个标记,那么这时用户需要设置属性 hive.auto.convert.JOIN 的值为 true ,这样Hive才会在必要的时候启动这个优化。默认情况下这个属性的值是 false

set hive.auto.convert.join=trueSELECT s.ymd,s.symbol,s.price_close,d.dividend >FROM stocks s JOIN dividends d ON s.ymd=d.ymd ANDs.symbol=d.symbol >WHERE s.symbo1='AAPL'

需要注意的是,用户也可以配置能够使用这个优化的小表的大小。如下是这个属性的默认值(单位是字节):

hive.mapjoin.smalltable.filesize=25000000 

Hive 对于右外连接(RIGHT OUTERJOIN)和全外连接(FULLOUTERJOIN)不支持这个优化。
如果所有表中的数据是分桶的,那么对于大表,在特定的情况下同样可以使用这个优化,简单地说,表中的数据必须是按照ON语句中的键进行分桶的,而且其中一张表的分桶的个数必须是另一张表分桶个数的若干倍。当满足这些条件时,那么Hive可以在map阶段按照分桶数据进行连接。因此这种情况下,不需要先获取到表中所有的内容,之后才去和另一张表中每个分桶进行匹配连接。
不过,这个优化同样默认是没有开启的。需要设置参数 hive.optimize.bucketmapJOINtrue 才可以开启此优化:

set hive.optimize.bucketmapJoIN=true

如果所涉及的分桶表都具有相同的分桶数,而且数据是按照连接键或桶的键进行排序的,那么这时Hive可以执行一个更快的分类-合并连接(sort-mergeJOIN)。同样地,这个优化需要需要设置如下属性才能开启:

set hive.input.format=org.apache.hadoop.hive.ql.io.BucketizedHiveInputFormat;
set hive.optimize.bucketmapjoin=trueset hive.optimize.bucketmapjoin.sortedmerge=true

ORDER BY 和 SORT BY

Hive中order by 语句和会对查询结果集执行一个全局排序,这也就是说会有一个所有的数据都通过一个 reducer进行处理的过程。

sort by 只会在每个reducer 中对数据进行排序,也就是执行一个局部排序过程。

用户可以指定任意期望进行排序的字段,并可以在字段后面加上 ASC 关键字,表示升序,desc 关键字表示降序,这个和常见的关系型数据库一样。

order by 操作可能会导致运行时间过长,如果属性 hive.mapred.mode 的值是 strict 的话,那么 Hive 要求这样的语句必须加有 limit 语句进行限制,默认情况下,这个属性值是 nonstrict,也就是不会有这样的限制。

含有SORT BY 的 DISTRIBUTE BY

DISTRIBUTE BY 控制 map 的输出在reducer 中是如何划分的。

DISTRIBUTE BY 和 group by 在其控制着 reducer 是如何接受一行行数据进行处理这方面是类似的,而 sort by 则控制 reducer 内的数据是如何进行排序的。

Hive 要求DISTRIBUTE BY 语句要写在 DORT BY 语句之前。

CLUSTER BY

cluster by = DISTRIBUTE BY + SORT BY

使用DISTRIBUTE BY …SORT BY 语句或其简化版的CLUSTER BY 语句会剥夺SORT BY 的并行性,然而这样可以实现输出文件的数据全局排序。

xxx.by 之前的相关笔记:
  1. order by 会对输入做全局排序,因此只有一个 reducer,会导致当输入规模较大时,需要较长的计算时间。

  2. sort by 不是全局排序,其在数据进入 reducer 前完成排序。因此,如果用 sort by 进行排序,并且设置 mapred.reduce.tasks > 1,则 sort by 只保证每个 reducer 的输出有序,不保证全局有序。

  3. **distribute by(**字段)根据指定字段将数据分到不同的 reducer,分发算法是 hash 散列。

  4. Cluster by(字段) 除了具有 Distribute by 的功能外,还会对该字段进行排序。

如果 distribute 和 sort 的字段是同一个时,此时,cluster by = distribute by + sort by。

抽样查询

有时用户需要使用的是一个具有代表性的查询结果而不是全部的结果,Hive可以通过对表进行分桶抽样来满足这个需求。

我们可以使用 round() 函数进行抽样,这个函数会返回一个随机值。

SELECT * from numbers TABLESAMPLE(BUCKET 3 OUT OF10 ON rand())s;
SELECT * from numbers TABLESAMPLE(BUCKET 3 OUT OF 10 ON rand())s;
SELECT * from numbers TABLESAMPLE(BUCKET 3 OUT OF 10 oN rand())s;

分桶语句中的分母表示的是数据将会被散列的桶的个数,而分子表示将会选择同的个数。

SELECT * from numbers TABLESAMPLE(BUCKET 1 OUT OF 2 ON number)s;
SELECT * from numbers TABLESAMPLE(BUCKET 2 OUT OF 2 ON number)s;
数据块抽样

Hive提供了另外一种按照抽样百分比进行抽样的方式,这种是基于行数的,按照输入路径下的数据块百分比进行的抽样:

SELECT * FROM numbersflat TABLESAMPLE(0.1 PERCENT)s;

这种抽样方式不一定适用于所有的文件格式。另外,这种抽样的最小抽样单元是一个HDFS数据块。因此,如果表的数据大小 小于普通的块大小128MB的话,那么将会返回所有行。
基于百分比的抽样方式提供了一个变量,用于控制基于数据块的调优的种子信息:

<property>
    <name>hive.sample.seednumber</name> 
    <value>0</value> 
    <description>A number used for percentage sampling.By changing this number,user will change the subsets of data sampled.</description> 
</property>

UNION ALL

UNION 也可用于同一个源表的数据合并。从逻辑上讲,可以使用一个 SELECT 和 WHERE 语句来获得相同的结果。这个技术便于将一个长的复杂的WHERE语句分割成2个或多个UNION子查询。不过,除非源表建立了索引,否则,这个查询将会对同一份源数据进行多次拷贝分发。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值