一、MySQL 语法基础
MySQL 使用三值逻辑 —— TRUE, FALSE 和 UNKNOWN。任何与 NULL 值进行的比较都会与第三种值 UNKNOWN 做比较。这个“任何值”包括 NULL 本身!这就是为什么 MySQL 提供 IS NULL 和 IS NOT NULL 两种操作来对 NULL 特殊判断。
-- 先删旧表,方便反复测试
DROP TABLE IF EXISTS orders;
DROP TABLE IF EXISTS users;
-- 用户表
CREATE TABLE users (
id INT PRIMARY KEY,
name VARCHAR(50) NOT NULL,
age INT NULL
) ;
-- 订单表
CREATE TABLE orders (
id INT PRIMARY KEY,
user_id INT NOT NULL,
amount DECIMAL(10,2) NOT NULL,
status TINYINT NOT NULL DEFAULT 0,
KEY idx_user_id (user_id)
) ;
-- 用户数据
INSERT INTO users (id, name, age) VALUES
(1, '张三', 20),
(2, '李四', 25),
(3, '王五', NULL),
(4, '赵六', 30);
-- 订单数据
-- 注意:user_id = 5 的用户在 users 表里不存在
INSERT INTO orders (id, user_id, amount, status) VALUES
(1, 1, 100, 1),
(2, 1, 200, 1),
(3, 2, 300, 0),
(4, 5, 400, 1);
(一)、查询基础
(二)、连接
inner join
-- 找出所有有有效订单的用户
SELECT u.id, u.name, o.id AS order_id, o.amount
FROM users u
INNER JOIN orders o
ON u.id = o.user_id
and o.status = 1;
-- 找出所有有有效订单的用户
SELECT u.id, u.name, o.id AS order_id, o.amount
FROM users u
INNER JOIN orders o
ON u.id = o.user_id
where o.status = 1;
上面这两种写法都可以,但是建议 on 里写关联条件,where 里写过滤条件
-- 找出有 status=1 订单的用户
SELECT u.id, u.name
FROM users u
INNER JOIN orders o ON u.id = o.user_id
WHERE o.status = 1;
-- 找出有 status=1 订单的用户
SELECT u.id, u.name
FROM users u
WHERE EXISTS (SELECT 1
FROM orders o
WHERE o.user_id = u.id
AND o.status = 1);
如果 select 里只有一张表的字段,建议用 exists 语义更清晰
LEFT JOIN 保留左表所有行,右表无匹配时字段填 NULL。
left join 过滤左表的条件写在 on 里和 where 里一样,都是过滤用户
-- 展示 id=1 的那个用户及其所有订单,没订单的显示null
SELECT u.id, u.name, o.id AS order_id, o.amount
FROM users u
LEFT JOIN orders o
ON u.id = o.user_id
and u.id = 1;
-- 同上
SELECT u.id, u.name, o.id AS order_id, o.amount
FROM users u
LEFT JOIN orders o
ON u.id = o.user_id
where u.id = 1;
where 过滤右边条件=inner join,找出有有效订单的客户
-- 找出有有效订单的客户(不建议使用)
SELECT u.id, u.name, o.id AS order_id, o.amount
FROM users u
LEFT JOIN orders o
ON u.id = o.user_id
WHERE o.status = 1;
-- 找出所有用户+status=1的订单,其他订单显示null
SELECT u.id, u.name, o.id AS order_id, o.amount
FROM users u
LEFT JOIN orders o
ON u.id = o.user_id
AND o.status = 1;
警告
不要写 LEFT JOIN ... WHERE 右表字段 = x —— 这是最常见的语义陷阱,它既不是真正的 LEFT JOIN,又让代码意图模糊。
根本原因: LEFT JOIN 时,未匹配的右表字段会被填成 NULL。任何 右表字段 = 值 或 右表字段 > 值 这类条件,都会把 NULL 行排除掉。
exists
判断 A 表的数据是否在 B 表中存在。
not exists
写法 A:NOT EXISTS
-- 找出没有任何订单的用户
SELECT u.id, u.name
FROM users u
WHERE NOT EXISTS (SELECT 1
FROM orders o
WHERE o.user_id = u.id);
写法 B:LEFT JOIN + IS NULL(上一轮提过的反连接)
-- 等价写法
SELECT u.id, u.name
FROM users u
LEFT JOIN orders o ON u.id = o.user_id
WHERE o.id IS NULL;
limit
limit 和 offset 后面只接收正整数
distinct 和 group by 去重有什么区别?
SQL 标准要求 select 里的非聚合字段,必须出现在 group by 里
group by 就是一行行拿出来分组。
having
过滤分组。
窗口函数 vs group by 能否保留原始列
窗口函数也是分组计算,但不会向GROUP BY那样“把一组压缩成一行”;它保留原始每一行,在每行旁边额外算出一个结果。
-- 找出每个用户订单最高金额的那一行
select *
from (select id,
user_id,
amount,
row_number() over (partition by user_id order by amount desc) as rn
from orders) t
where rn = 1;
-- 每个用户的最高金额这个数值
SELECT user_id, MAX(amount) AS max_amount
FROM orders
GROUP BY user_id;
-- 按用户分组,计算每个用户订单总金额
SELECT user_id, SUM(amount) AS user_total
FROM orders
GROUP BY user_id;
--订单最多的企业
select customer_number from Orders group by customer_number order by count(*) desc limit 1
--订单最多的企业(可并列)
select customer_number from (
select customer_number ,dense_rank() over (order by count(order_number) desc ) as rk
from Orders
group by customer_number
) as a where a.rk=1
