SQL練習2

牛客網的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
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章