更多描述
一个学校中的每个学生属于一个班级,如何获取每个班的前三名
假设有学生表 student(id, score, class_id)
,score
代表分数,class_id
代表班级,如何写出 SQL 取每个班级分数前三名
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 7(opens new window)
Author
回答者: shfshanyue(opens new window)
通过一个自连接可以获取到
select id, class_id, score from student s1 where (
-- 对比每行 student,通过自连接找到该班中比该 student 分数要高与等于的人数,即排名
select count(*) from student s2 where s2.class_id = s1.class_id and s2.score >= s1.score
) = 1
Author
回答者: Skylor-Tang(opens new window)
select id, class_id, score from student s1 where exists(select count(*) from student s2 where s2.score >= s1.score and s2.class_id=s1.class_id group by s2.class_id having count(*)<=3) order by class_id, score desc;
更多描述
一个学校中的每个学生属于一个班级,如何获取每个班的前三名
假设有学生表 student(id, score, class_id)
,score
代表分数,class_id
代表班级,如何写出 SQL 取每个班级分数前一名
与 Top n(opens new window) 类似,不过有更简单的方法
(当然考虑到并列情况就会复杂很多
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 8(opens new window)
Author
回答者: shfshanyue(opens new window)
单纯的获取 Top 1,只需要 group by
加聚合函数,不过复杂的还是参考 top n(opens new window)
select class_id, max(score) from student group by class_id
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 25(opens new window)
Author
回答者: shfshanyue(opens new window)
使用 jsonb_pretty
函数,示例如下
> select jsonb_pretty('{"a": {"b": 4}}'::jsonb)
+----------------+
| jsonb_pretty |
|----------------|
| { |
| "a": { |
| "b": 4 |
| } |
| } |
+----------------+
SELECT 1
Time: 0.018s
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 159(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 181(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 208(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 209(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 210(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 213(opens new window)
Author
回答者: shfshanyue(opens new window)
使用 select version()
postgres 示例如下:
postgres@db:school> select version()
+---------------------------------------------------------------------------------------+
| version |
|---------------------------------------------------------------------------------------|
| PostgreSQL 12.1 on x86_64-pc-linux-musl, compiled by gcc (Alpine 8.3.0) 8.3.0, 64-bit |
+---------------------------------------------------------------------------------------+
SELECT 1
Time: 0.028s
mysql 示例如下:
> select version()
+-------------+
| version() |
|-------------|
| 5.6.16-log |
+-------------+
1 row in set
Time: 0.003s
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 214(opens new window)
Author
回答者: shfshanyue(opens new window)
隔离级事关并发事务的隔离机制,ANSI SQL 中定义了四种隔离级,分别是
- Read Uncommited
- Read Committed
- Repetable Read
- Serializable
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 223(opens new window)
Author
回答者: shfshanyue(opens new window)
PostgresSQL 的最大连接数与当前连接数
-- 最大连接数
show max_connections;
-- 当前连接数
select count(*) from pg_stat_activity;
mysql 的最大连接数与当前连接数
-- 最大连接数
show variables like 'max_connections';
-- 当前连接数
show full processlist;
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 224(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 225(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 227(opens new window)
Author
回答者: shfshanyue(opens new window)
多个事务对资源的交替顺序访问,如
事务 1,访问 A 表,A 表锁住,访问 B 表,此时 B 表却被事务 2 锁住,等待 事务 2,访问 B 表,B 表锁住,访问 A 表,此时 A 表却被事务 1 锁住,等待
由此观之,此死锁出现的条件极为苛刻
- 并发,产生多个事务
- 顺序,对相同资源的不同顺序访问 (干嘛要不同顺序呀)
- 时机,恰好两个事物都刚刚走完了第一步
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 230(opens new window)
Author
回答者: shfshanyue(opens new window)
以下答案仅说明在 postgres 中的情况:
在 postgres 有三种 varchar
,char
以及 text
,其中三者没有性能差异,见官方文档
Different from other database systems, in PostgreSQL, there is no performance difference among three character types. In most situation, you should use text or varchar, and varchar(n) if you want PostgreSQL to check for the length limit
所以,选择 text
是最好的方案
更多描述
当多并发场景下有以下事务执行 (计数器自增),会出现什么问题
begin;
-- select count from counter;
update counter set count = count + 1 where id = 1;
commit;
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 234(opens new window)
Author
回答者: shfshanyue(opens new window)
如果在 pg 下:
如果隔离级为 RC,则多个事务在更新同一行时,会受到阻塞 (Row Lock) 如果隔离级为 RR,则多个事务在更新同一行时,会报错
could not serialize access due to concurrent update
mysql 未测试
更多描述
一个计数器,自增一
begin;
select count from user;
-- 根据以上 SQL 查询出来的 count 来进行自增
-- 如果在此次事务中,已有多次事务对 count 进行了多次更改怎么办?
update user set count = $count + 1 where id = 1;
commit;
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 235(opens new window)
Author
回答者: shfshanyue(opens new window)
在并发情况下会出现问题,先查看本次事务的流程
- 查询当前计数 -> 此时为 10
- 对当前计数自增 -> 此时为 11?在自增时,有可能被多并发的其它事务已经自增到 100 了,此时若设置为 11,肯定有问题
如何解决?
要在第一步时加锁,同一时间只放行一个事务,可以设置分布式锁和悲观锁
- 分布式锁 (redis):
SET LOCK_KEY RANDOM_VALUE EX 100 NX
- 悲观锁:
select count from user for update
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 272(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 277(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 333(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 334(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 335(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 336(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 343(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 344(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 345(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 348(opens new window)
Author
回答者: shfshanyue(opens new window)
假设在 user
表简历索引 (name, age)
Index Scan 指查询中除利用索引外还有额外的查询条件,如 (name, age) 是索引,但是 sex 不是
select * from user where name = '张三' and age = 10 and sex = 'MALE'
Index Only Scan 指查询中可以全部利用索引,如 (name, age) 都是索引
select * from user where name = '张三' and age = 10
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 371(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 384(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 386(opens new window)
Author
回答者: shfshanyue(opens new window)
假设一个用户表 User(id primary, name unique, age)
,此时 name 为唯一索引。
聚合索引: select * from User where id in (1, 2, 3, 4, 5)
,由于 id 都在 B+ tree
的叶子节点,不需要去磁盘进一步 IO,称作聚合索引
假设有一个需求: 根据 name 查询该人对应的 age,在现有情况下是这样的:
- 根据 name 上的
B+ tree
快速找到该行对应的 id - 根据 id 上的
B+ tree
快速找到该行对应的 age,此时需要通过主键 id 进一步 IO,称作回表
如果建立一个联合索引 (name, age),直接在 name 上的 B+ tree
就可以找到它对应的 age,省了回表操作,称为覆盖索引
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 445(opens new window)
Issue
欢迎在 Gtihub Issue 中回答此问题: Issue 467(opens new window)
Author
回答者: shfshanyue(opens new window)
SELECT * FROM User LIMIT 1000000, 10
如上语句,跳过 1000000 行用户,取其后的 10 个用户。其中 offset 过大,将会进行 1000000 行扫描,导致磁盘 IO 多大,拖垮数据库
解决方案也很简单: 取出这 10 个用户的 ID,由于查询 ID 使用聚簇索引,避免 100000 行的磁盘 IO,再通过子查询找到这 10 个用户的所有数据