SQL基础教程(2)

函数、谓词、CASE表达式

函数

先创建表和数据

create table SampleMath (
    m numeric (10, 3),
    n integer ,
    p integer
);

start transaction;
insert into SampleMath values (500, 0, null);
insert into SampleMath values (-180, 0, null);
insert into SampleMath values (null, null, null);
insert into SampleMath values (null, 7, 3);
insert into SampleMath values (null, 5, 2);
insert into SampleMath values (null, 4, null);
insert into SampleMath values (8, null, 3);
insert into SampleMath values (2.27, 1, null);
insert into SampleMath values (5.555, 2, null);
insert into SampleMath values (null, 1, null);
insert into SampleMath values (8.76, null, null);
commit;
abs(数值)

计算绝对值

select m, abs(m) as abs_col from SampleMath;
mod(被除数, 除数)

计算除法余数(求余)

select n, p, mod(n, p) as mod_col from SampleMath;
round(对象数值, 保留小数的位数)

四舍五入,如果指定位数为1,那么会对小数点第2位进行四舍五入处理

select m, n, round(m, n) as round_col from SampleMath;
字符串拼接函数

Mysql 中使用 concat, PostgreSql 中用 ||

select str1, str2, concat(str1, str2) as str_concat from SampleStr;
length函数

字符串长度,注意这里由于每个DBMS的计算方式不同,结果可能不一样,这涉及到字符串的字节

select str1, length(str1) as len_str from SampleStr;
lower(字符串)

小写转换,对应的upper是大写转换

select str1, lower(str1) as low_str from SampleStr where str1 in ('ABC', 'aBC', 'abc', '山田');
replace(对象字符串, 替换前的字符串, 替换后的字符串)
select str1, str2, str3, replace(str1, str2, str3) as rep_str from SampleStr;
substring(对象的字符串 from 截取的起始位置 for 截取的字符数)
select str1, substring(str1 from 3 for 2) as sub_str from SampleStr;
current_date 当前日期
select current_date;
current_time 当前时间
select current_time;
current_timestamp 当前日期和时间
select current_timestamp;
extract 截取日期元素

extract(日期元素 from 日期)

select current_timestamp,
       extract(year from current_timestamp) as year,
       extract(month from current_timestamp) as month,
       extract(day from current_timestamp) as day,
       extract(hour from current_timestamp) as hour,
       extract(minute from current_timestamp) as minute,
       extract(second from current_timestamp) as second;
cast 类型转换

case(转换前的值 as 想要转换的数据类型)

select cast('0001' as signed integer) as int_col;
select cast('2009-12-14' as date) as date_col;
coalesce 将NULL转换为其他值

coalesce(数据1, 数据2, 数据3...)
是sql特有的函数。该函数会返回可变参数中左侧开始第1个不是NULL的值。参数个数是可变的,因此可以根据需要无限增加。
在sql语句中将NULL转换为其他值时就会用到转换函数。

select coalesce(null, 1) as col_1,
       coalesce(null, 'test', null) as col_2,
       coalesce(null, null, '2009-11-01') as col_3;
       
select coalesce(str2, 'NULL') from SampleStr;
谓词
like 字符串的部分一致查询

查询结果有前方一致、中间一致和后方一致这三个概念

  • 前方一致:其实部分相同
  • 中间一致:存在相同即可,就是说包括前方一致和后方一致
  • 后方一致:末尾部分相同
-- 前方一致
select * from SampleLike where strcol like 'ddd%';

-- 中间一致
select * from SampleLike where strcol like '%ddd%';

-- 后方一致
select * from SampleLike where strcol like '%ddd';

% 代表 0字符以上的任意字符串
_ 代表 任意一个字符,有几个 _ 就只能有几个字符

select * from SampleLike where strcol like 'abc__';
select * from SampleLike where strcol like 'abc___';
between 范围查询

包含临界值

select product_name, sale_price from Product where sale_price between 100 and 1000;
is null 、 is not null

判断是否为null

select product_name, purchase_price from Product where purchase_price is null;
select product_name, purchase_price from Product where purchase_price is not null;
in -- or的简便用法

否定形式是 not in

select product_name, purchase_price from Product where purchase_price = 320 or purchase_price = 500 or purchase_price = 5000;

select product_name, purchase_price from Product where purchase_price in (320, 500, 5000);

select product_name, purchase_price from Product where purchase_price not in (320, 500, 5000);
使用子查询作为in谓词的参数

先创建一个商品商店的关联表

create table ShopProduct (
    shop_id varchar(4) not null ,
    shop_name varchar(200) not null ,
    product_id varchar(4) not null ,
    quantity integer not null ,
    primary key (shop_id, product_id)
);

start transaction ;
insert into ShopProduct values ('000A', '东京', '0001', 30);
insert into ShopProduct values ('000A', '东京', '0002', 50);
insert into ShopProduct values ('000A', '东京', '0003', 15);
insert into ShopProduct values ('000B', '名古屋', '0002', 30);
insert into ShopProduct values ('000B', '名古屋', '0003', 120);
insert into ShopProduct values ('000B', '名古屋', '0004', 20);
insert into ShopProduct values ('000B', '名古屋', '0006', 10);
insert into ShopProduct values ('000B', '名古屋', '0007', 40);
insert into ShopProduct values ('000C', '大阪', '0003', 20);
insert into ShopProduct values ('000C', '大阪', '0004', 50);
insert into ShopProduct values ('000C', '大阪', '0006', 90);
insert into ShopProduct values ('000C', '大阪', '0007', 70);
insert into ShopProduct values ('000D', '福冈', '0001', 100);
commit ;

找出在大阪销售的商品,这里就要用到子查询

select product_name, sale_price from Product where product_id in (select product_id from ShopProduct where shop_id = '000C');
exist 谓词

理论上都可以用 in 和 not in 来代替。
只有1个参数,该参数通常都会是一个子查询。
exist通常都会使用关联子查询作为参数

同样的,使用 not exist 表示相反的结果

select product_name, sale_price from Product as P where exists(select * from ShopProduct as SP where SP.shop_id = '000C'
    and SP.product_id = P.product_id);
case表达式

和if-else差不多,有多个条件需要判断,最终只会有一个返回结果

select product_name,
       case when product_type = '衣服' then concat('A: ', product_type)
            when product_type = '办公用品' then concat('B: ', product_type)
            when product_type = '厨房用具' then concat('C: ', product_type)
            else null
           end as abc_product_type
from Product;

select sum(case when product_type = '衣服' then sale_price else 0 end) as sum_price_clothes,
       sum(case when product_type = '厨房用具' then sale_price else 0 end) as sum_price_kitchen,
       sum(case when product_type = '办公用品' then sale_price else 0 end) as sum_price_office
from Product;

上面使用的是搜索CASE表达式,下面用简单CASE表达式来实现以下

select product_name,
       case product_type
           when '衣服' then concat('A: ', product_type)
           when '办公用品' then concat('B: ', product_type)
           when '厨房用具' then concat('C: ', product_type)
           else null
        end as abc_product_type
from Product;

简单case表达式看上去和代码里的switch差不多了

集合运算

通过集合运算,可以得到两张表中记录的集合或者公共记录的集合,又或者其中某张表中的记录的集合。像这样用来进行集合运算的运算符称为集合运算符。

表的加减法
表的加法 - union(并集)

会去除重复的记录

select product_id, product_name from Product union select product_id, product_name from Product2;

注意事项

  1. select的列必须一致
  2. select的列的类型必须一致
  3. 如果有order by的话,必须放在最后使用

保留重复行,在union后面加一个all即可

select product_id, product_name from Product union all select product_id, product_name from Product2;
选取表中公共部分 - intersect(MySQL不支持)

希望保留重复行,同样是加上all

select product_id, product_name from Product intersect select product_id, product_name from Product2;
记录的减法 - except(MySQL不支持)

要注意左右关系,左边 - 右边

select product_id, product_name from Product except select product_id, product_name from Product2 order by product_id;
联结(以列为单位对表进行联结)

一个表中的数据往往不够,这时候要从别的表中把列拿过来,这时候用联结
以表A的列作为桥梁,将表B中满足同样条件的列汇集到同一结果之中

内联结 - inner join
select SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price
from ShopProduct as SP inner join Product as P on SP.product_id = P.product_id

内联结要点

  1. 进行联结时需要在from字句中使用多张表
  2. on必不可少,它指定了联结条件,需要指定多个键时,可以使用and、or。必须在from和where之间
  3. select指定列时,有些列是共有的,有些列是独有的,独有的列可以不写<表的别名>.<列名>这种形式,共有的一定要写,这里为了不混乱,全都写上表名

内联结和where子句结合使用

select SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price
from ShopProduct as SP inner join Product as P on SP.product_id = P.product_id where SP.shop_id = '000A';

联结之后的结果可以想象为一个新表,我们可以对这个表使用where,group by,having,order by等工具

外连接 - outer join
select SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price
from ShopProduct as SP right outer join Product as P on SP.product_id = P.product_id;

外连接的结果比内联结多了两条数据,这两条数据是ShopProduct中product_id没有的。也就是说,当我使用外连接时,先像内联结一样,找出联结点关联的数据,如果还有没有关联到的数据,就继续取出来。这里有一个leftright,就涉及到取哪边的数据了。

select SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price
from Product as P left outer join ShopProduct as SP on SP.product_id = P.product_id;

这两条sql结果是一样的

3张以上表的联结

表的联结是没有数量限制的

准备一个表

create table InventoryProduct (
    inventory_id char(4) not null ,
    product_id char(4) not null ,
    inventory_quantity integer not null ,
    primary key (inventory_id, product_id)
);

start transaction;
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P001', '0001', 0);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P001', '0002', 120);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P001', '0003', 200);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P001', '0004', 3);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P001', '0005', 0);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P001', '0006', 99);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P001', '0007', 999);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P001', '0008', 200);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P002', '0001', 10);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P002', '0002', 25);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P002', '0003', 34);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P002', '0004', 19);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P002', '0005', 99);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P002', '0006', 0);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P002', '0007', 0);
INSERT INTO InventoryProduct (inventory_id, product_id, inventory_quantity) VALUES ('P002', '0008', 18);
commit;

现在需要把仓库P001的产品数量,和商店的信息、产品信息展示出来

select SP.shop_id, SP.shop_name, SP.product_id, P.product_name, P.sale_price, IP.inventory_quantity
from ShopProduct as SP inner join Product as P on SP.product_id = P.product_id
inner join InventoryProduct as IP on SP.product_id = IP.product_id where IP.inventory_id = 'P001';

这里先把ShopProduct表和Product表联结,再和InventoryProduct表联结,Product表就不需要再和InventoryProduct表联结了

交叉联结 - cross join

很少使用,但是是所有联结运算的基础

select SP.shop_id, SP.shop_name, SP.product_id, P.product_name
from ShopProduct as SP cross join Product as P;

结果有104个记录,ShopProduct有13条记录,Product有8条记录,13x8=104。是两个表交叉组合的结果。
所以,内联结是交叉联结的一部分,”内“也可以理解为”包含在交叉联结结果中的部分“。相反,外联结的”外“可以理解为”交叉联结结果之外的部分“

来自 https://leejnull.github.io/2020/01/10/2020-01-10-01/

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 213,254评论 6 492
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,875评论 3 387
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 158,682评论 0 348
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,896评论 1 285
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 66,015评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 50,152评论 1 291
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 39,208评论 3 412
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,962评论 0 268
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,388评论 1 304
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,700评论 2 327
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,867评论 1 341
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,551评论 4 335
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 40,186评论 3 317
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,901评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,142评论 1 267
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,689评论 2 362
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,757评论 2 351