SQL入门

一、 sql语句的执行顺序

  1、 sql语句一共分为11步,如下所标注的那样,最先执行的总是FROM操作,最后执行的是LIMIT操作。

 

  2、 其中每一个操作都会产生一张虚拟的表,这个虚拟的表作为一个处理的输入,只是这些虚拟的表对用户来说是透明的,就只有最后一个虚拟的表才会被作为结果返回。

(8) SELECT (9) DISTINCT <select_list>

(1) FROM <left_table>

(3) <join_type> JOIN <right_table>

(2) ON <join_condition>

(4) WHERE <where_condition>

(5) GROUP BY <group_by_list>

(6) WITH {CUBE|ROLLUP}

(7) HAVING <having_condition>

(10) ORDER BY <order_by_list>

(11) LIMIT <limit_number>

    Ps: (1)、(2)、(3)、(4)、(5)…… sql的执行顺序

    建议:使用sql的时候,尽量加上limit 10;

 

  3、 下面我们来具体分析一下查询处理的每一个阶段

1). FORM: 对FROM的左边的表和右边的表计算笛卡尔积。产生虚表VT1

2). ON: 对虚表VT1进行ON筛选,只有那些符合<join-condition>的行才会被记录在虚表VT2中。

3). JOIN: 如果指定了OUTER JOIN(比如left join、 right join),那么保留表中未匹配的行就会作为外部行添加到虚拟表VT2中,产生虚拟表VT3, rug from子句中包含两个以上的表的话,那么就会对上一个join连接产生的结果VT3和下一个表重复执行步骤1~3这三个步骤,一直到处理完所有的表为止。

4). WHERE: 对虚拟表VT3进行WHERE条件过滤。只有符合<where-condition>的记录才会被插入到虚拟表VT4中。

5). GROUP BY: 根据group by子句中的列,对VT4中的记录进行分组操作,产生VT5.

6). CUBE | ROLLUP: 对表VT5进行cube或者rollup操作,产生表VT6.

7). HAVING: 对虚拟表VT6应用having过滤,只有符合<having-condition>的记录才会被 插入到虚拟表VT7中。

8). SELECT: 执行select操作,选择指定的列,插入到虚拟表VT8中。

9). DISTINCT: 对VT8中的记录进行去重。产生虚拟表VT9.

10). ORDER BY: 将虚拟表VT9中的记录按照<order_by_list>进行排序操作,产生虚拟表VT10.

11). LIMIT:取出指定行的记录,产生虚拟表VT11, 并将结果返回。

 

二、 行转列(根据主键,进行多行合并一列)

使用函数:concat_ws(',',collect_set(column))

说明collect_list不去重,collect_set去重。column的数据类型要求是String类型。concat_ws是带分隔符的字符串连接函数

eg: 如表:t_row_to_column数据如下,根据id,将要对tag_new进行合并

hive(default)> select * from t_row_to_column;

id tag_new

212121 1

212121 12

212121 15

212121 1

212121 12

212121 15

Time taken: 0.059 seconds, Fetched: 6 row(s)

 

  SQL语句1:

Select id,concat_ws(‘,’,collect_set(tag_new)) as tag_col from t_row_to_column group by id;

 

结果如下:

id tag_col

212121 1,12,15

 

  SQL语句2:

select id,concat_ws(‘,’,collect_list(tag_new)) as tag_col from t_row_to_column group by id;

 

结果如下:

id tag_col

212121 1,12,15,1,12,15

  

三、 列转行(对某列拆分,一列拆多行)

使用函数:lateral view explode(split(column,’,’)) xxx xxx(自定义名称)代表虚拟表,不能缺少

eg: 如表:t_column_to_row 数据如下,将要tag列进行拆分

hive(default)> select * from t_column_to_row;

id tag

212121 9,12

212122 12,15

212123 12,15

212124 1,12,15

212125 1,12

Time taken: 0.067 seconds, Fetched: 6 row(s)

 

SQL语句:

select id,tag,tag_new 
  from t_column_to_row lateral
    view explode(split(tag,’,’)) num as tag_new
    where id = 212121;

 

 结果如下:

id tag tag_new

212121 9,12    9

212121 9,12    12

 

四、 分组获取top N

使用函数:row_number

eg: 如表:t_select_rownumber 数据如下,根据deptid分组,显示每个部门的工资情况

hive(default)> select * from t_select_rownumber;

empid       deptid      visit

1           10          5500.00

2           10          4500.00

3           20          1900.00

4           20          4800.00

5           40          6500.00

6           40          14500.00

7           40          44500.00

8           50          6500.00

9           50          7500.00

    Time taken: 0.061 seconds, Fetched: 6 row(s)

 

 

  SQL语句:

select *, row_number() over (partition by deptid order by visit desc) rank from t_select_rownumber;

select *, row_number() over (partition by 客户号 oder by 日期 desc) ranknum from table_name;

 

   结果如下:

empid       deptid      visit                                  rank

1           10          5500.00                                 1

2           10          4500.00                                 2

4           20          4800.00                                 1

3           20          1900.00                                 2

7           40          44500.00                                1

6           40          14500.00                                2

5           40          6500.00                                 3

9           50          7500.00                                 1

8           50          6500.00                                 2

 

  eg: 如表:t_select_rownumber 数据如下,根据deptid分组,显示每个部门的最高工资

hive(default)> select * from t_select_rownumber;

 

empid       deptid      visit

1           10          5500.00

2           10          4500.00

3           20          1900.00

4           20          4800.00

5           40          6500.00

6           40          14500.00

7           40          44500.00

8           50          6500.00

9           50          7500.00

    Time taken: 0.061 seconds, Fetched: 6 row(s)

 

  SQL语句:

select *, row_number() over (partition by deptid order by visit desc) rank from t_select_rownumber where rank = 1;

 

结果如下:

empid       deptid      visit                                  rank

1           10          5500.00                                 1

4           20          4800.00                                 1

7           40          44500.00                                1

9           50          7500.00                                 1

 

五、 级联求和

 

要求:

有如下访客访问次数统计表 t_access_times

访客

月份

访问次数

A

2015-01

5

A

2015-01

15

B

2015-01

5

A

2015-01

8

B

2015-01

25

A

2015-01

5

A

2015-02

4

A

2015-02

6

B

2015-02

10

B

2015-02

5

……

……

……

 

需要输出报表:t_access_times_accumulate

访客

月份

月访问总计

累计访问总计

A

2015-01

33

33

A

2015-02

10

43

…….

…….

…….

…….

B

2015-01

30

30

B

2015-02

15

45

…….

…….

…….

…….

 

难点在统计累计访问总计这个指标

 

思路:

第一步:先求每个访客用户月的总访问次数

select username,month,sum(visit) as visit from t_access_times group by username,month

  结果如下:

Username

Month

Visit

A

2015-01

33

A

2015-02

10

…….

…….

…….

B

2015-01

30

B

2015-02

15

…….

…….

…….

 

 

  第二步:将月总访问次数自己连接自己

select * from

(select username,month,sum(visit) as visit from t_access_times group by username,month) A

inner join

(select username,month,sum(visit) as visit from t_access_times group by username,month) B

on

username=B.username
where b.month <= a.month

 

  结果如下:

Username

Month

Visit

Username

Month

Visit

A

2015-01

33

A

2015-01

33

A

2015-01

33

A

2015-02

10

A

2015-02

10

A

2015-01

33

A

2015-02

10

A

2015-02

10

B

2015-01

30

B

2015-01

30

B

2015-01

30

B

2015-02

15

B

2015-02

15

B

2015-01

30

B

2015-02

15

B

2015-02

15

 

第三步:第三步,从上一步的结果中

进行分组查询,分组的字段是a.username a.month

求当月累计值:  b.month <= a.month的所有b.visit求和即可

有两个字段在分组函数group by里面,要想还有一个字段展示并且不报错,需要使用一个聚合函数处理这个字段max(A.visit)

select A.username,A.month,max(A.visit) as visit,sum(B.visit) as accumulate

from

(select username,month,sum(visit) as visit from t_access_times group by username,month) A

inner join

(select username,month,sum(visit) as visit from t_access_times group by username,month) B

on

A.username=B.username

where B.month <= A.month

group by A.username,A.month

order by A.username,A.month;

 

 

六、 Join

 

  1、left join on 之后and 和 where 的区别

 

众所周知,数据库的表都是单独存在的,但是当我们进行联合查询(多表查询)时,我们获得数据库返回的值时就好像在一张表里一样,这是因为在进行联合查询时数据库会生成一个临时表返回给我们所想要的数据信息,这时我们都是通过LEFT JOIN 等语句进行相关联,并且我们也会为我们所想查询的数据进行一个筛选,这时我们就会用到过滤语句。

  LEFT JOIN ON WHERE:在临时表生成后,再对临时表的数据进行过滤,再返回左表。

  LEFT JOIN ON AND:在临时表生成的过程时,ON中的条件不管是否为真,都将返回左表。

  例如:

      表1.id  表1.value      表2.value  表2.name

         1     100        100      开心

       2     200        200     很开心

       3       300        300      超级开心

       4     400        400      无敌开心

SQL语句如下:

  

[1] SELECT * FROM 表1 LEFT JOIN 表2 ON (表1.value = 表2.value) WHERE 表2.name = 开心

[2] SELECT * FROM 表1 LEFT JOIN 表2 ON (表1.value = 表2.value) and 表2.name = 开心

 

当执行[1]的时候得:

      表1.id  表1.value      表2.value  表2.name

         1     100        100      开心

当执行[2]的时候得:

      表1.id  表1.value      表2.value  表2.name

         1     100        100      开心

       2     200        NULL      NULL

       3       300        NULL      NULL

       4     400        NULL      NULL

同理,RIGHT JOIN和FULL JOIN都具备这个特性,注意,INNER JOIN不具备这个特性。

 

 

  2、join前尽量减少重复关联键

join时,最好对on条件中的字段进行distinct。原因:a表的gid有3条重复的,b表只有一条和其相等,join的时候,会出现3条记录。如下:

select a.gid,b.gid from a join b on a.gid=b.gid;

 

a.gid     b.gid

1ab        1ab

1ab        1ab

1ab        1ab

 

3、join时,将重复关联键少的表放在join关键字的左边,做关联时可以提高join的效率

 

4、 join图解

原始数据:

id name       id  name

1  Pirate     1   Rutabaga

2  Monkey     2   Pirate

3  Ninja      3   Darth Vader

4  Spaghetti  4   Ninja

    

左连接

SQL语句:

SELECT * FROM TableA

LEFT JOIN TableB

ON TableA.name = TableB.name

结果如下: 

id  name       id    name

1   Pirate     2     Pirate

2   Monkey     null  null

3   Ninja      4     Ninja

4   Spaghetti  null  null

Ps:左连接(left join)生成表A的所有记录,包括在表B里匹配的记录。如果表B里没有匹配的,右边将是null。(如下图)

 

    

 

七、 case when then else end

 

select 字段1, 字段2,       

    case 字段3     

    when 值1 then 新值       

    when 值2 then 新值       

    else 新值

    end as 重新命名字段3的名字       

from table      

where ……      

order by ……

 

  eg1:

select id,

   case sex

        when '1' then ''

        when '2' then ''

   else '其他'

end,

months

    from table_name;

case的另一种用法:case后可无对象,而在when后添加条件判断语句,如,

case when 字段1='a' then true else false end;

 

 

select 字段1, 字段2,       

    case    

    when 条件1 then 值1       

    when 条件2 then 值2       

    else 字段3

    end as 重新命名字段3的名字       

from table      

where ……      

order by ……

  eg2:

select id,

   case

        when orgname like '%营销%' then '营销'

        when orgname like '%物业%' then '物业'

   else '其他'

end as org2,

   case

when length(trim(jobgrade))=2 and  trim(jobgrade) between 'G4' and 'G9' then 'G4-G9'

     when length(trim(jobgrade))=3 and  trim(jobgrade) between 'G10' and 'G13' then 'G10-G13'

     when length(trim(jobgrade))=3 and  trim(jobgrade) between 'G14' and 'G16' then 'G14-G16'

     when length(trim(jobgrade))=3 and  trim(jobgrade) > 'G16' then 'G16以上'

  else '其他'

end as grade,

months

  from table_name;

 

 

  eg3:

select

guid,

CONCAT('os_',os_id) AS os,

case when (country_id = '' or country_id = 'NULL' or country_id isnull)

            and (province_id ='' or province_id = 'NULL' or province_id is null)

            and (city_id = ''or city_id = 'NULL' or city_id is null)

        then ''

     when (country_id = '' orcountry_id = 'NULL' or country_id is null)

            and (province_id<> '' or province_id <> 'NULL' or province_id is not null orcity_id <> '' or city_id <> 'NULL' or city_id is not null)

        then '1'

     else country_id end as ascountry_id,

city_id,

CONCAT('brand_',b.brand_id) AS brand,

(CASE connection_type WHEN '2' THEN CONCAT('carrier_','wifi') ELSECONCAT('carrier_',c.element_id) END) AS carrier,

uid,

SUM(CASE WHEN logtype = '1' THEN 1 ELSE 0 END) AS imp_pv,

SUM(CASE WHEN logtype = '2' THEN 1 ELSE 0 END) AS clk_pv

from table_name;

 

 

  eg4:

select managecom,  

       subtype,  

       count(*) loadsucc,  

       sum(case when state in  ('4', '5', '6', '7', '8', '9') then 1 else 0 end) recogsucc,  

       sum(case when state in  ('3', '12', '13') then 1 else 0 end) recogfail,  

       sum(case when state in  ('1', '2') then 1 else 0 end) waitrecog  

  from table_name

 

 

八、 group by与聚合函数

 

  "Group By"从字面意义上理解就是根据"By"指定的规则对数据进行分组,所谓的分组就是将一个"数据集"划分成若干个"小区域",然后针对若干个"小区域"进行数据处理。

  大部分数据库都支持的5个聚类函数

聚合函数   作用

sum(列名)     求和   

max(列名)     最大值   

min(列名)     最小值   

avg(列名)     平均值

count(列名) 统计记录数

 

  eg:

name        class      sex              score

张1         1班        男                60

张2         1班        女                70

张3         1班        男                80

张4         1班        女                80

张5         2班        男                70

张6         2班        女                60

张7         2班        男                80

 

sql语句:

select class,sex,AVG(score) as avgscore

from test

group class,sex

 

   结果如下:

class       sex              avgscore

1班        男                70

1班        女                75

2班        男                75

2班        女                60

 

九、 判断空与非空

 

判断某个字段非空时的四种情况(判断非空时,使用and):

xxxx != '' and xxx is not null and xxx != ' ' and trim(xxx) != ''

判断某个字段是空的四种情况(判断是空时,使用or):

xxxx = '' or xxx is null or xxx = ' ' or trim(xxx) = ''

 

  

十、 创建索引

 

1. 不要频繁更新的字段上建索引

2. 索引列通常用来做join

   常常被用来join的字段,建索引可以提高join的性能。(如: 外键)

3. 索引列经常被频繁使用在where语句中

4. 批量导入数据需删除索引

   导入大量数据时,先删除索引,等数据导完后再重建。这样速度会更快。

 

  创建索引:

CREATE INDEX index_name ON table_name (column_name);

 

  删除索引:

DROP INDEX index_name;

 

posted @ 2018-05-12 10:13  宇宙超人  阅读(482)  评论(0编辑  收藏  举报