SQL进阶及工作中常用SQL

1.工作中常用sql排行

①group by(用来聚合也时常用来去重注意用groupby select 后要跟聚合字段)
②where(SQL计算之前进行过滤,对性能是极大的提高,代码测试阶段where条件下尽量跟分区)
③having(SQL计算之后进行过滤,它的使用会极大提升的代码的简洁性,同样它的功能十分强大)
④时间函数(时间与我们息息相关时间函数同样也是)
⑤nvl函数(去空变0)
⑥union以及union all(像双面胶一样只要数据结构一样就可以直接拼接 union去重拼接,union all不去重)
⑦row_number,rank_number,dence_rank rank函数(给函数标上rank序号)
⑧窗口函数 over(partition by t1 order by t2)(常与rank函数一同使用)
(⑦⑧使用详细可看博客https://blog.csdn.net/shaiguchun9503/article/details/82349050)

2.工作中常用sql小技巧

①运行代码前进行校检代码逻辑性是否正确使用explain sql语句(运行SQL前检验可以提前发现语法错误,减少资源浪费,提高开发效率,强烈建议每次运行前使用explain进行检验)
②查看异常数据null
③查看表分区下具体数据量(show rowcount extended table)
④代码冗余且可复用可创建临时表
(CREATE TABLE if not exists tem_table select * from table)

3.SQL进阶(这是重点)

1.关于null值

1.1null值常见问题

1.sql null 值不是值也不是变量 连接null值常用is null 或者 is not null 来判断是否为空值
错误示范

错误:把null当成值
case col_1
	when 1 then 'o'
	when null then 'x'
end

正确语法

case when col_1 =1 then 'O'
	when col_1 is null then 'x'
end

2.null值下not in 不等价于 not exists
''not in 不等价于 not exists" ,虽然in 和 exists 是可以互换但是在有null情况下不可使用not in 来代替 not exists,not in 在有null情况下默认生成结果为空值,原理涉及到sql的三值原理(分别为true ,false ,以及因为null存在的unknow)
又兴趣可以简单百度下,这里不做过多讲解
例:
原表

/* NOT IN和NOT EXISTS不是等价的 */
CREATE TABLE Class_A
(name VARCHAR(16) PRIMARY KEY,
 age  INTEGER,
 city VARCHAR(16) NOT NULL );

CREATE TABLE Class_B
(name VARCHAR(16) PRIMARY KEY,
 age  INTEGER,
 city VARCHAR(16) NOT NULL );

INSERT INTO Class_A VALUES('布朗', 22, '东京');
INSERT INTO Class_A VALUES('拉里',   19, '埼玉');
INSERT INTO Class_A VALUES('伯杰',   21, '千叶');

INSERT INTO Class_B VALUES('齐藤',  22,   '东京');
INSERT INTO Class_B VALUES('田尻',  23,   '东京');
INSERT INTO Class_B VALUES('山田',  NULL, '东京');
INSERT INTO Class_B VALUES('和泉',  18,   '千叶');
INSERT INTO Class_B VALUES('武田',  20,   '千叶');
INSERT INTO Class_B VALUES('石川',  19,   '神奈川');
/* 1.比较谓词和NULL(1):排中律不成立 */
CREATE TABLE Students
(name VARCHAR(16) PRIMARY KEY,
 age  INTEGER );

INSERT INTO Students VALUES('布朗', 22);
INSERT INTO Students VALUES('拉里',   19);
INSERT INTO Students VALUES('约翰',   NULL);
INSERT INTO Students VALUES('伯杰', 21);

需求: 查询与B班住在东京的学生年龄不同的A班学生的SQL语句?

/*错误sql语句,结果为空值*/
SELECT *
  FROM Class_A
 WHERE age NOT IN ( SELECT age
                      FROM Class_B
                     WHERE city = '东京' );
/* 正确的SQL语句:拉里和伯杰将被查询到 */
SELECT *
  FROM Class_A A
 WHERE NOT EXISTS ( SELECT *
                      FROM Class_B B
                     WHERE A.age = B.age
                       AND B.city = '东京' );

在null下使用限定谓词all也会爆空值.有兴趣大家也可以去验证下

/* 查询比B班住在东京的所有学生年龄都小的A班学生 */
SELECT *
  FROM Class_A
 WHERE age < ALL ( SELECT age
                     FROM Class_B
                    WHERE city = '东京' );

1.2null值克星(重点):

1.极值函数(max,min) 忽略null值
2.聚合函数:count(具体列名)忽略null值 注:count(*)包含null
avg,sum均忽略null值
3.where条件下过滤null值又或者是使用nvl等函数转换null值

2.case表达式

需求: 求x、y和z中的最大值
1.1.原表:
注:若字段与关键字重复不可用,可用飘号引起 例:key 英文输入法点击键盘"~"

CREATE TABLE Greatests
(key CHAR(1) PRIMARY KEY,
 x   INTEGER NOT NULL,
 y   INTEGER NOT NULL,
 z   INTEGER NOT NULL);

INSERT INTO Greatests VALUES('A', 1, 2, 3);
INSERT INTO Greatests VALUES('B', 5, 5, 2);
INSERT INTO Greatests VALUES('C', 4, 7, 1);
INSERT INTO Greatests VALUES('D', 3, 3, 8);

1.2.闭环

/* 求x、y和z中的最大值 */
SELECT key,
       CASE WHEN CASE WHEN x < y THEN y ELSE x END < z
            THEN z
            ELSE CASE WHEN x < y THEN y ELSE x END
        END AS greatest
  FROM Greatests;

1.3效果图
在这里插入图片描述

3.having函数的使用

工作时用好having是特别关键的,我们可以使用having来判定表字段间的关系
例是否字段之间呈一一对应的关心是否是唯一值

查看结果是否有值,有值则说明a字段具有唯一值
select rank_number() over (partition by a order by b) rn
from table
having rn = 1

1.1加强having的使用
创建原表

/* 用HAVING子句进行子查询:求众数(求中位数时也用本代码) */
CREATE TABLE Graduates
(name   VARCHAR(16) PRIMARY KEY,
 income INTEGER NOT NULL);

INSERT INTO Graduates VALUES('桑普森', 400000);
INSERT INTO Graduates VALUES('迈克',     30000);
INSERT INTO Graduates VALUES('怀特',   20000);
INSERT INTO Graduates VALUES('阿诺德', 20000);
INSERT INTO Graduates VALUES('史密斯',     20000);
INSERT INTO Graduates VALUES('劳伦斯',   15000);
INSERT INTO Graduates VALUES('哈德逊',   15000);
INSERT INTO Graduates VALUES('肯特',     10000);
INSERT INTO Graduates VALUES('贝克',   10000);
INSERT INTO Graduates VALUES('斯科特',   10000);

1.2求中位数

/* 求中位数的SQL语句:在HAVING子句中使用非等值自连接 */
SELECT AVG(DISTINCT income)
  FROM (SELECT T1.income
          FROM Graduates T1, Graduates T2
      GROUP BY T1.income
               /* S1的条件 */
        HAVING SUM(CASE WHEN T2.income >= T1.income THEN 1 ELSE 0 END) 
                   >= COUNT(*) / 2
               /* S2的条件 */
           AND SUM(CASE WHEN T2.income <= T1.income THEN 1 ELSE 0 END) 
                   >= COUNT(*) / 2 ) TMP;

2.1 原表

/* 查询不包含NULL的集合 */
CREATE TABLE Students
(student_id   INTEGER PRIMARY KEY,
 dpt          VARCHAR(16) NOT NULL,
 sbmt_date    DATE);

INSERT INTO Students VALUES(100,  '理学院',   '2005-10-10');
INSERT INTO Students VALUES(101,  '理学院',   '2005-09-22');
INSERT INTO Students VALUES(102,  '文学院',   NULL);
INSERT INTO Students VALUES(103,  '文学院',   '2005-09-10');
INSERT INTO Students VALUES(200,  '文学院',   '2005-09-22');
INSERT INTO Students VALUES(201,  '工学院',   NULL);
INSERT INTO Students VALUES(202,  '经济学院', '2005-09-25');

2.2 需求2

/* 查询“提交日期”列内不包含NULL的学院(1):使用COUNT函数 */
SELECT dpt
  FROM Students
 GROUP BY dpt
HAVING COUNT(*) = COUNT(sbmt_date);

2.3需求3

/* 练习“特征函数” 
   查找所有学生都在9月份提交完成的学院(2):使用EXTRACT函数 */
SELECT dpt
  FROM Students
 GROUP BY dpt
HAVING COUNT(*) = SUM(CASE WHEN EXTRACT (YEAR FROM sbmt_date) = 2005
                            AND EXTRACT (MONTH FROM sbmt_date) = 09
                           THEN 1 ELSE 0 END);

2.3另一种方法

练习“特征函数” 
   查找所有学生都在9月份提交完成的学院(1):使用BETWEEN谓词 */
SELECT dpt
  FROM Students
 GROUP BY dpt
HAVING COUNT(*) = SUM(CASE WHEN sbmt_date BETWEEN '2005-09-01' AND '2005-09-30'
                           THEN 1 ELSE 0 END);

4.rank窗口函数使用

原表

/* 练习题1-2-2:分地区排序 */
CREATE TABLE DistrictProducts
(district  VARCHAR(16) NOT NULL,
 name      VARCHAR(16) NOT NULL,
 price     INTEGER NOT NULL,
 PRIMARY KEY(district, name, price));

INSERT INTO DistrictProducts VALUES('东北', '橘子',	100);
INSERT INTO DistrictProducts VALUES('东北', '苹果',	50);
INSERT INTO DistrictProducts VALUES('东北', '葡萄',	50);
INSERT INTO DistrictProducts VALUES('东北', '柠檬',	30);
INSERT INTO DistrictProducts VALUES('关东', '柠檬',	100);
INSERT INTO DistrictProducts VALUES('关东', '菠萝',	100);
INSERT INTO DistrictProducts VALUES('关东', '苹果',	100);
INSERT INTO DistrictProducts VALUES('关东', '葡萄',	70);
INSERT INTO DistrictProducts VALUES('关西', '柠檬',	70);
INSERT INTO DistrictProducts VALUES('关西', '西瓜',	30);
INSERT INTO DistrictProducts VALUES('关西', '苹果',	20);

注:mysql不可用窗口函数

/* 练习题1-2-2 分地区排序 */
SELECT district, name, price,
          RANK() OVER(PARTITION BY district 
                      ORDER BY price DESC) AS rank_1
  FROM DistrictProducts;

mysql通过子查询达到窗口函数效果

SELECT P1.district, P1.name,
       P1.price,
       (SELECT COUNT(P2.price)
          FROM DistrictProducts P2
         WHERE P1.district = P2.district    /* 在同一个地区内进行比较 */
           AND P2.price > P1.price) + 1 AS rank_1
  FROM DistrictProducts P1;

5.自连接

/* 练习题1-2-1:可重组合 */
SELECT P1.name AS name_1, P2.name AS name_2
  FROM Products P1, Products P2
 WHERE P1.name >= P2.name;

总结:要想在工作中写好sql,首先就要增强对需求的理解然后就是增强对数据敏感度其中包含但不限于对null值的处理,对字段与字段之间关系的把控需不需要去重,怎么去重都是十分关键的.最后我们还要学会如何使用巧劲又好又快的实现代码demo,实现多demo快速并发这里就要求我们恰当使用where等限制条件以及中间表.

参考书目:《SQL进阶教程》| MICK

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章