常用的SQL例子

67 阅读9分钟

这是一些常用的SQL例子,不同类型的SQL都列举了一下,方便自己忘记时查看😊

准备SQL表:
DROP TABLE IF EXISTS `product`;
CREATE TABLE `product`  (
  `product_id` char(4) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `product_name` varchar(100) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `product_type` varchar(32) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL,
  `sale_price` int(0) NULL DEFAULT NULL,
  `purchase_price` int(0) NULL DEFAULT NULL,
  `regist_date` date NULL DEFAULT NULL,
  PRIMARY KEY (`product_id`) USING BTREE
);

INSERT INTO `product` VALUES ('0001', 'T恤衫', '衣服', 1000, 500, '2009-09-20');
INSERT INTO `product` VALUES ('0002', '打孔器', '办公用品', 500, 320, '2009-09-11');
INSERT INTO `product` VALUES ('0003', '运动T恤', '衣服', 4000, 2800, '2009-09-11');
INSERT INTO `product` VALUES ('0004', '菜刀', '厨房用具', 3000, 2800, '2009-09-20');
INSERT INTO `product` VALUES ('0005', '高压锅', '厨房用具', 6800, 5000, '2009-01-15');
INSERT INTO `product` VALUES ('0006', '叉子', '厨房用具', 500, NULL, '2009-09-20');
INSERT INTO `product` VALUES ('0007', '擦菜板', '厨房用具', 880, 790, '2009-01-15');
INSERT INTO `product` VALUES ('0008', '圆珠笔', '办公用品', 100, NULL, '2009-11-11');

DROP TABLE IF EXISTS `my_course`;
CREATE TABLE `my_course`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `course` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `score` int(0) NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
);
insert into my_course VALUES (1,'张三','语文',74);
insert into my_course VALUES (2,'张三','数学',83);
insert into my_course VALUES (3,'张三','英语',93);
insert into my_course VALUES (4,'李四','语文',74);
insert into my_course VALUES (5,'李四','数学',84);
insert into my_course VALUES (6,'李四','英语',94);

DROP TABLE IF EXISTS `my_city`;
CREATE TABLE `my_city`  (
  `id` int(0) NOT NULL AUTO_INCREMENT,
  `name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `city1` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `city2` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  `city3` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL,
  PRIMARY KEY (`id`) USING BTREE
);
INSERT INTO `my_city` VALUES (1, '张三', '北京', '上海', '广州');
INSERT INTO `my_city` VALUES (2, '李四', '纽约', '东京', '首尔');
  • 全部科目及格(>=60)的学生:

    select name,min(score) from my_course
     GROUP BY name
     HAVING min(score) >=60;
    
  • 全部科目平均分>=80的学生:

    select name,avg(score) from my_course
     GROUP BY name
     HAVING avg(score) >=80;
    
  • 查询重复的数据:

    select * from my_tb tb where EXISTS
    ( SELECT 1 FROM 
        (select min(id) as mid from my_tb 
    		group by name,course,score) as min_ids
     	WHERE tb.id=min_ids.mid
    );
    
  • 查询商品的每月上架的数量:

    select month(regist_date),count(*) from product group by month(regist_date);
    
  • 查询商品的价格区间的数量

    select scope,count(*) 
    from  (select  
    				CASE 
    					WHEN sale_price <1000
    						THEN '价格[0-1000)'
    					WHEN sale_price <5000
    						THEN '价格[1000-5000)'
    					WHEN sale_price>=5000
    						THEN '价格[5000+)'
    					ELSE '[未知]'
    				END 'scope'
    				from product
    			) tmp
    group by scope ;
    

连接查询

  • CROSS JOIN:

    -- CROSS JOIN
    select * FROM
    join_student s
    CROSS join 
    join_school sc
    order by id;
    打印:
    id	name	school_id	sh_id	sh_name
    1001	张三	1	3	中心学校
    1001	张三	1	2	南方学校
    1001	张三	1	1	北方学校
    1002	李四	2	3	中心学校
    1002	李四	2	2	南方学校
    1002	李四	2	1	北方学校
    1003	王五	2	3	中心学校
    1003	王五	2	2	南方学校
    1003	王五	2	1	北方学校
    1005	赵六	99	3	中心学校
    1005	赵六	99	2	南方学校
    1005	赵六	99	1	北方学校
    
    -- CROSS JOIN,带条件时,等价于inner join
    select * FROM
    join_student s
    CROSS join 
    join_school sc
    on s.school_id=sc.sh_id
    order by id;
    打印:
    id	name	school_id	sh_id	sh_name
    1001	张三	1	1	北方学校
    1002	李四	2	2	南方学校
    1003	王五	2	2	南方学校
    
  • INNER JOIN:

    -- INNER JOIN
    select * FROM
    join_student s
    inner join 
    join_school sc
    on s.school_id=sc.sh_id
    order by id;
    打印:
    id	name	school_id	sh_id	sh_name
    1001	张三	1	1	北方学校
    1002	李四	2	2	南方学校
    1003	王五	2	2	南方学校
    

    图片.png 加外键索引 图片.png

  • LEFT JOIN:

    -- LEFT JOIN
    select * FROM
    join_student s
    LEFT join 
    join_school sc
    on s.school_id=sc.sh_id
    order by id;
    打印:
    id	    name	school_id	sh_id	sh_name
    id	name	school_id	sh_id	sh_name
    1001	张三	1	1	北方学校
    1002	李四	2	2	南方学校
    1003	王五	2	2	南方学校
    1005	赵六	99	null	null
    

    图片.png

  • RIGHT JOIN:

    -- RIGHT JOIN
    select * FROM
    join_student s
    RIGHT join 
    join_school sc
    on s.school_id=sc.sh_id
    order by id;
    打印:
    id		name	school_id	sh_id	sh_name
    null	null	null	3	 中心学校
    1001	张三  	1		1	北方学校
    1002	李四		2		2	南方学校
    1003	王五		2		2	南方学校
    

子查询

  • 子查询:(select引发的子查询)

    SELECT name,(select city.city_name from city WHERE city.id=addr_id) addr_name FROM person; -- 查询person的name和addr_name
    
  • WHERE 引发的子查询:(子句结果当条件)

        SELECT * FROM product WHERE sale_price>(SELECT AVG(sale_price) FROM product); -- 查询sale_price大于平均price的product。
    
        SELECT * FROM product WHERE sale_price IN (SELECT MAX(sale_price) FROM product GROUP BY product_type); -- 查询每个类别中price最大的product。
    
  • EXISTS引发的子查询:(将主句的结果拿到子句中比较,返回符合的记录)

        SELECT * FROM product WHERE EXISTS (SELECT MAX(sale_price) max_price FROM product GROUP BY product_type HAVING sale_price=max_price ); -- 查询每个类别中price最大的product。
    
  • FROM引发的子查询:(将子句结果当临时表)

        SELECT * FROM (SELECT * FROM product) tp GROUP BY product_type;
    
  • Update子查询:

    update product 
    set stock=(
    	select quantity from store where product_id=product.id 
    );
    

WITH AS

  • 递归查询 name

    with recursive name_path (id, name, path) as (
        select id, name, cast(name as char(200))
        from employees_mgr
        where manager_id=0
        union all
        select e.id, e.name, concat(ep.path, '/', e.name)
        from name_path as ep
        inner join employees_mgr as e
        on ep.id = e.manager_id
        )
    select path from name_path;
    
    with recursive name_path (id, menu_name, path,level) as (
        select e.id, e.menu_name, cast(menu_name as char(255)), 1
        from sys_menu e
        where parent_id=0
        union all
        select e.id, e.menu_name, concat(ep.path, '/', e.menu_name), level+1
        from name_path as ep
        inner join sys_menu as e
        on ep.id = e.parent_id
        )
    select * from name_path;
    

行转列、列转行

  • listagg:拼接值

    select *, listagg(score) within group(order by course) as scores 
    from my_course group by name;
    
    
  • PIVOT:行转列

    select * from my_course 
    pivot(sum(score) for course in ('语文','数学','英语') );
    
  • UNPIVOT:列转行

    select * from my_city
    unpivot(city for c_seq in (city1,city2,city3) ) up;
    

窗口函数

  • 语法: <窗口函数> OVER ([PARTITION BY <列>] ORDER BY <排序的列>)
    • partition by :用于分组(非必须),分组后的记录相当于一个窗口
    • order by:用于排序 ,对窗口内的记录进行排序
    • 注意: 窗口函数只能在Select子句中。
排名函数:

特点:是对窗口内的记录进行排名

  • rank():排名,如 1、1、3
  • dense_rank():排名,如 1、1、2
  • row_number():行数,如 1、2、3
-- 分类下的售价排名
SELECT product_name, product_type, sale_price,
 RANK() OVER (PARTITION BY product_type
 ORDER BY sale_price) AS ranking
 FROM product;
-- 全局的售价排名
SELECT product_name, product_type, sale_price, 
 RANK() OVER (ORDER BY sale_price) AS ranking
 FROM product;
-- 全局的售价排名(各种排名)
SELECT product_name, sale_price,
    RANK() OVER (ORDER BY sale_price) AS ranking,
    DENSE_RANK() OVER (ORDER BY sale_price) AS dense_ranking,
    ROW_NUMBER() OVER (ORDER BY sale_price) AS row_num
    FROM product;
聚合函数:

特点:是对窗口内的记录进行累计,而且只累计当前记录之前的(窗口0行~当前行)

  • sum(column):
  • avg(column):
  • count(column):
  • 注意: 是对窗口的聚合
-- 计算分类下的累计的价格(窗口0行~当前行)
 SELECT product_id,product_type, product_name, sale_price,
 SUM(sale_price) OVER (PARTITION BY product_type ORDER BY sale_price) AS current_sum
 FROM product;

-- 计算累计的价格(窗口0行~当前行)
 SELECT product_id,product_type, product_name, sale_price,
 SUM(sale_price) OVER (ORDER BY sale_price desc) AS current_sum
 FROM product;

 -- 计算平均的价格(窗口0行~当前行)
SELECT product_id, product_name, sale_price,
    SUM(sale_price) OVER (ORDER BY product_id) AS curr_sum,
AVG(sale_price) OVER (ORDER BY product_id) AS curr_avg
    FROM product;
 
 -- 计算平均的价格,
 -- 'ROWS 2 preceding'表示只累计当前记录之前的前2+1行
 -- 'ROWS 2 following'表示只累计当前记录之后的后2+1行
 -- 'ROWS BETWEEN 1 preceding AND 1 following'表示只累计当前记录前后的1+1+1行
SELECT product_id, product_name, sale_price,
 AVG (sale_price) OVER (ORDER BY product_id
 ROWS 2 preceding) AS moving_avg
 FROM product;


-- 根据分类 — 上架时间 分级合计价格。(注,在mysql中写法为 GROUP BY product_type WITH ROLLUP)
SELECT product_type, shelf_time, SUM(sale_price) AS sum_price
    FROM product GROUP BY ROLLUP(product_type,shelf_time); 
GROUPING运算:
  • rollup:rollup 是“卷起”的意思,形象地说明了该操作能够得到像从小计到合计这样。rollup可以同时得出合计和小计。

    -- 分类 计算价格总和(注,在mysql中写法为 GROUP BY product_type WITH rollup)
    SELECT product_type, SUM(sale_price) AS sum_price
     FROM product GROUP BY rollup(product_type);
     
    -- 分类、上架时间 计算价格总和(注,在mysql中写法为 GROUP BY product_type,regist_date WITH rollup)
     SELECT product_type, regist_date, SUM(sale_price) AS sum_price
        FROM product GROUP BY rollup(product_type,regist_date);
    
    -- grouping(column):该函数在其参数列的值为超级分组记录所产生的null 时返回 1(即小结的记录标记为1)
    
    -- rollup的使用
    -- rollup分了层级:一级分组product_type,二级分组:regist_date。(即groupBy()、groupBy(product_type)、groupBy(product_type,regist_date))
    SELECT 
    		CASE WHEN GROUPING(product_type) = 1 
    		 THEN '商品种类 合计'
    		 ELSE product_type END AS product_type,
    		 CASE WHEN GROUPING(regist_date) = 1 
    		 THEN '登记日期 合计'
    		 ELSE CAST(regist_date AS VARCHAR(16)) END AS regist_date,
    		 SUM(sale_price) AS sum_price
     FROM Product GROUP BY rollup(product_type, regist_date);
    
  • cube:用数据来搭积木

     -- cube的使用
     -- cube相比rollup多了 直接以regist_date分组的记录。(即groupBy()、groupBy(product_type)、groupBy(product_type,regist_date)、groupBy(registe_date))
    SELECT 
    		CASE WHEN GROUPING(product_type) = 1 
    		 THEN '商品种类 合计'
    		 ELSE product_type END AS product_type,
    		 CASE WHEN GROUPING(regist_date) = 1 
    		 THEN '登记日期 合计'
    		 ELSE CAST(regist_date AS VARCHAR(16)) END AS regist_date,
    		 SUM(sale_price) AS sum_price
     FROM Product GROUP BY cube(product_type, regist_date);
     
    
  • grouping sets:相当从 ROLLUP 或者 CUBE 的结果中取出部分记录

    -- grouping sets的使用
    -- cube中的记录的子集。(即groupBy(product_type)、groupBy(registe_date))
    SELECT 
    		CASE WHEN GROUPING(product_type) = 1 
    		 THEN '商品种类 合计'
    		 ELSE product_type END AS product_type,
    		 CASE WHEN GROUPING(regist_date) = 1 
    		 THEN '登记日期 合计'
    		 ELSE CAST(regist_date AS VARCHAR(16)) END AS regist_date,
    		 SUM(sale_price) AS sum_price
     FROM Product GROUP BY grouping sets(product_type, regist_date);
    

JSON操作

-- json数组,追加元素(基本值)
UPDATE ch_chat_msg 
            SET reads_sign = JSON_ARRAY_APPEND(reads_sign,'$',:userId ) 
            WHERE msg_id=:msgId
-- json数组,追加元素(对象值)
UPDATE ch_group_relation 
            SET users = JSON_ARRAY_APPEND(users,'$',CONVERT(:member,JSON) ) 
            WHERE group_no=:groupNo
-- json数组,移除元素
UPDATE ch_group_relation SET users = JSON_REMOVE(users, CONCAT('$[',:index,']') ) where group_no=:groupNo