数据库基础——SQL基础查询

参考资料:https://blog.youkuaiyun.com/weixin_43896929/article/details/120750965

数据库基础——SQL基础查询

使用as关键字起别名

 mysql> select deptno,dname as deptname from dept;
      +--------+------------+
      | deptno | deptname   |
      +--------+------------+
      |     10 | ACCOUNTING |
      |     20 | RESEARCH   |
      |     30 | SALES      |
      |     40 | OPERATIONS |
      +--------+------------+
  • 注意:只是将显示的查询结果列名显示为deptname,原表列名还是叫:dname
  • 记住:select语句是永远都不会进行修改操作的。(因为只负责查询)

<> 或!= 不等于

查询薪资不等于800的员工姓名和编号?
  select empno,ename from emp where sal != 800;
  select empno,ename from emp where sal <> 800; // 小于号和大于号组成的不等号

逻辑运算符优先级

在SQL中,当ANDOR同时出现时,存在优先级问题:

  1. AND的优先级高于OR,类似于数学中乘法优先于加法
  2. 执行顺序是从左到右,但会先计算所有AND条件,再计算OR条件

示例说明

-- 条件:工资大于1000并且部门是20,或者部门是30的所有员工
-- 由于AND优先级高,实际执行顺序是:(sal > 1000 AND deptno = 20) OR deptno = 30
SELECT * FROM emp WHERE sal > 1000 AND deptno = 20 OR deptno = 30;

-- 如果想改变优先级,让OR先执行,需要加括号
-- 条件:工资大于1000,并且(部门是20或者30)的员工
SELECT * FROM emp WHERE sal > 1000 AND (deptno = 20 OR deptno = 30);

最佳实践:当不确定优先级时,使用括号明确指定执行顺序。

IN 运算符

IN相当于多个OR条件的简写形式:

-- 这两种写法等价
SELECT * FROM emp WHERE deptno = 10 OR deptno = 20 OR deptno = 30;
SELECT * FROM emp WHERE deptno IN (10, 20, 30);

-- NOT IN 表示不在这个范围内
SELECT * FROM emp WHERE deptno NOT IN (10, 30);

NOT 运算符

NOT用于取反,常用于ISIN条件:

-- 查询佣金不为空的员工
SELECT * FROM emp WHERE comm IS NOT NULL;

-- 查询不在10和30部门的员工
SELECT * FROM emp WHERE deptno NOT IN (10, 30);

LIKE 模糊查询

LIKE用于模式匹配,支持两个通配符:

  1. %:匹配任意多个字符(包括零个字符)
  2. _:匹配任意单个字符

示例详解

-- 1. 找出名字以T结尾的
SELECT ename FROM emp WHERE ename LIKE '%T';
-- 匹配:Scott (以t结尾),不匹配:Turner

-- 2. 找出名字以K开始的
SELECT ename FROM emp WHERE ename LIKE 'K%';
-- 匹配:King,不匹配:Blake

-- 3. 找出第二个字母是A的
SELECT ename FROM emp WHERE ename LIKE '_A%';
-- 匹配:James (J-A-m-e-s),不匹配:Adams (A-d-a-m-s)

-- 4. 找出第三个字母是R的
SELECT ename FROM emp WHERE ename LIKE '__R%';
-- 匹配:Turner (T-u-R-n-e-r),不匹配:Miller (M-i-L-l-e-r)

注意事项

  1. LIKE匹配默认不区分大小写(取决于数据库配置)
  2. 如果要匹配%_本身,需要使用转义字符:
    -- 查找包含"20%"的数据
    SELECT * FROM products WHERE description LIKE '%20\%%' ESCAPE '\';
    
  3. 模糊查询通常会导致全表扫描,在大表上性能较差

SQL分组查询(Group By)详解

基本概念

分组查询是将表中的数据按照一个或多个列的值进行分类,然后对每个分组进行聚合计算(如求和、平均值、最大值等)。

基本语法

SELECT 
    分组字段, 
    聚合函数(字段)
FROM 
    表名
[WHERE 条件]
GROUP BY 
    分组字段
[HAVING 分组后条件]
[ORDER BY 排序字段];

核心规则

  1. SELECT后面的字段限制

    • 只能包含:
      • GROUP BY子句中出现的字段
      • 聚合函数(如SUM, AVG, MAX, MIN, COUNT等)
    • 不能包含其他非分组字段
  2. 为什么有这个限制

    • 分组后,每个分组只返回一行结果
    • 非分组字段可能有多个值,无法确定显示哪一个

错误示例分析

-- 错误示例(在Oracle中会报错)
SELECT ename, job, SUM(sal) FROM emp GROUP BY job;

问题分析:

  • 按job分组后,每个job分组可能对应多个ename
  • 无法确定应该显示哪个ename,所以Oracle会报错
  • MySQL宽松模式下会显示第一个遇到的ename,但这通常不是我们想要的结果

正确用法示例

示例1:每个部门的平均薪资

SELECT 
    deptno, 
    AVG(sal) AS avg_salary
FROM 
    emp
GROUP BY 
    deptno;

示例2:每个部门不同岗位的最高薪资

SELECT 
    deptno, 
    job, 
    MAX(sal) AS max_salary
FROM 
    emp
GROUP BY 
    deptno, job;

这里deptno,job两个字段联合分组,相当于把这两个字段组合起来作为分组依据。

HAVING子句

HAVING用于对分组后的结果进行过滤,类似于WHERE但作用于分组后的数据。

WHERE与HAVING的区别

特性WHEREHAVING
执行时机在分组前过滤数据在分组后过滤数据
可否用聚合不能直接使用聚合函数可以使用聚合函数
性能优先使用,效率更高后使用,效率较低
使用场景过滤原始数据过滤分组后的结果

示例:找出平均薪资大于2000的部门

SELECT 
    deptno, 
    AVG(sal) AS avg_salary
FROM 
    emp
GROUP BY 
    deptno
HAVING 
    AVG(sal) > 2000;

完整SQL执行顺序

  1. FROM:确定数据来源表
  2. WHERE:对原始数据进行筛选
  3. GROUP BY:按照指定字段分组
  4. HAVING:对分组后的结果进行筛选
  5. SELECT:选择要显示的列
  6. ORDER BY:对最终结果排序

实际应用技巧

  1. 多列分组:当需要更细粒度的分组时,可以使用多个字段组合分组

    SELECT 
        deptno, 
        job, 
        COUNT(*) AS employee_count
    FROM 
        emp
    GROUP BY 
        deptno, job;
    
  2. 分组后排序

    SELECT 
        deptno, 
        AVG(sal) AS avg_salary
    FROM 
        emp
    GROUP BY 
        deptno
    ORDER BY 
        avg_salary DESC;
    
  3. WHERE和HAVING组合使用

    -- 先筛选出薪资>1000的员工,再按部门分组,最后筛选出平均薪资>2000的部门
    SELECT 
        deptno, 
        AVG(sal) AS avg_salary
    FROM 
        emp
    WHERE 
        sal > 1000
    GROUP BY 
        deptno
    HAVING 
        AVG(sal) > 2000;
    

常见错误

  1. SELECT包含非分组字段

    -- 错误:ename不是分组字段
    SELECT ename, deptno, AVG(sal) FROM emp GROUP BY deptno;
    
  2. 在WHERE中使用聚合函数

    -- 错误:不能在WHERE中使用聚合函数
    SELECT deptno, AVG(sal) FROM emp WHERE AVG(sal) > 2000 GROUP BY deptno;
    
  3. 混淆GROUP BY和ORDER BY

    -- 错误:GROUP BY不是用来排序的
    SELECT deptno, AVG(sal) FROM emp GROUP BY AVG(sal);
    

掌握分组查询是SQL进阶的重要一步,理解这些概念后,你就能处理更复杂的数据分析需求了。

SQL数据处理函数详解

数据处理函数是SQL中用于对数据进行转换、计算和格式化的工具,可以分为单行处理函数和多行处理函数两大类。

一、单行处理函数

单行处理函数的特点:一个输入对应一个输出,即对每一行数据单独处理。

1. 字符串处理函数

(1) LOWER/UPPER - 大小写转换
-- 转换为小写
SELECT LOWER(ename) AS lowercase_name FROM emp;

-- 转换为大写
SELECT UPPER(ename) AS uppercase_name FROM emp;
(2) SUBSTR - 字符串截取
-- 语法:SUBSTR(字符串, 起始位置, 长度)
-- 注意:SQL中字符串索引通常从1开始

-- 获取名字的第一个字母
SELECT SUBSTR(ename, 1, 1) AS first_letter FROM emp;

-- 获取名字的前三个字母
SELECT SUBSTR(ename, 1, 3) AS name_prefix FROM emp;
(3) CONCAT - 字符串连接
-- 连接员工编号和姓名
SELECT CONCAT(empno, '-', ename) AS emp_info FROM emp;

-- 实际应用:生成邮箱地址
SELECT CONCAT(LOWER(ename), '@company.com') AS email FROM emp;
(4) LENGTH - 字符串长度
-- 获取员工姓名的长度
SELECT ename, LENGTH(ename) AS name_length FROM emp;
(5) TRIM - 去除空格
-- 去除两边空格
SELECT * FROM emp WHERE ename = TRIM('   KING');

-- 去除左边空格(LTRIM)或右边空格(RTRIM)
SELECT LTRIM('   Hello') AS left_trimmed;
SELECT RTRIM('Hello   ') AS right_trimmed;

2. 数值处理函数

(1) ROUND - 四舍五入
-- 保留整数
SELECT ROUND(1236.567, 0) AS result;  -- 1237

-- 保留1位小数
SELECT ROUND(1236.567, 1) AS result;  -- 1236.6

-- 保留到十位
SELECT ROUND(1236.567, -1) AS result; -- 1240
(2) RAND - 随机数
-- 生成0-1之间的随机数
SELECT RAND() AS random_num;

-- 生成0-100的随机整数
SELECT ROUND(RAND()*100, 0) AS random_int;

3. 条件处理函数

(1) CASE WHEN - 条件判断
-- 根据不同职位调整工资显示
SELECT 
    ename,
    job, 
    sal AS old_salary,
    CASE job 
        WHEN 'MANAGER' THEN sal*1.1 
        WHEN 'SALESMAN' THEN sal*1.5 
        ELSE sal 
    END AS new_salary
FROM emp;
(2) IFNULL - 空值处理
-- 将NULL值替换为0
SELECT 
    ename, 
    sal, 
    comm, 
    IFNULL(comm, 0) AS commission,
    sal + IFNULL(comm, 0) AS total_income
FROM emp;

4. 日期处理函数

(1) DATE_FORMAT - 日期格式化
-- 将日期格式化为YYYY-MM-DD形式
SELECT ename, DATE_FORMAT(hiredate, '%Y-%m-%d') AS hire_date FROM emp;

-- 常用格式符号:
-- %Y 四位年份
-- %y 两位年份
-- %m 月份(01-12)
-- %d 日(01-31)
-- %H 小时(00-23)
-- %i 分钟(00-59)
-- %s 秒(00-59)
(2) STR_TO_DATE - 字符串转日期
-- 将字符串转换为日期类型
SELECT STR_TO_DATE('2023-01-15', '%Y-%m-%d') AS date_value;

二、多行处理函数(分组函数)

多行处理函数的特点:多个输入对应一个输出,通常与GROUP BY一起使用。

1. 常用分组函数

-- 计数
SELECT COUNT(*) AS total_employees FROM emp;

-- 求和
SELECT SUM(sal) AS total_salary FROM emp;

-- 平均值
SELECT AVG(sal) AS avg_salary FROM emp;

-- 最大值
SELECT MAX(sal) AS max_salary FROM emp;

-- 最小值
SELECT MIN(sal) AS min_salary FROM emp;

2. 分组函数注意事项

  1. 必须先分组后使用:如果没有明确分组,则默认整表为一组
  2. 自动忽略NULL值:不需要特别处理NULL
  3. COUNT的区别
    -- 统计有佣金的人数(忽略NULL)
    SELECT COUNT(comm) FROM emp;
    
    -- 统计总行数(不忽略NULL)
    SELECT COUNT(*) FROM emp;
    
  4. 不能在WHERE中直接使用
    -- 错误写法
    SELECT ename FROM emp WHERE sal > AVG(sal);
    
    -- 正确写法(使用子查询)
    SELECT ename FROM emp WHERE sal > (SELECT AVG(sal) FROM emp);
    

三、DISTINCT关键字

用于去除查询结果中的重复记录。

基本用法

-- 单列去重
SELECT DISTINCT job FROM emp;

-- 多列联合去重
SELECT DISTINCT job, deptno FROM emp;

注意事项

  1. DISTINCT必须放在所有字段前面
  2. DISTINCT对NULL值也会去重
  3. 可以与分组函数结合使用
    -- 计算不同职位的数量
    SELECT COUNT(DISTINCT job) AS job_types FROM emp;
    

四、函数综合应用示例

-- 综合应用:格式化员工信息,计算奖金,统计部门数据
SELECT 
    deptno,
    COUNT(*) AS emp_count,
    CONCAT('Dept-', deptno) AS dept_name,
    ROUND(AVG(sal), 2) AS avg_salary,
    SUM(CASE WHEN job = 'SALESMAN' THEN sal*1.5 ELSE sal END) AS total_compensation
FROM 
    emp
WHERE 
    LENGTH(ename) > 4
GROUP BY 
    deptno
HAVING 
    AVG(sal) > 2000
ORDER BY 
    avg_salary DESC;

掌握这些数据处理函数可以极大地提高SQL查询的灵活性和数据处理能力,是进行复杂数据分析和报表生成的基础。

SQL连接查询详解

连接查询是SQL中最重要和最常用的功能之一,它允许我们从多个表中获取关联数据。下面我将详细讲解各种连接类型,并提供丰富的示例。

一、连接查询基本概念

连接查询是将多个表中的数据通过关联条件组合在一起的查询方式。主要分为两大类:

  1. 内连接(INNER JOIN):只返回满足连接条件的记录
  2. 外连接(OUTER JOIN):返回主表所有记录,从表不满足条件的显示为NULL

二、内连接详解

1. 等值连接

等值连接是最常见的连接类型,使用等号(=)作为连接条件。

示例1:查询员工及其部门信息

-- SQL92语法(老式写法)
SELECT e.ename, d.dname
FROM emp e, dept d
WHERE e.deptno = d.deptno;

-- SQL99语法(推荐)
SELECT e.ename, d.dname
FROM emp e
INNER JOIN dept d
ON e.deptno = d.deptno;

示例2:查询在NEW YORK工作的员工

SELECT e.ename, e.job, d.loc
FROM emp e
JOIN dept d ON e.deptno = d.deptno
WHERE d.loc = 'NEW YORK';

2. 非等值连接

连接条件不是等号,而是其他比较运算符(BETWEEN, >, <等)。

示例:查询员工薪资等级

SELECT e.ename, e.sal, s.grade
FROM emp e
JOIN salgrade s
ON e.sal BETWEEN s.losal AND s.hisal;

3. 自连接

将同一张表当作两张表来连接查询。

示例:查询每个员工及其经理名字

SELECT 
    a.ename AS employee_name,
    b.ename AS manager_name
FROM emp a
JOIN emp b
ON a.mgr = b.empno;

三、外连接详解

外连接会保留主表的所有记录,即使从表中没有匹配的记录。

1. 左外连接(LEFT JOIN)

保留左表(主表)的所有记录,右表不匹配的显示NULL。

示例:查询所有部门及员工(包括没有员工的部门)

SELECT d.deptno, d.dname, e.ename
FROM dept d
LEFT JOIN emp e
ON d.deptno = e.deptno;

2. 右外连接(RIGHT JOIN)

保留右表(主表)的所有记录,左表不匹配的显示NULL。

示例:查询所有员工及部门信息(包括未分配部门的员工)

SELECT e.ename, d.dname
FROM emp e
RIGHT JOIN dept d
ON e.deptno = d.deptno;

3. 左右连接转换

任何右连接都可以转换为左连接,只需调换表顺序:

-- 这两个查询等价
SELECT e.ename, d.dname FROM emp e RIGHT JOIN dept d ON e.deptno = d.deptno;
SELECT d.dname, e.ename FROM dept d LEFT JOIN emp e ON d.deptno = e.deptno;

四、多表连接

可以连接三张或更多表,支持混合使用内外连接。

示例1:查询员工、部门及薪资等级

SELECT e.ename, d.dname, s.grade
FROM emp e
JOIN dept d ON e.deptno = d.deptno
JOIN salgrade s ON e.sal BETWEEN s.losal AND s.hisal;

示例2:复杂多表连接(包含内外连接混合)

SELECT 
    e.ename AS employee,
    m.ename AS manager,
    d.dname AS department,
    s.grade AS salary_grade
FROM emp e
LEFT JOIN emp m ON e.mgr = m.empno  -- 左连接获取经理信息(允许经理为NULL)
JOIN dept d ON e.deptno = d.deptno  -- 内连接获取部门信息
JOIN salgrade s ON e.sal BETWEEN s.losal AND s.hisal;  -- 内连接获取薪资等级

五、连接查询注意事项

  1. 性能考虑:连接操作可能很耗资源,应确保连接条件字段有索引
  2. 歧义列名:多表连接时,相同列名需用表名或别名限定
    SELECT e.ename, d.deptno  -- 错误,deptno在两张表都存在
    SELECT e.ename, d.deptno AS dept_no  -- 正确
    
  3. 连接条件:确保连接条件合理,避免产生笛卡尔积(不加条件的连接)
  4. NULL处理:外连接中不匹配的列会显示NULL,注意处理

六、特殊连接场景

1. 全外连接(FULL OUTER JOIN)

返回左右两表的全部记录,不匹配的显示NULL。MySQL不直接支持,但可通过UNION实现:

SELECT e.ename, d.dname
FROM emp e
LEFT JOIN dept d ON e.deptno = d.deptno
UNION
SELECT e.ename, d.dname
FROM emp e
RIGHT JOIN dept d ON e.deptno = d.deptno
WHERE e.empno IS NULL;

2. 交叉连接(CROSS JOIN)

返回两表的笛卡尔积(所有可能的组合),慎用:

-- 以下两种写法等价
SELECT e.ename, d.dname FROM emp e CROSS JOIN dept d;
SELECT e.ename, d.dname FROM emp e, dept d;

掌握这些连接查询技术,你就能处理绝大多数多表数据查询需求了。实际应用中,建议先明确需要哪些数据,再设计合适的连接方式。

SQL LIMIT 子句详解

LIMIT 是 SQL 中用于限制查询结果数量的子句,特别常用于分页查询。下面我将详细讲解 LIMIT 的使用方法、分页实现原理以及 SQL 语句的执行顺序。

一、LIMIT 基本用法

1. 基本语法格式

SELECT 列名 FROM 表名 LIMIT [offset,] row_count;

SELECT 列名 FROM 表名 LIMIT row_count OFFSET offset;

2. 常用示例

(1) 查询前N条记录
-- 查询前5名员工
SELECT ename, sal FROM emp ORDER BY sal DESC LIMIT 5;

-- 等价写法
SELECT ename, sal FROM emp ORDER BY sal DESC LIMIT 0, 5;
(2) 从指定位置开始查询
-- 从第3条记录开始,查询5条记录(即第3-7条)
SELECT ename, sal FROM emp ORDER BY sal DESC LIMIT 2, 5;

-- 等价写法(OFFSET语法)
SELECT ename, sal FROM emp ORDER BY sal DESC LIMIT 5 OFFSET 2;

二、分页查询实现

1. 分页基本公式

// Java中的分页计算
int pageNo = 3;    // 当前页码
int pageSize = 10; // 每页记录数
int startIndex = (pageNo - 1) * pageSize;

// SQL语句
String sql = "SELECT * FROM table LIMIT " + startIndex + ", " + pageSize;

2. 分页示例

假设每页显示3条记录:

-- 第1页 (记录1-3)
SELECT ename, job FROM emp ORDER BY empno LIMIT 0, 3;

-- 第2页 (记录4-6)
SELECT ename, job FROM emp ORDER BY empno LIMIT 3, 3;

-- 第3页 (记录7-9)
SELECT ename, job FROM emp ORDER BY empno LIMIT 6, 3;

3. 实际应用案例

-- 分页查询薪资最高的员工(每页5条)
-- 第一页
SELECT ename, sal FROM emp ORDER BY sal DESC LIMIT 0, 5;

-- 第二页
SELECT ename, sal FROM emp ORDER BY sal DESC LIMIT 5, 5;

-- 第三页
SELECT ename, sal FROM emp ORDER BY sal DESC LIMIT 10, 5;

三、SQL语句执行顺序

完整的SELECT语句结构:

SELECT 
    [DISTINCT] 列名
FROM
    表名
[WHERE 条件]
[GROUP BY 分组字段]
[HAVING 分组后条件]
[ORDER BY 排序字段]
[LIMIT 分页参数];

执行顺序

  1. FROM - 确定数据来源
  2. WHERE - 对原始数据过滤
  3. GROUP BY - 分组
  4. HAVING - 对分组结果过滤
  5. SELECT - 选择要显示的列
  6. ORDER BY - 排序
  7. LIMIT - 限制结果数量

执行顺序示例

SELECT 
    deptno, COUNT(*) AS emp_count
FROM
    emp
WHERE
    sal > 1000
GROUP BY
    deptno
HAVING
    COUNT(*) > 2
ORDER BY
    emp_count DESC
LIMIT 2;

执行步骤解析:

  1. 从emp表获取数据
  2. 筛选sal > 1000的员工
  3. 按deptno分组
  4. 筛选员工数>2的部门
  5. 选择显示deptno和计数结果
  6. 按员工数降序排序
  7. 只返回前2条记录

四、LIMIT 高级用法

1. 与子查询结合使用

-- 查询薪资第6-10高的员工
SELECT ename, sal
FROM emp
ORDER BY sal DESC
LIMIT 5, 5;

2. 随机抽样数据

-- 随机获取5条记录
SELECT ename, job FROM emp ORDER BY RAND() LIMIT 5;

3. 分页查询优化

对于大数据表,使用WHERE条件优化分页:

-- 效率低的写法
SELECT * FROM large_table LIMIT 1000000, 10;

-- 效率高的写法(假设id是主键且有序)
SELECT * FROM large_table 
WHERE id > (SELECT id FROM large_table ORDER BY id LIMIT 1000000, 1)
ORDER BY id LIMIT 10;

五、使用建议

  1. 总是与ORDER BY一起使用:确保分页结果有序且一致
  2. 避免大偏移量:对于LIMIT 100000,10这样的查询,考虑使用WHERE条件优化
  3. 考虑使用游标:对于需要连续分页的应用,游标可能更高效
  4. 前端分页:对于小数据集,可以考虑在前端实现分页

LIMIT是SQL中实现分页查询的核心技术,理解其原理和优化方法对于开发高效的数据查询功能至关重要。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值