Hive 数据类型
基本数据类型
数据类型 | 长度 | 例子 |
---|---|---|
tinyint | 1byte有符号整数 | 20 |
smallint | 2byte有符号整数 | 20 |
int | 4byte有符号整数 | 20 |
bigint | 8byte有符号整数 | 20 |
boolean | 布尔类型,true或者false | TRUE |
float | 单精度浮点数 | 3.14159 |
double | 双精度浮点数 | 3.14159 |
string | 字符序列。可以指定字符集。可以使用单引号或者双引号 | ‘now is the time’,’for all good men’ |
timestamp | 整数,浮点数或者字符串 | 1327882394(UNIX新纪元秒),1327882394.123456789(UNIX新纪元秒并跟随纳秒数)和’2016-03-13 16:23:30.123456789’(JDBC所兼容的java.sql.Timestamp时间格式) |
binary | 字节数组 | 请看后面的讨论 |
注:Hive会隐式地将类型转换为两个整型类型中值较大的那个类型;
集合数据类型
数据类型 | 描述 | 字面语法示例 |
---|---|---|
struct | 如果某个列的数据定义是struct{first String, last String} 取数方式: struct.first
|
struct('John', 'Doe') |
map | 一组“键值对”元组集合 取数方式: map['first']
|
map('first', 'John','last' , 'Doe') |
array | 每个元素都有一个编号,编号从零开始 取数方式: array[0]
|
array('John', 'Doe') |
示例(用类似JSON的结构描述):
{
"names": ["Smith" , "Jones"] , //列表Array, names[1]="Jones"
"deductions": { //键值Map, deductions[’Federal’]=0.2
"Federal": 0.2 ,
"State": 0.05,
"Insurance": 0.1
}
"address": { //结构Struct, address.city=”Chicago”
"street": "1 Michigan Ave." ,
"city": "Chicago" ,
"state": "IL" ,
"zip": 60600
}
}
在Hive中实际存储形式:
Smith_Jones,Federal:0.2_State:0.05_Insurance:0.1,Michigan Ave._Chicago_1L_60600
Jan_Ketty,Federal:0.2_State Taxes:0.05_Insurance:0.1,Guang dong._China_0.5L_60661
注:MAP,STRUCT和ARRAY里的元素间关系都可以用同一个字符表示,这里用“_”
取数:
select names[1], deductions['Federal'],address.city
from learn.employees;
------
OK
Jones 0.2 Chicago
Ketty 0.2 China
Time taken: 0.123 seconds, Fetched: 2 row(s)
文本文件数据编码
Hive中默认的记录和字段分隔符
分隔符 | 名称 | 说明 |
---|---|---|
\n | 换行符 | 对于文本文件而言,每一行是一条记录,因此换行符可以分割数据。 |
^A | <Ctrl>+A | 常用于分隔列,在CREATE TABLE语句中可以使用八进制编码\001 表示。 |
^B | <Ctrl>+B | 常用于分隔ARRAY与STRUCT元素,或用于MAP中键值对之间的分隔。CREATE TABLE语句中可以使用八进制编码\002 表示 |
^C | <Ctrl+C> | MAP中键值对的分隔。CREATE TABLE语句中可以使用八进制编码\003 表示 |
\t | 制表符 | 常用 |
, | 逗号 | 常用 |
在创建表格的时候,可规定分隔符:
create table employee(
name string,
salary float,
subordinates array<string>,
deductions map<string, float>,
address struct<street:string, city:string, state:string, zip:int>
)
row format delimited
fields terminated by '\001' //列分隔符
collection items terminated by '\002' //集合元素间的分隔符
map keys terminated by '\003' //map 键值对的分隔符
lines terminated by '\n' //行与行之间的分隔符,只支持'\n'
stored as textfile;
读时模式
Hive处理的数据是大数据,在保存表数据时不对数据进行校验,而是在读数据时校验,不符合格式的数据设置为NULL
;
读时模式的优点是,加载数据库快。
HiveQL:数据定义
Hive不支持行级插入操作、更新操作和删除操作,Hive也不支持事务。
HiveQL数据定义语言部分,用于创建、修改和删除数据库、表、试图、函数和索引
Hive中的数据库
创建数据库:
create database financials;
防止因“已存在”而抛出错误:
create database if not exits financials;
查看包含的数据库:
show databases;
---
default
financials
使用正则表达式匹配:
show databases like 'h.*';
---
human_resources
存储位置:
Hive会为每个数据库创建一个目录。数据库中的表将会以这个数据库目录子目录的形式存储。数据库所在目录位于属性hive.metastore.warehouse.dir
所指定的顶层目录之后。可通过如下命令修改:
hive> create database financials
> location '/my/preferred/directory'
描述信息:
hive> create database financials
> comment 'Holds all financial tables'
hive> describe database financials;
---
financials Holds all financial tables
hdfs://master-server/user/hive/warehouse/financials.db
设置为当前的工作数据库
hive> use financials;
显示正在使用的数据库
hive> set hive.cli.print.current.db=true;
hive (financials)> use default;
hive (default)> set hive.cli.print.current.db=false;
hive> ...
删除数据库
hive> drop database if exists financials; //if exists可选,避免抛出警告
注:默认情况下,Hive是不允许用户删除一个包含有表的数据库的;要么先删表,再删库;要么加上关键字 cascade
hive> drop database if exists financials cascade;
修改数据库
使用 alter database命令为某个数据库的dbproperties设置键-值对属性,来描述这个数据库的属性信息。其他信息不可更改。
hive> alter database financials set dbproperties ('edited-by' = 'Joe Dba')
创建表
在mydb库中增加employees表:
create table if not exists mydb.employees(
name string comment 'employee name'//comment 后面是属性
salary float comment 'employee salary'
subordinates array<string> comment 'names of subordinates'
deductions map<string, float> comment 'names,values'
address struct<street:string, city:string, state:string, zip:int> comment 'home address'
)
comment 'description of the table'
tblproperties ('creator'='me','created_at'='2012-01-02')//可通过键值对增加配置信息
location '/user/hive/warehouse/mydb.db/employees';
拷贝一张已经存在的表的表模式(而无需拷贝数据):
create table if not exists mydb.employees2
like mydb.employees;
呈现所有表
use mydb;
show tables;
----
employees
employees2
呈现所有表,不在当前数据库情况下:
show tables in mydb;
----
employees
employees2
过滤所需表
show tables '*.ees2'
----
employees2
查看表结构信息:
describe extended mydb.employees;
----
name string employee name
...
查看更详细信息
describe formatted mydb.employees;
----
name string employee name
...
只查看某一列的信息
described mydb.employees.salary;
---
salary float employees salary
管理表vs外部表
上面创建的是所谓的管理表,也被称为内部表;删除表的时候,会删除数据。
现在可以创建外部表,其可以读取所有位于/data/stocks
目录下的以逗号分隔的数据:
create external table if not exists stocks (
exchange string,
symbol string,
ymd string,
price_open float,
price_high float,
price_low float,
price_close float,
volume int,
price_adj_close float
)
row format delimited fields terminated by ','
location '/data/stocks'
external告诉hive这个表是外部的,location告诉hive数据位于哪个路径。这个表对相关文件无控制权,因此删除表并不会删除数据。
通过describe extended tablename
查看表信息的时候,可查看是管理表还是外部表:
...tableType:managed_table;
...tableType:external_table;
用户还可以对一张存在的表进行表结构的复制(不复制数据),这里external关键词决定了无论源表是管理表还是外部表,创建的表都是外部表。
create external table if not exists mydb.employees3
like mydb.employees
location '/path/to/data';
分区表、管理表
分区管理表具有明显的性能优势
先按照country再按照state来对数据进行分析:
create table if not exists mydb.employees(
name string comment 'employee name'
salary float comment 'employee salary'
subordinates array<string> comment 'names of subordinates'
deductions map<string, float> comment 'names,values'
address struct<street:string, city:string, state:string, zip:int> comment 'home address'
)
partitioned by (country string,state string);
之前的表存储在如下目录:
数据是按照如下子目录存储的:
...
.../employees/country=CA/state=AB
.../employees/country=CA/state=BC
...
.../employees/country=US/state=AL
.../employees/country=US/state=AK
...
如何在分区表查找信息:
select *
from employees
where country = 'US' and state = 'AL'
设置“strice”模式,对分区表查询的时候如果不对分区进行过滤,则任务不会提交:
set hive.mapred.mode = strict;
set hive.mapred.mode = nostrict;
查看所有分区
show partitions employees;
-----
country=CA/state=AB
country=CA/state=BC
...
show partitions employees partition (country='US');
-----
country=US/state=AL
country=US/state=AK
...
如何引用HOME环境变量:
load data local inpath '${env:HOME}/california-employees'
into table employees
patition (country = 'US', state = 'CA');
Hive会把${env:HOME}/california-employees
这个目录下面的文件将会拷贝到上述分区目录下。
外部分区表
外部表也可以使用分区,可首先创建分区,然后载入数据
create external table if not exists log_messages(
hms int,
severity string,
server string,
process_id int,
message string
)
partitoned by (year int, month int, day int)
row format delimited fields terminated by '\t';
//这里不要求location子句
可通过alter table
语句可以单独进行增加分区(同样适用于管理表)
alter table log_messages add partition(year=2012,month=1,day=2)
location 'hdfs://master_server/data/log_messages/2012/01/02';
删除表
drop table if exists employees
管理表:删除表的元信息和数据
外部表:删除表的元信息
修改表
修改表只会影响表的元信息,用户需自己确保数据符合元信息要求。
表重命名
alter table log_messages rename to logmsgs;
增加、修改、删除表分区
增加
alter table log_messages add if not exists
partition (year=2012, month=1, day = 1) location '/logs/2011/01/01'
partition (year=2012, month=1, day = 2) location '/logs/2011/01/02'
...
修改
alter table log_messages partition (year=2012, month=1, day = 2)
set location 's3n:/ourbucket/logs/2011/01/02'
删除
alter table log_messages drop if exists partition (year=2012, month=1, day = 2)
修改列信息
alter table log_messages
change columns hms hours_minutes_seconds int
comment 'the hours, minutes, and seconds part of the timestamp'
after severity;
重命名、改类型,改列顺序到severity后面(如果想移动到第一个位置则要用first
替代after severity
)
上述语句只修改元数据,用户需保证数据符合元数据的要求。
增加列
alter table log_messages add columns(
app_name string comment 'application name',
session_id long comment 'the current session id'
);
删除或替换列(等于整表替换为如下的列)
alter table log_messages replace columns(
hours_mins_secs int comment 'hour,minute,seconds from timestamp',
severity string comment 'The message severity',
message string comment 'The rest of the message'
)
修改表属性
alter table log_messages set tblproperties (
'notes' = 'the process id is no longer captured;this column is always NULL'
)
HiveQL:数据操作
向管理表中装载数据
load data local inpath '${env:HOME}/california-employees'
overwrite into table employees
patition (country='US', state='CA')
local
关键字说明指定的目录是本地的,如果没有local 关键字则应该是分布式文件系统中的路径。
overwrite
关键字说明先删除原先存在的数据,没有这个关键字则不删除。
通过查询语句向表中插入数据
insert overwrite table employees
partition (country = 'US', state = 'OR')
select * from staged_employees se
where se.cnty = 'US' and se.st = 'OR'
如果需要多种对应关系,无需写很多遍上面的语句,按如下格式插入数据效率会高(只需要扫描一遍):
from staged_employees se
insert overwrite table employees
partition (country = 'US', state = 'OR')
select * where se.cnty = 'US' and se.st = 'OR'
insert overwrite table employees
partition (country = 'US', state = 'CA')
select * where se.cnty = 'US' and se.st = 'CA'
insert overwrite table employees
partition (country = 'US', state = 'IL')
select * where se.cnty = 'US' and se.st = 'IL'
动态分区插入
insert overwrite table employees
partition (country, state)
select ...,se.cnty,se.st
from staged_employees se;
hive根据select语句中最后2列来确定分区字段country和state的值
混合使用动态和静态分区
insert overwrite table employees
partition (country = 'US',state)
select ...,se.cnty,se.st
from staged_employees se
where se.cnty = 'US'
country字段为静态,state是动态值,静态分区键必须在动态分区键之前。
动态分区属性
hive.exec.dynamic.partition 设置为true,表示开启动态分区功能
单个查询语句中创建表并加载数据
create table ca_employees
as select name, salary, address
from employees se
where se.state = 'CA'
创建表并载入数据,不能用于外部表。
导出数据
将所有的字段序列化为字符串写入到文件中
insert overwrite local directory '/tmp/ca_employees'
select name, salary, address
from employees
where state = 'CA';
输入到多个文件
from staged_employees se
insert overwrite directory '/tmp/or_employees'
select * where se.cty = 'US' and se.st = 'OR'
insert overwrite directory '/tmp/ca_employees'
select * where se.cty = 'US' and se.st = 'CA'
insert overwrite directory '/tmp/il_employees'
select * where se.cty = 'US' and se.st = 'IL'
视图
Hive先执行这个视图,然后使用这个结果进行余下后续的查询
create view if not exists shipments(time, part)
comment 'time and parts for shipments。'
tblproperties ('creator' = 'me')
as
select ...;
if not exists
和comment
是可选子句。
- 复制视图
create view shipments2 like shipments;
- 删除视图
drop view if exists shipments
- 显示视图清单
show tables
- 视图不能作为insert语句和load命令的目标表
- 视图是只读的
HiveQL 查询
select...from 语句
select e.name,e.salary
from employees e;
查询集合数据
查询数组
注:集合的字符串元素是加上引号的,而基本数据类型string的列值是不加引号的。
select name,subordinates
from employees;
---
John Doe ["Mary Smith","Todd Jones"]
Mary Smith ["Bill King"]
Todd Jones [""]
Bill King [""]
查询Map
select name,deductions
from employees;
---
John Doe {"Federal":0.2,"State":0.05,"Insurance":0.1}
Mary Smith {"Federal":0.2,"State":0.05,"Insurance":0.1}
Todd Jones {"Federal":0.15,"State":0.03,"Insurance":0.1}
Bill King {"Federal":0.15,"State":0.03,"Insurance":0.1}
查询Struct
select name,address
from employees;
---
John Doe {"street":"1 Michigan Ave.","City":"Chicago","state":"IL","zip":60600}
Mary Smith {"street":"30 Michigan Ave.","City":"Chicago","state":"IL","zip":60600}
Todd Jones {"street":"20 Michigan Ave.","City":"Chicago","state":"IL","zip":60600}
Bill King {"street":"8 Michigan Ave.","City":"Chicago","state":"IL","zip":60600}
引用集合数据类型中的元素
选取数组元素
注:string数据类型不再加引号
select name,subordinates[0]
from employees;
---
John Doe Mary Smith
Mary Smith Bill King
Todd Jones NULL
Bill King NULL
引用map元素
select name,deductions["State"]
from employees;
---
John Doe 0.05
Mary Smith 0.05
Todd Jones 0.03
Bill King 0.03
引用struct的元素,使用“点”符号
select name,address.city
from employees;
---
John Doe Chicago
Mary Smith Chicago
Todd Jones Oak Park
Bill King Obscuria
使用正则表达式制定列
select symbol,`price.*`
from stocks;
---
AAPL 195.69 197.88 194.0
AAPL 195.69 197.88 194.0
AAPL 195.69 197.88 194.0
使用列值进行计算
select upper(name),salary,deductions["Federal"]
,round(salary * (1-deductions["Federal"]))
from employees;
---
JOHN DOE 10000.0 0.2 8000
MARY SMITH 8000.0 0.2 6400
JOHN DOE 7000.0 0.15 5950
算数运算符
运算符 | 类型 | 描述 |
---|---|---|
A+B | 数值 | A和B相加 |
A-B | 数值 | A减去B |
A*B | 数值 | A和B相乘 |
A/B | 数值 | A除以B。如果不能整除,那么返回商数。 |
A%B | 数值 | A除以B的余数。 |
A&B | 数值 | A和B按位取与。 |
A|B | 数值 | A和B按位取或。 |
A^B | 数值 | A和B按位取异或。 |
~A | 数值 | A按位取反。 |
注:
- 两种类型数据计算:值范围较小的数据类型将转换为范围更广的数据类型。
- 需要注意数据溢出问题,数据溢出的计算结果不会自动转换为更广泛的数据类型
使用函数
数学函数
返回值类型 | 样式 | 描述 |
---|---|---|
bigint | round(double d,int n) | 返回保留n位小数的近似值 |
bigint | floor(double d) | 返回<=d的最大整数 |
bigint | ceil(double d) ceiling(double d) |
返回>=d的最小整数 |
double | rand() rand(INT seed) |
返回一个DOUBLE型的随机数,seed是随机因子 |
double | pow(double d,double p) | 计算d的p次幂 |
double | sqrt(double d) | 计算d的平方根 |
double | abs(double d) | 计算d的绝对值 |
float | sign(double d) | 如果d是正数,则返回 1.0; 如果d是负数,则返回-1.0; 否则返回0.0 |
聚合函数
对多行进行计算,返回一个结果值
返回值类型 | 样式 | 描述 |
---|---|---|
bigint | count(*) | 计算总行数,包括含有NULL值的行 |
bigint | count(expr) | 计算expr表达式的值非NULL的行数 |
bigint | count(distinct expr) | 计算expr表达式的值排重后非NULL的行数 |
double | sum(col) | 计算制定行的值的和 |
double | sum(distinct col) | 计算排重后值的和 |
double | avg(col) | 计算指定行的值的平均值 |
double | avg(distinct col) | 计算排重后值的平均值 |
double | min(col) | 计算指定行的最小值 |
double | max(col) | 计算指定行的最大值 |
double | percentile(bigint int_expr,p) | int_expr在p(范围是[0,1])的百分比数值点 |
array<double> | percentile(bigint int_expr,array(p1[,p2]...)) | int_expr在p(范围是[0,1])的百分比数值点 |
double | percentile_approx(bigint int_expr,p[,NB]) | int_expr在p(范围是[0,1])的百分比数值点,NB是控制精度(默认是10000) |
array<double> | percentile(bigint int_expr,array(p1[,p2]...)[, NB]) | int_expr在p(范围是[0,1])的百分比数值点,NB是控制精度(默认是10000) |
array | collect_set(col) | 返回集合col元素排重后的数组 |
注:目前不允许在一个查询语句中使用多于一个的函数(distinct ...
)表达式
表生成函数
将单列拓展成多列或者多行
返回值类型 | 样式 | 描述 |
---|---|---|
N行结果 | explode(array) | 返回0到多行结果,每行都对应输入的array数组中的一个元素 |
N行结果 | explode(map) | 返回0到多行结果,每行对应每个map键-值对 |
结果插入表中 | inline(array<struct[,struct]>) | 将结构体数组提取出来并插入到表中 |
tuple | json_tuple(string jsonstr,p1,p2,...,pn) | 接受多个标签名称,对输入的json字符串进行处理, |
tuple | parse_url_tuple(url,partname1,partname2,...) | 从url中解析N个部分信息:HOST,PATH,QUERY,REF,PROTOCOL,AUTHORITY,FILE,USERINFO,QUERY:<KEY_NAME>
|
N行结果 | stack(int n,col1,col2,...,colm) | 把M列转换成N行,每行有M/N个字段 |
注:explode不能和其他字段一起使用,比如select name,explode(map) from ee
其他内置函数
返回值类型 | 样式 | 描述 |
---|---|---|
type | cast(<expr> as <type>) | 将expr转换成type类型的,如果转换失败则返回null |
string | concat(str1,str2,...) | 将字符串拼接成一个字符串,例如concat('ab','cd')的结果是'abcd' |
string | concat_ws(separator,str1,str2,...) | 使用指定分隔符拼接字符串 |
int | find_in_set(s,string) | 返回以逗号分隔的字符串中s出现的位置 |
boolean | in | test in (val1,val2,...),其表示如果test等于后面任一值,则返回true |
int | length(str) | 计算字符串的长度 |
int | instr(str,substr) | str字符串中substr第一次出现的位置 |
int | locate(substr,str[,pos]) | 查找字符串str中的pos位置后字符串sbustr第一次出现的位置 |
string | lower(string) | 转换为小写字母 |
string | upper(string) | 转换为大写字母 |
string | regexp_replace(str,regex,replace) | 按照java正则表达式regex将字符串str中符合条件的部分替换成replacement |
string | repeat(str,int) | 重复输出n次字符串s |
string | reverse(str) | 反转字符串 |
array<array<string>> | sentences(str,str lang,str locale) | 将输入的字符串转换成句子数组,每个句子由一个单词数组构成 |
int | size(map<k.v>) | 返回map中元素的个数 |
array<string> | split(str,str pattern) | 按照正则表达式pattern分割字符串,以字符串数组的方式返回 |
map<str,str> | str_to_map(str,delim1,delim2) | 将字符串s按照指定分隔符转换成map |
string | substr(str,start_index,length) | 从start位置截取length长度的字符串 |
string | trim(str) rtrim ltrim |
将字符串空格去掉 |
时间函数
返回值类型 | 样式 | 描述 |
---|---|---|
string | from_unixtime(bigin T unixtime[, str format]) | 将unix时间戳转换成UTC时间,可通过format规定输出的时间格式 |
bigint | unix_timestamp() | 获取当前本地时区下的当前时间戳 |
bigint | unix_timestamp(str date, str pattern) | 将指定时间字符串格式字符串转换成unix时间戳:unix_timestamp('2009-01-01','yyyy-MM-dd')
|
timestamp | from_utc_timestamp(timestamp,timezone) | 视同输入UTC下的时间戳,返回指定时区的时间戳 |
timestamp | to_utc_timestamp(timestamp,timezone) | 视同输入指定时区的时间戳,返回UTC下的时间戳 |
string | to_date(str timestamp) | 返回时间字符串的日期部分 |
int | year(str date) | 返回年份 |
int | month(str date) | 返回月份 |
int | day(str date) | 返回天 |
int | hour(str date) | 返回小时 |
int | minute(str date) | 返回分钟 |
int | second(str date) | 返回秒数 |
int | weekofyear(str date) | 返回第几周 |
int | datediff(str enddate,str startdate) | 相差的天数 |
str | date_add(str startdate,int days) | 增加天数 |
str | date_sub(str startdate,int days) | 减去天数 |
LIMIT语句
select upper(name),salary,deductions["Federal"]
,round(salary * (1 - deductions["Federal"]))
from employees
limit 2;
列别名
select upper(name),salary,deductions["Federal"] as fed_taxes
,round(salary * (1 - deductions["Federal"])) as salary_minus_fed_taxes
from employees
limit 2;
嵌套select语句
from (
select upper(name),salary,deductions["Federal"] as fed_taxes
,round(salary * (1 - deductions["Federal"])) as salary_minus_fed_taxes
from employees
) e
select e.name,e.salary_minus_fed_taxes
where e.salary_minus_fed_taxes > 70000;
case...when...then...end 语句
select name,salary
case
when salary < 5000 then 'low'
when salary < 7000 then 'middle'
else 'high'
end as bracket
from employees
where语句
select upper(name),salary,deductions["Federal"] as fed_taxes
,round(salary * (1 - deductions["Federal"])) as salary_minus_fed_taxes
from employees
where round(salary * (1 - deductions["Federal"])) > 7000 //不可使用列别名
谓语操作符
运算符 | 数据类型 | 描述 |
---|---|---|
A = B | 基本类型 | 如果表达A等于表达B,结果TRUE ,否则FALSE。 |
A <=> B | 基本类型 | 如果A和B都为null则返回true,其他和(=)操作符结果一致。任一为null则结果为null |
A != B A <> B |
所有基本类型 | 如果A不等于表达式B表达返回TRUE ,否则FALSE。 |
A < B | 所有基本类型 | TRUE,如果表达式A小于表达式B,否则FALSE。 |
A <= B | 所有基本类型 | TRUE,如果表达式A小于或等于表达式B,否则FALSE。 |
A > B | 所有基本类型 | TRUE,如果表达式A大于表达式B,否则FALSE。 |
A >= B | 所有基本类型 | TRUE,如果表达式A大于或等于表达式B,否则FALSE。 |
A [NOT] between B and C | 如果表达式A大于或等于表达式B,小于或等于表达式C则返回true | |
A IS [NOT] NULL | 所有类型 | TRUE,如果表达式的计算结果为NULL,否则FALSE。 |
A [NOT] LIKE B | 字符串 | TRUE,如果字符串模式A匹配到B(简单正则表达式),否则FALSE。% 匹配任意数量字符,_ 匹配单个字符 |
A RLIKE B A REGEXP B |
字符串 | NULL,如果A或B为NULL;TRUE,如果A任何子字符串匹配Java正则表达式B;否则FALSE。 |
注:一般来说A和B中任一为null,则返回null
关于浮点数比较
select *
from employees
where deductions['Federal'] > 0.2;
---
john 0.2
boss 0.3
为什么会返回0.2呢,这是因为用户写的浮点数0.2,hive会默认保存为double类型“0.200000000001”,而deductions是float类型的,将隐式转换为double类型“0.200000100000”
如何避免呢:可以显示的指出0.2是float类型
select *
from employees
where deductions['Federal'] > cast(0.2 as float);
---
boss 0.3
Group by 语句
group by语句通常和聚合函数一起使用,对分组进行过滤
select year(ymd),avg(price_close)
from stocks
group by year(ymd)
having avg(price_close) > 50;
join 语句
select a.ymd, a.price_close, b.price_close
from stocks a
join stocks b on a.ymd = b.ymd
where a.symbol = 'AAPL' and b.symbol = 'AAPL'
注:sql不支持“非等值连接”,也不支持在on子句中使用or。
join优化
当连接3个或更多表时,Hive会嘉定最后一个表时最大的表,然后尝试将其它表缓存起来,然后扫面最后那个表进行计算。
可以显示的告诉Hive哪张表是达标:
select /*+STREAMTABLE(s)+*/a.ymd, a.price_close, b.price_close
from stocks a
join stocks b on a.ymd = b.ymd
where a.symbol = 'AAPL' and b.symbol = 'AAPL'
其他join
- left outer join
- outer join
- right outer join
- full outer join
注:where语句在连接操作执行后才会执行
left semi-join
比inner join更高效的查询方式,但是只能引用左边表的字段
select s.ymd, s.symbol, s.price_close
from stocks s
left semi join dividends d on s.ymd = d.ymd and s.symbol = d.symbol;
排序 order by 和 sort by
order by 全局排序
select s.ymd, s.symbol, s.price_close
from stocks s
order by s.ymd asc ,s.symbol desc
sort by 只在reducer内部排序,如果有多个reducer,则不保证输出的是整体有序的。
select s.ymd, s.symbol, s.price_close
from stocks s
sort by s.ymd asc ,s.symbol desc
使用distribute by来保证相同的数据会分发到同一个reducer中进行处理:
select s.ymd, s.symbol, s.price_close
from stocks s
distribute by s.symbol --同一个symbol放到同一个reducer中处理
sort by s.symbol, s.ymd desc
如果上面的语句没有要求按ymd 倒序排列,则可以用cluster by简化:
select s.ymd, s.symbol, s.price_close
from stocks s
cluster by s.symbol
类型转换
cast(value as TYPE)
抽样查询
分桶抽样
分子是要分为几桶,分子是取回的第几个桶,rand()表示随机排序(如果省略rand(),则返回的结果会始终一致)
select *
from numbers tablessample(bucket 1 out of 2 on rand()) s;
---
2
4
6
8
10
数据块抽样
select *
from numbersflat tablesample(0.1 percent) s;
union all 语句
将两个或多个表进行合并,但是要求对应的字段类型必须一致。
分析函数
分析函数用于计算基于组的某种聚合值,它和聚合函数的不同之处是:对于每个组返回多行,而聚合函数对于每个组只返回一行。
基本结构:
基础数据.png
分析函数() + over(partition by ** order by ** rows between ** and **)
SELECT cookieid,createtime,pv,
SUM(pv) OVER(PARTITION BY cookieid ORDER BY createtime) AS pv1, -- 默认为从起点到当前行
SUM(pv) OVER(PARTITION BY cookieid ORDER BY createtime ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS pv2, --从起点到当前行,结果同pv1
SUM(pv) OVER(PARTITION BY cookieid ORDER BY createtime ROWS BETWEEN 3 PRECEDING AND CURRENT ROW) AS pv3, --当前行+往前3行
SUM(pv) OVER(PARTITION BY cookieid ORDER BY createtime ROWS BETWEEN 3 PRECEDING AND 1 FOLLOWING) AS pv4, --当前行+往前3行+往后1行
SUM(pv) OVER(PARTITION BY cookieid ORDER BY createtime ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING) AS pv5 ---当前行+往后所有行
FROM test1;
-----
cookieid createtime pv pv1 pv2 pv3 pv4 pv5
a 2017-12-01 3 3 3 3 3 3
b 2017-12-00 3 3 3 3 3 3
cookie1 2017-12-10 1 1 1 1 6 26
cookie1 2017-12-11 5 6 6 6 13 25
cookie1 2017-12-12 7 13 13 13 16 20
cookie1 2017-12-13 3 16 16 16 18 13
cookie1 2017-12-14 2 18 18 17 21 10
cookie1 2017-12-15 4 22 22 16 20 8
cookie1 2017-12-16 4 26 26 13 13 4
cookie2 2017-12-12 7 7 7 7 13 14
cookie2 2017-12-16 6 13 13 13 14 7
cookie2 2017-12-24 1 14 14 14 14 1
cookie3 2017-12-22 5 5 5 5 5 5
行数控制(window子句):
- preceding:往前
- FOLLOWING:往后
CURRENT ROW:当前行
UNBOUNDED:无边界,UNBOUNDED PRECEDING 表示从最前面的起点开始, UNBOUNDED FOLLOWING:表示到最后面的终点
开窗函数
sum
sum()计算的是分区内排序后一个个叠加的值,和order by有关;如果没有order by,不仅分区内没有排序,sum()计算的pv也是整个分区的pv
select cookieid,createtime,pv,
sum(pv) over(PARTITION BY cookieid ORDER BY createtime) as pv1
FROM test1
max
返回最大值
min
返回最小值
ntile
NTILE(n),用于将分组数据按照顺序切分成n片,返回当前切片值
注1:如果切片不均匀,默认增加第一个切片的分布
注2:NTILE不支持ROWS BETWEEN
SELECT cookieid,createtime,pv,
NTILE(2) OVER(ORDER BY createtime) AS ntile1, --分组内将数据分成2片
NTILE(3) OVER(ORDER BY createtime) AS ntile2, --分组内将数据分成3片
NTILE(4) OVER(ORDER BY createtime) AS ntile3 --将所有数据分成4片
FROM test1
row_number
row_number() 从1开始,按照顺序,生成分组内记录的序列
类似:
- rank() 生成数据项在分组中的排名,排名相等会在名次中留下空位
- dense_rank() 生成数据项在分组中的排名,排名相等会在名次中不会留下空位
SELECT cookieid,createtime,pv,
rank() OVER(PARTITION BY cookieid ORDER BY pv desc) AS rank1,
dense_rank() OVER(PARTITION BY cookieid ORDER BY pv desc) AS d_rank2,
row_number() OVER(PARTITION BY cookieid ORDER BY pv DESC) AS rn3
FROM test1
cume_dist
返回:
比如,我们可以统计小于等于当前薪水的人数,所占总人数的比例
SELECT cookieid,createtime,pv,
round(cume_dist() OVER(ORDER BY pv),2) AS cd1,
round(cume_dist() OVER(PARTITION BY cookieid ORDER BY pv),2) AS cd2
FROM test1;
类似的:percent_rank,返回:
LAG 和 LEAD
LAG(col,n,DEFAULT) 用于统计窗口内往上第n行值,第一个参数为列名,第二个参数为往上第n行(可选,默认为1),第三个参数为默认值(当往上第n行为NULL时候,取默认值,如不指定,则为NULL)
LEAD 函数则与 LAG 相反: LEAD(col,n,DEFAULT) 用于统计窗口内往下第n行值
SELECT cookieid,createtime,pv,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
LAG(createtime,1,'1970-01-01') OVER(PARTITION BY cookieid ORDER BY createtime) AS lag1,
LAG(createtime,2) OVER(PARTITION BY cookieid ORDER BY createtime) AS lag2
FROM test1;
FIRST_VALUE 和 LAST_VALUE
FIRST_VALUE 取分组内排序后,截止到当前行,第一个值;
LAST_VALUE 函数则相反,取分组内排序后,截止到当前行,最后一个值。
SELECT cookieid,createtime,pv,
ROW_NUMBER() OVER(PARTITION BY cookieid ORDER BY createtime) AS rn,
FIRST_VALUE(pv) OVER(PARTITION BY cookieid ORDER BY createtime) AS first
FROM test1;
网友评论