SQL Tips汇集及常见问题

本文介绍了SQL中的LEFTJOIN操作,以及WHERE子句和ON子句在JOIN中的差异。展示了如何通过LEFTJOIN筛选特定条件的记录,同时讨论了在INNERJOIN中ON和WHERE子句的等效性。此外,还提到了在低版本MySQL中实现ROW_NUMBER函数的方法,CASEWHEN用于分区间统计,以及处理NULL值的策略,包括GREATEST、LEAST函数的运用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

SQL Tips汇集及常见问题

表与数据

-- 创建并初始化部门表

DROP TABLE IF EXISTS `dept`;
CREATE TABLE `dept`  (
  `deptno` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '部门编号',
  `dname` varchar(15) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '部门名称',
  `loc` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '部门所在位置',
  PRIMARY KEY (`deptno`) USING BTREE
) ENGINE = InnoDB AUTO_INCREMENT = 41 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '部门表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of dept
-- ----------------------------
INSERT INTO `dept` VALUES (1, '张三', '{\"dname\": \"销售部\", \"dno\": \"1\", \"dmanagerId\": \"3\"}');
INSERT INTO `dept` VALUES (2, '李四', '{\"dname\": \"财务部\", \"deptno\": \"2\", \"dmanagerId\": \"4\"}');
INSERT INTO `dept` VALUES (10, 'ACCOUNTING', 'NEW YORK');
INSERT INTO `dept` VALUES (20, 'RESEARCH', 'DALLAS');
INSERT INTO `dept` VALUES (30, 'SALES', 'CHICAGO');
INSERT INTO `dept` VALUES (40, 'OPERATIONS', 'BOSTON');

-- 创建并初始化员工表
DROP TABLE IF EXISTS `emp`;
CREATE TABLE `emp`  (
  `empno` int(10) UNSIGNED NOT NULL AUTO_INCREMENT COMMENT '雇员编号',
  `ename` varchar(15) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '雇员姓名',
  `job` varchar(10) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '雇员职位',
  `mgr` int(10) UNSIGNED NULL DEFAULT NULL COMMENT '雇员对应的领导的编号',
  `hiredate` date NULL DEFAULT NULL COMMENT '雇员的雇佣日期',
  `sal` decimal(7, 2) NULL DEFAULT NULL COMMENT '雇员的基本工资',
  `comm` decimal(7, 2) NULL DEFAULT NULL COMMENT '奖金',
  `deptno` int(10) UNSIGNED NULL DEFAULT NULL COMMENT '所在部门',
  PRIMARY KEY (`empno`) USING BTREE,
  INDEX `deptno`(`deptno`) USING BTREE,
  CONSTRAINT `emp_ibfk_1` FOREIGN KEY (`deptno`) REFERENCES `dept` (`deptno`) ON DELETE RESTRICT ON UPDATE RESTRICT
) ENGINE = InnoDB AUTO_INCREMENT = 7935 CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci COMMENT = '雇员表' ROW_FORMAT = Dynamic;

-- ----------------------------
-- Records of emp
-- ----------------------------
INSERT INTO `emp` VALUES (7369, 'SMITH', 'CLERK', 7902, '1980-12-17', 800.00, NULL, 20);
INSERT INTO `emp` VALUES (7499, 'ALLEN', 'SALESMAN', 7698, '1981-02-20', 1600.00, 300.00, 30);
INSERT INTO `emp` VALUES (7521, 'WARD', 'SALESMAN', 7698, '1981-02-22', 1250.00, 500.00, 30);
INSERT INTO `emp` VALUES (7566, 'JONES', 'MANAGER', 7839, '1981-04-02', 2975.00, NULL, 20);
INSERT INTO `emp` VALUES (7654, 'MARTIN', 'SALESMAN', 7698, '1981-09-28', 1250.00, 1400.00, 30);
INSERT INTO `emp` VALUES (7698, 'BLAKE', 'MANAGER', 7839, '1981-05-01', 2850.00, NULL, 30);
INSERT INTO `emp` VALUES (7782, 'CLARK', 'MANAGER', 7839, '1981-06-09', 2450.00, NULL, 10);
INSERT INTO `emp` VALUES (7788, 'SCOTT', 'ANALYST', 7566, '1987-07-13', 3000.00, NULL, 20);
INSERT INTO `emp` VALUES (7839, 'KING', 'PRESIDENT', NULL, '1981-11-17', 5000.00, NULL, 10);
INSERT INTO `emp` VALUES (7844, 'TURNER', 'SALESMAN', 7698, '1981-09-08', 1500.00, 0.00, 30);
INSERT INTO `emp` VALUES (7876, 'ADAMS', 'CLERK', 7788, '1987-07-13', 1100.00, NULL, 20);
INSERT INTO `emp` VALUES (7900, 'JAMES', 'CLERK', 7698, '1981-12-03', 950.00, NULL, 30);
INSERT INTO `emp` VALUES (7902, 'FORD', 'ANALYST', 7566, '1981-12-03', 3000.00, NULL, 20);
INSERT INTO `emp` VALUES (7934, 'MILLER', 'CLERK', 7782, '1982-01-23', 1300.00, NULL, 10);

LEFT JOIN与WHERE及On Clause

    SELECT A.* ,B.*
    FROM dept A
    LEFT JOIN emp B
    ON A.deptno = B.deptno  AND mgr = 7839

-- 等同于如下写法:

    SELECT A.* ,B.*
    FROM dept A
    LEFT JOIN
        (SELECT *
            FROM emp B WHERE  mgr = 7839
        )B
    ON A.deptno = B.deptno

即先通过B表(emp)按照mgr=7839筛选出一批deptno,然后再以A表(dept)为主表通过dept进行关联出最终结果。

deptno

dname

loc

empno

ename

job

mgr

hiredate

sal

comm

deptno(1)

1

张三

{"dname": "销售部", "dno": "1", "dmanagerId": "3"}

2

李四

{"dname": "财务部", "deptno": "2", "dmanagerId": "4"}

10

ACCOUNTING

NEW YORK

20

RESEARCH

DALLAS

30

SALES

CHICAGO

7499

ALLEN

SALESMAN

7698

1981-02-20

1600.00

300.00

30

30

SALES

CHICAGO

7521

WARD

SALESMAN

7698

1981-02-22

1250.00

500.00

30

30

SALES

CHICAGO

7654

MARTIN

SALESMAN

7698

1981-09-28

1250.00

1400.00

30

30

SALES

CHICAGO

7844

TURNER

SALESMAN

7698

1981-09-08

1500.00

0.00

30

30

SALES

CHICAGO

7900

JAMES

CLERK

7698

1981-12-03

950.00

30

40

OPERATIONS

BOSTON

而如果是想仅取A和B表关联后mgr=7839的记录,则需要在WHERE Clause后写条件,及如下形式:

SELECT A.* ,B.*
    FROM dept A
    LEFT JOIN emp B
    ON A.deptno = B.deptno 
    WHERE   mgr = 7698

deptno

dname

loc

empno

ename

job

mgr

hiredate

sal

comm

deptno(1)

30

SALES

CHICAGO

7499

ALLEN

SALESMAN

7698

1981-02-20

1600.00

300.00

30

30

SALES

CHICAGO

7521

WARD

SALESMAN

7698

1981-02-22

1250.00

500.00

30

30

SALES

CHICAGO

7654

MARTIN

SALESMAN

7698

1981-09-28

1250.00

1400.00

30

30

SALES

CHICAGO

7844

TURNER

SALESMAN

7698

1981-09-08

1500.00

0.00

30

30

SALES

CHICAGO

7900

JAMES

CLERK

7698

1981-12-03

950.00

30

:在inner join(一般缩写为join)时在ON clause后写条件和where后结果是一致的。

    SELECT A.* ,B.*
    FROM dept A
    INNER JOIN emp B
    ON A.deptno = B.deptno
    WHERE   mgr = 7839

 -- 结果等同于:
SELECT A.* ,B.*
    FROM dept A
    INNER JOIN emp B
    ON A.deptno = B.deptno  AND mgr = 7839

低版本mysql实现row_number

低版本mysql(如5.7)系统没有内置的窗口分析函数,比如ROW_NUMBER,需要手动实现。

select empno,deptno,sal,rank from (
  select heyf_tmp.empno,heyf_tmp.deptno,heyf_tmp.sal,@rownum := @rownum+1 ,
  if(@pdept= heyf_tmp.deptno,@rank:=@rank +1, @rank:= 1) as rank,
  @pdept:=heyf_tmp.deptno
  from (
  select empno,deptno,sal from emp order by deptno asc ,sal desc
  ) heyf_tmp ,
  (select @rownum:=0 , @pdept:= null ,@rank:= 0) a
) result

order by deptno,rank;

empno

deptno

sal

rank

7839

10

5000.00

1

7782

10

2450.00

2

7934

10

1300.00

3

7788

20

3000.00

1

7902

20

3000.00

2

7566

20

2975.00

3

7876

20

1100.00

4

7369

20

800.00

5

7698

30

2850.00

1

7499

30

1600.00

2

7844

30

1500.00

3

7521

30

1250.00

4

7654

30

1250.00

5

7900

30

950.00

6

CASE WHEN分区间统计

SELECT CASE WHEN sal > 0 AND sal <= 2000 THEN 'level1'
            WHEN sal > 2000 AND sal <= 4000 THEN 'level2'
            ELSE 'leve13' END level_name,COUNT(DISTINCT empno) emp_num,SUM(sal) sum_sal
            FROM emp

GROUP BY
CASE WHEN sal > 0 AND sal <= 2000 THEN 'level1'
            WHEN sal > 2000 AND sal <= 4000 THEN 'level2'
            ELSE 'leve13' END
            ORDER BY level_name

level_name

emp_num

sum_sal

leve13

1

5000.00

level1

8

9750.00

level2

5

14275.00

NULL值不参与聚合函数的计算

-- 对comm字段求平均值
SELECT AVG(comm),(300+500+1400+0)/4
FROM emp
-- 这里不难发现,comm为NULL的不参与AVG聚合计算,即仅有值的参与计算,具体为:
-- (300+500+1400+0)/4

-- 写法等价于如下:
SELECT SUM(comm)/COUNT(1) avg_value
FROM emp WHERE comm IS NOT NULL

NULL值与GREATEST、LEAST的使用

-- 计算所有部门累计工资里的最大值,注意没有部门40。

SELECT
GREATEST(
SUM( CASE WHEN deptno = 10 THEN sal ELSE NULL END) ,
SUM( CASE WHEN deptno = 20 THEN sal ELSE NULL END) ,
SUM( CASE WHEN deptno = 30 THEN sal ELSE NULL END),
SUM( CASE WHEN deptno = 40 THEN sal ELSE NULL END)
) max_value
FROM emp

max_value

查不到的数据返回默认值

-- 查找员工号为10000的工资,如果查不到则返回为0。

SELECT IFNULL(MAX(sal),0)
FROM emp
WHERE empno = 10000

sal

0.00

统计连续字段的个数

-- 统计连续月份的最大值,注意给出的月份数据必须持续递增的且不能重复。
SELECT MAX(cnt) max_val
FROM
(
SELECT datediff,COUNT(1) cnt
FROM
(

           SELECT seq,order_date,DATE_ADD(order_date,interval -seq month) datediff FROM
           (
           SELECT 1 seq,'2023-03-01' order_date UNION ALL
           SELECT 2 seq,'2023-04-01' order_date UNION ALL
           SELECT 3 seq,'2023-05-01' order_date UNION ALL
           SELECT 4 seq,'2023-07-01' order_date UNION ALL
           SELECT 5 seq,'2023-08-01' order_date UNION ALL
           SELECT 6 seq,'2023-10-01' order_date UNION ALL
           SELECT 7 seq,'2023-12-01' order_date
           -- UNION ALL SELECT 8 seq,'2023-12-01' order_date  注意这种脏数据需过滤掉
           ) A
)B        
GROUP BY datediff
)C

max_val

3

空、空字符串及NA值

1、查不到数据时,显示为N/A,但结果并不是NULL。

注意:这里的IFNULL(NULL,’Seeme’)并未执行。

2、能查到数据时IFNULL(NULL,’Seeme’)可以执行

3、能查到数据和查不到数据取UNION ALL时结果显然是查到的数据。

SELECT deptno,IFNULL(deptno,'Seeme'),IFNULL(NULL,'Seeme')
FROM dept WHERE deptno = 50
UNION ALL
SELECT deptno,IFNULL(deptno,'Seeme'),IFNULL(NULL,'Seeme')
FROM dept WHERE deptno = 40

4、同理通过length函数,我们可以看出空字符串、空及N/A的区别

空及空字符串的length分别为NULL及0

5、如果想从N/A里查出NULL,可以用如下的写法:

注:该情况仅支持查一个字段。

连接时用ON 1= 1

在拼接宽表的单个指标时可以通过表关联ON 1= 1,这种方式主要是用于数据的拼接。

比如将A结果集的dname和B结果集里的sal合并出两列。

SELECT A.dname,B.sal
FROM
           (SELECT dname FROM  dept WHERE deptno = 1
           ) A
LEFT JOIN
           (
           SELECT sal FROM emp WHERE empno = 7369
           )B
ON 1= 1

dname

sal

张三

800.00

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

ShenLiang2025

您的鼓励是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值