牛客網的sql真的很值得練習,感興趣戳:https://www.nowcoder.com/ta/sql。 有難度選擇(入門、簡單、中等、較難、困難)、做題模式選擇(練習和考試)以及兩種數據庫選擇(MySql8 和 sqlite3)。 我都是按順序+練習模式+MySQL 另外做完題目可以分享自己的思路,當然做不出來也可以參考別人的思路(題解或者討論區,真的很多大佬!)。 以下總結一些知識點,方便翻閱。 創建 題目描述:對於如下表actor,其對應的數據爲:
actor_id | first_name | last_name | last_update |
---|---|---|---|
1 | PENELOPE | GUINESS | 2006-02-15 12:34:33 |
2 | NICK | WAHLBERG | 2006-02-15 12:34:33 |
請你創建一個actor_name表,並且將actor表中的所有first_name以及last_name導入該表. actor_name表結構如下:
列表 | 類型 | 是否爲NULL | 含義 |
---|---|---|---|
first_name | varchar(45) | not null | 名字 |
last_name | varchar(45) | not null | 姓氏 |
#我的答案-過
create table if not exists actor_name (
first_name varchar(45) not null,
last_name varchar(45) not null
);
insert into actor_name select first_name,last_name from actor;
#知識點:MySQL創建數據表的三種方法:
①常規創建,如上sql
create table if not exists 目標表
②複製創建(表結構)
create 目標表 like 來源表
注意:只是參照來源表創建與之相同表結構的目標表,來源表的數據不會複製
③將查詢table1的結果集來創建table2
create table if not exists table2
(
first_name varchar(45) not null,
last_name varchar(45) not null
)
select first_name,last_name
from table1
題目描述:創建一個actor表,包含如下列信息
列表 | 類型 | 是否爲NULL | 含義 |
---|---|---|---|
actor_id | smallint(5) | not null | 主鍵id |
first_name | varchar(45) | not null | 名字 |
last_name | varchar(45) | not null | 姓氏 |
last_update | date | not null | 日期 |
#我的答案-過
create table actor
(actor_id smallint(5) not null primary key ,
first_name varchar(45) not null ,
last_name varchar(45) not null,
last_update date not null
)
#更讚的答案
create table if not exists `actor`(
actor_id smallint(5) primary key not null comment '主鍵id',
first_name varchar(45) not null comment '名字',
last_name varchar(45) not null comment '姓氏',
last_update date not null comment '日期'
)engine=innodb default charset=utf8;
#知識點1:comment註釋(https://blog.csdn.net/chamtianjiao/article/details/6698690)
①使用註釋
創建
create table test1
(
field_name int comment '字段的註釋'
)comment='表的註釋';
修改
alter table test1 comment '修改後的表的註釋';
alter table test1 modify column field_name int comment '修改後的字段註釋';--注意:字段名和字段類型照寫就行
②查看註釋
表的註釋查看
--在生成的SQL語句中看
show create table test1;
--在元數據的表裏面看
use information_schema;
select * from TABLES where TABLE_SCHEMA='my_db' and TABLE_NAME='test1'
字段的註釋查看
show full columns from test1;
--在元數據的表裏面看
select * from COLUMNS where TABLE_SCHEMA='my_db' and TABLE_NAME='test1'
#知識點2:Mysql常用的數據存儲引擎(https://blog.csdn.net/qq_29168493/article/details/79066399)
①查看數據存儲引擎
SHOW ENGINES
Support列的值表示某種引擎是否能使用:YES表示可以使用、NO表示不能使用、DEFAULT表示該引擎爲當前默認的存儲引擎。MySQL目前默認的存儲引擎是innodb,故上述更讚的SQL裏面可以省略,但是規範寫法,你會知道更多,不是嗎?
②常用數據存儲引擎介紹--後續補
題目描述:在audit表上創建外鍵約束,其emp_no對應employees_test表的主鍵id。 (以下2個表已經創建了)
1
2
3
4
5
6
7
8
9
10
11
12
|
CREATE TABLE employees_test(
ID
INT
PRIMARY KEY NOT NULL,
NAME TEXT NOT NULL,
AGE
INT
NOT NULL,
ADDRESS
CHAR
(50),
SALARY REAL
);
CREATE TABLE audit(
EMP_no
INT
NOT NULL,
create_date datetime NOT NULL
);
|
alter table audit add constraint fk_emp_no
foreign key (emp_no) references employees_test(id);
#外鍵約束
MySQL有兩種常用的引擎類型:MyISAM和InnoDB。目前只有InnoDB引擎類型支持外鍵約束。
#外鍵創建方式
[CONSTRAINT symbol] FOREIGN KEY [id] (index_col_name, ...)
REFERENCES tbl_name (index_col_name, ...)
[ON DELETE {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}]
[ON UPDATE {RESTRICT | CASCADE | SET NULL | NO ACTION | SET DEFAULT}]
①建表的時候創建外鍵約束
CREATE TABLE t_book(
id int primary key auto_increment,
bookName varchar(20),
author varchar(10),
price decimal(6,2),
bookTypeId int,
constraint `fk` foreign key (`bookTypeId`) references `t_bookType`(`id`)
);
②建表完成後創建外鍵約束,例子見本題答案。
題目描述:
針對如下表actor結構創建索引:
(注:在 SQLite 中,除了重命名錶和在已有的表中添加列,ALTER TABLE 命令不支持其他操作,mysql支持ALTER TABLE創建索引)
1
2
3
4
5
|
CREATE TABLE actor (
actor_id smallint(5) NOT NULL PRIMARY KEY,
first_name varchar(45) NOT NULL,
last_name varchar(45) NOT NULL,
last_update datetime NOT NULL);
|
對first_name創建唯一索引uniq_idx_firstname,對last_name創建普通索引idx_lastname
create unique index uniq_idx_firstname on actor(first_name) ;
create index idx_lastname on actor(last_name) ;
#Mysql創建索引有三種方式:http://c.biancheng.net/view/2605.html
題目描述:構造一個觸發器audit_log,在向employees_test表中插入一條數據的時候,觸發插入相關的數據到audit中。 CREATE TABLE employees_test( ID INT PRIMARY KEY NOT NULL, NAME TEXT NOT NULL, AGE INT NOT NULL, ADDRESS CHAR(50), SALARY REAL ); CREATE TABLE audit( EMP_no INT NOT NULL, NAME TEXT NOT NULL );
#這個答案在oj是過的,但是我本機提示:1465 - Triggers can not be created on system tables??
CREATE TRIGGER audit_log AFTER INSERT ON employees_test
for each row
BEGIN
INSERT INTO audit VALUES (NEW.ID, NEW.NAME);
END;
#創建觸發器(名稱)什麼表的什麼事件的什麼時候執行什麼內容
create TRIGGER 觸發器名稱
[after | before][Insert | Delete | Update] on table_name
for each row
BEGIN
執行的內容; --英文分號
--如果執行內容需要獲取觸發前/後表table_name的數據可以使用NEW/OLD關鍵字訪問
END
查詢 題目描述 獲取所有部門中當前(dept_emp.to_date = '9999-01-01')員工當前(salaries.to_date='9999-01-01')薪水最高的相關信息,給出dept_no, emp_no以及其對應的salary,按照部門編號升序排列。 題目有點表述不清,這裏理解爲:求出每個部門當前員工的最高薪水。 CREATE TABLE `dept_emp` ( `emp_no` int(11) NOT NULL, `dept_no` char(4) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`dept_no`)); CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL, `salary` int(11) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`from_date`)); 如插入: INSERT INTO dept_emp VALUES(10001,'d001','1986-06-26','9999-01-01'); INSERT INTO dept_emp VALUES(10002,'d001','1996-08-03','9999-01-01'); INSERT INTO dept_emp VALUES(10003,'d001','1996-08-03','1997-08-03'); INSERT INTO salaries VALUES(10001,90000,'1986-06-26','1987-06-26'); INSERT INTO salaries VALUES(10001,88958,'2002-06-22','9999-01-01'); INSERT INTO salaries VALUES(10002,72527,'1996-08-03','1997-08-03'); INSERT INTO salaries VALUES(10002,72527,'2000-08-02','2001-08-02'); INSERT INTO salaries VALUES(10002,72527,'2001-08-02','9999-01-01'); INSERT INTO salaries VALUES(10003,90000,'1996-08-03','1997-08-03');
#原本答案--不過
select dept_emp.dept_no,dept_emp.emp_no,max(salary) from salaries
inner join dept_emp on dept_emp.emp_no = salaries.emp_no
where salaries.to_date='9999-01-01'
and dept_emp.to_date = '9999-01-01'
group by dept_emp.dept_no
order by dept_emp.dept_no asc
#刷了一波評論區後,知道了:使用GROUP BY子句時,SELECT子句中只能有聚合鍵、聚合函數、常數。簡單來說就是我的答案emp_no 和 max(salary) 可能不是對應的。
#如何解決?看了下題解和討論區,總結了兩種解法
#解法1:
select b.dept_no,b.emp_no,salaries.salary from salaries
inner join dept_emp b on b.emp_no = salaries.emp_no
where b.to_date = '9999-01-01' and salaries.to_date='9999-01-01'
and salaries.salary = (select max(salary) from salaries
inner join dept_emp a on a.emp_no = salaries.emp_no
where salaries.to_date='9999-01-01' and a.to_date = '9999-01-01' and a.dept_no = b.dept_no)
order by b.dept_no asc
#這個解法有個關鍵點,是內查詢需要添加查詢條件a.dept_no = b.dept_no,由外查詢確定需要查詢的具體某個部門的最高薪水
#解法2:窗口函數
select dept_no, emp_no ,salary from (select dept_emp.dept_no,dept_emp.emp_no ,salary,
rank()over(partition by dept_no order by salary desc) as rk
from salaries inner join dept_emp on salaries.emp_no =dept_emp.emp_no
and salaries.to_date='9999-01-01' and dept_emp.to_date='9999-01-01' ) as t
where rk = 1
order by dept_no
#知識點:
https://zhuanlan.zhihu.com/p/138282683
題目描述 對所有員工的當前(to_date='9999-01-01')薪水按照salary進行按照1-N的排名,相同salary並列且按照emp_no升序排列 CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL, `salary` int(11) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`from_date`)); 結果示例:
emp_no | salary | t_rank |
---|---|---|
10005 | 94692 | 1 |
10009 | 94409 | 2 |
10010 | 94409 | 2 |
10001 | 88958 | 3 |
10007 | 88070 | 4 |
10004 | 74057 | 5 |
10002 | 72527 | 6 |
10003 | 43311 | 7 |
10006 | 43311 | 7 |
10011 | 25828 | 8 |
SELECT s1.emp_no, s1.salary, COUNT(DISTINCT s2.salary) AS t_rank
FROM salaries AS s1, salaries AS s2
WHERE s1.to_date = '9999-01-01'
AND s2.to_date = '9999-01-01'
AND s1.salary <= s2.salary
GROUP BY s1.emp_no
ORDER BY s1.salary DESC, s1.emp_no ASC
#思路分析:
題目描述:針對salaries表emp_no字段創建索引idx_emp_no,查詢emp_no爲10005, 使用強制索引。 CREATE TABLE `salaries` ( `emp_no` int(11) NOT NULL, `salary` int(11) NOT NULL, `from_date` date NOT NULL, `to_date` date NOT NULL, PRIMARY KEY (`emp_no`,`from_date`)); create index idx_emp_no on salaries(emp_no);
SELECT * FROM salaries FORCE INDEX (idx_emp_no) WHERE emp_no = 10005
#強制索引:https://www.jb51.net/article/49807.htm
刪除
題目描述:
刪除emp_no重複的記錄,只保留最小的id對應的記錄。
CREATE TABLE IF NOT EXISTS titles_test (
id int(11) not null primary key,
emp_no int(11) NOT NULL,
title varchar(50) NOT NULL,
from_date date NOT NULL,
to_date date DEFAULT NULL);
insert into titles_test values ('1', '10001', 'Senior Engineer', '1986-06-26', '9999-01-01'),
('2', '10002', 'Staff', '1996-08-03', '9999-01-01'),
('3', '10003', 'Senior Engineer', '1995-12-03', '9999-01-01'),
('4', '10004', 'Senior Engineer', '1995-12-03', '9999-01-01'),
('5', '10001', 'Senior Engineer', '1986-06-26', '9999-01-01'),
('6', '10002', 'Staff', '1996-08-03', '9999-01-01'),
('7', '10003', 'Senior Engineer', '1995-12-03', '9999-01-01');
刪除後titles_test表爲
id | emp_no | title | from_date | to_date |
1 | 10001 | Senior Engineer | 1986-06-26 | 9999-01-01 |
2 | 10002 | Staff | 1996-08-03 | 9999-01-01 |
3 | 10003 | Senior Engineer | 1995-12-03 | 9999-01-01 |
4 | 10004 | Senior Engineer | 1995-12-03 | 9999-01-01 |
#我的答案-報錯: 1093 - You can't specify target table 'titles_test' for update in FROM clause
delete from titles_test where id not in (
select min(id) from titles_test
group by emp_no
)
#知識點:MySQL不允許同時更新或刪除同一張表,即不允許查詢表的結果作爲條件更新或刪除表
#解決方法:把子查詢的結果創建臨時表存儲,並把這個臨時表作爲原表刪除的條件,刪除原表數據。
#正確答案:
delete from titles_test where id not in (select * from(
select min(id) from titles_test
group by emp_no
) as t)
#需要注意:取別名t這步是必須的!不然會報錯:1248 - Every derived table must have its own alias(每個派生出來的表都必須有一個自己的別名)
更新
題目描述:將id=5以及emp_no=10001的行數據替換成id=5以及emp_no=10005,其他數據保持不變,使用replace實現,直接使用update會報錯了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
|
CREATE TABLE titles_test (
id
int
(11) not null primary key,
emp_no
int
(11) NOT NULL,
title varchar(50) NOT NULL,
from_date date NOT NULL,
to_date date DEFAULT NULL);
insert into titles_test values
(
'1'
,
'10001'
,
'Senior Engineer'
,
'1986-06-26'
,
'9999-01-01'
),
(
'2'
,
'10002'
,
'Staff'
,
'1996-08-03'
,
'9999-01-01'
),
(
'3'
,
'10003'
,
'Senior Engineer'
,
'1995-12-03'
,
'9999-01-01'
),
(
'4'
,
'10004'
,
'Senior Engineer'
,
'1995-12-03'
,
'9999-01-01'
),
(
'5'
,
'10001'
,
'Senior Engineer'
,
'1986-06-26'
,
'9999-01-01'
),
(
'6'
,
'10002'
,
'Staff'
,
'1996-08-03'
,
'9999-01-01'
),
(
'7'
,
'10003'
,
'Senior Engineer'
,
'1995-12-03'
,
'9999-01-01'
);
|
REPLACE INTO titles_test
VALUES (5, 10005, 'Senior Engineer', '1986-06-26', '9999-01-01')
#replace into : 根據主鍵/唯一鍵查詢,如有存在則刪除然後新增,否則直接新增。
函數 group_concat():將一組結果連接成字符串顯示,逗號隔開 select dept_no ,group_concat(emp_no SEPARATOR ';') from dept_emp group by dept_no #SEPARATOR 後面跟間隔符,默認是,(英文逗號) 詳見官網:https://dev.mysql.com/doc/refman/8.0/en/aggregate-functions.html#function_group-concat