同学们可以结合本人的另外一篇博客,结合着使用~
使用 SQL 实战处理2020国赛C题数据
1. SQL 简介
SQL (Structured Query Language:结构化查询语言) 是用于用于管理关系数据库管理系统(RDBMS)。 SQL 的范围包括数据插入、查询、更新和删除,数据库模式创建和修改,以及数据访问控制。
2.优势
SQL 面向数据库进行 增删查改,在我们没有接触数据库的时候,如果要对数据进行处理,一般会用到 for 循环进行遍历。比如,我们有一个结构体数组(下文称为表),里面包含着名字,年龄和性别。如果我们要把性别为女的同学查找出来,我们需要使用 for 循环一遍,访问每一个结构体中的性别,进行甄别。这种方法对于几百几千条数据来说耗费时间看似没有差别,但如果是几十万,几百万条数据,时间的耗费则越来越重。
再者,如果存在另外一个表,里面记录着每一同学每次考试的成绩,我们现在需要将两个表进行合并,成为一条包含着名字,性别,年龄以及成绩(假设名字唯一)的数据,这时我们就需要使用两个 for 循环来处理,并构造新的结构体来进行存储,即便其他语言比如 Python 等 不需要构造结构体,但两层的 for 循环带来的时间耗费的提高也是不容小觑的,更何况会存在更多层 for 循环的情形
笔者曾参加过 2020年 的数模国赛,选择的就是大数据问题,说实话,当时没有用到过数据库,用的虽然是 Python 但 处理数据的时候,使用了许多 for 循环,耗费了许多时间,只水了个省二。贴一下当时处理处理数据的一小部分代码,可以看出,不仅数据量大而且处理逻辑复杂,还很容易出错。比赛完之后就觉得如果使用数据库来进行处理,定会有奇效的。果然,几分钟 for 才得到的数据 SQL 一两秒秒了。所以笔者很推荐准备参加数学建模并且选择大数据题目的同学,加强一下数据库的学习以及练习。
# Dep_In_Profit 企业进货税价和
# Dep_In_Price 企业进货价格和
# Dep_In_Rex 企业进货税收和
# Dep_In_Tic_T 企业进货有效发票
# Dep_In_Tic_T_R 负数票据
# Dep_In_Tic_T_S 正数票据
# Dep_In_Tic_F 企业进货无效发票
Dep_In_Profit_M = np.zeros(302)
Dep_In_Price_M = np.zeros(302)
Dep_In_Rex_M = np.zeros(302)
Dep_In_Profit_N = np.zeros(302)
Dep_In_Price_N = np.zeros(302)
Dep_In_Rex_N = np.zeros(302)
Dep_In_Tic_T = np.zeros(302)
Dep_In_Tic_F = np.zeros(302)
Dep_In_Tic_T_R = np.zeros(302)
Dep_In_Tic_T_S = np.zeros(302)
for i in range(0,395175):
if In_items.iloc[i,9] == 1:
Dep_In_Tic_T[In_items.iloc[i, 8]-124] = Dep_In_Tic_T[In_items.iloc[i, 8]-124] + 1
if In_items.iloc[i, 6] < 0:
Dep_In_Profit_N[In_items.iloc[i, 8]-124] = Dep_In_Profit_N[In_items.iloc[i, 8]-124] + In_items.iloc[i, 6]
Dep_In_Price_N[In_items.iloc[i, 8]-124] = Dep_In_Price_N[In_items.iloc[i, 8]-124] + In_items.iloc[i, 4]
Dep_In_Rex_N[In_items.iloc[i, 8]-124] = Dep_In_Rex_N[In_items.iloc[i, 8]-124] + In_items.iloc[i, 5]
Dep_In_Tic_T_R[In_items.iloc[i, 8]-124] = Dep_In_Tic_T_R[In_items.iloc[i, 8]-124] + 1
else:
Dep_In_Profit_M[In_items.iloc[i, 8]-124] = Dep_In_Profit_M[In_items.iloc[i, 8]-124] + In_items.iloc[i, 6]
Dep_In_Price_M[In_items.iloc[i, 8]-124] = Dep_In_Price_M[In_items.iloc[i, 8]-124] + In_items.iloc[i, 4]
Dep_In_Rex_M[In_items.iloc[i, 8]-124] = Dep_In_Rex_M[In_items.iloc[i, 8]-124] + In_items.iloc[i, 5]
Dep_In_Tic_T_S[In_items.iloc[i, 8]-124] = Dep_In_Tic_T_S[In_items.iloc[i, 8]-124] + 1
else:
Dep_In_Tic_F[In_items.iloc[i, 8]-124] = Dep_In_Tic_F[In_items.iloc[i, 8]-124] + 1
3.用武之处
首先是数学建模以及大数据处理方面的应用,当然这方面的应用是 SQL 相较于一般处理方法的优势所在,并且在处理多表级联关系时,SQL 显得更加简洁,for 循环或其他一般方式显得更加复杂以及更容易出错。
其次在应用开发时,无论是 web 应用还是移动端 ,数据库肯定是需要的,用于前后端数据的联系,虽然平时并不会设计很大的数据量,但熟悉 SQL 的基本增删改查也是很有必要哒。
4.入门
1. 增
1.1 增加数据库
create database learn; # learn 为数据库的名称
use learn; # 使用此数据库
set names utf8; # 设置编码
1.2 增加表
CREATE TABLE Persons # 括号里包含着每一个键的名称以及数据类型用逗号隔开
(
PersonID int,
LastName varchar(255),
FirstName varchar(255),
Address varchar(255),
City varchar(255)
# 可在定义之后加上一些约束,之后会提到
);
1.3 增加表中的数据
1.3.1 插入整条数据
insert into Persons # 插入整条数据的时候要与表中的键一一对应
values (1, 'mary', 'Smith', '127.0.0.1', 'Beijing');
insert into Persons
values (3, 'mar', 'Smith', '127.0.4.1', 'Beijing');
insert into Persons
values (4, 'max', 'Smith', '127.0.6.1', 'Tianjin');
insert into Persons
values (5, 'mute', 'Rio', '127.0.6.1', 're');
insert into Persons
values (6, 'mate', 'Linda', '127.1.6.1', 'Tianjin');
insert into Persons
values (2, 'mario', 'Smith', '127.1.0.1', 'Nanjing');
1.3.2 插入指定键的数据
INSERT INTO Persons (PersonID,LastName,City) # 括号中的便是 表中的键 下面插入的 value 需要与之对应
VALUES ('mirry','Smith','Hangzhou');
2. 删
2.1 删除数据库
drop database learn; # 删库需谨慎
2.2 删除表
drop TABLE website; # 删除表
TRUNCATE TABLE table_name # 删除表的数据,表本身存在
2.3 删除索引
ALTER TABLE table_name DROP INDEX index_name; # 适用于MySQL
DROP INDEX index_name; # 适用于 DB2/Oracle
DROP INDEX table_name.index_name; # 适用于 MS SQL Server
3. 改
3.1 表中增加列
ALTER TABLE table_name # 需要指定 表的名称,列的名称 以及 数据类型
ADD column_name datatype
alter table ts
add sex nvarchar(10);
3.2 表中删除列
ALTER TABLE table_name # 需要指定 表的名称,列的名称
DROP COLUMN column_name
alter table ts
drop column sex;
3.3 表中修改列的数据类型
# SQL Server / MS Access 适用
ALTER TABLE table_name # 需要指定 表的名称,列的名称,列要修改成的数据类型
ALTER COLUMN column_name datatype
# My SQL / Oracle 适用
ALTER TABLE table_name # 需要指定 表的名称,列的名称,列要修改成的数据类型
MODIFY COLUMN column_name datatype
alter table ts
modify column sex nvarchar(100);
4. 查
4.1 查询所有记录
select * # * 便是指代所有键
from Persons;
4.2 查询指定行的记录
# 先指定数据库
use learn;
select PersonID,City from Persons;
# 直接访问
select PersonID,City from learn.Persons;
4.3 查询数据库
show databases;
4.4 查询数据库中表的名称
show tables;
5. 主键,外键,UNIQUE 约束
主键是约束标识表中每一条记录的,就相当于我们的身份证,不能为 NULL , 也不能重复,在插入数据的时候必须为之指定,每一个表都有且仅有一个主键。
create table ts(
id int,
name varchar(25),
primary key (id) # 指定主键
);
外键是一个表指向另一个表中的 UNIQUE KEY(唯一约束的键),假设有 每个班任课老师的表 和 每个教职工的表 这两张表。
班级任课老师表
任课老师教职工号(外键) | 老师 | 班级 |
---|---|---|
03 | C | 二班 |
01 | A | 一班 |
03 | C | 三班 |
02 | B | 四班 |
教职工表
老师 | 教职工号(主键) |
---|---|
C | 03 |
B | 02 |
A | 01 |
班级任课老师表中的 任课老师教职工号 指向 教职工表的教职工号。
因此,教职工号作为 教职工的主键(PRIMARY KEY),任课老师教职工号 作为 班级任课老师表 的 外键(FOREIGN KEY)。
# 适用于 MySQL
CREATE TABLE TS
(
id int NOT NULL,
name varchar(25),
P_Id int,
primary key (id), # 指定主键
FOREIGN KEY (P_Id) REFERENCES TES(P_Id)
)
# 适用于SQL Server / Oracle / MS Access
CREATE TABLE TS
(
id int NOT NULL PRIMARY KEY,
name varchar(25),
P_Id int NOT NULL FOREIGN KEY REFERENCES TES(P_Id)
)
UNIQUE 约束唯一标识数据库表中的每条记录,和主键不同,表中允许多个UNIQUE 约束出现,PRIMARY KEY 约束拥有自动定义的 UNIQUE 约束,这地方在处理数据的时候用到的时候没有太多,就当了解一下。
约束的创建
create table ts(
id int NOT NULL,
name varchar(25),
UNIQUE (id) # UNIQUE 约束
);
或者
create table ts(
id int NOT NULL UNIQUE,# UNIQUE 约束
name varchar(25)
);
约束的删除
# MySQL
ALTER TABLE ts
DROP INDEX id
# SQL Server / Oracle / MS Access
ALTER TABLE Persons
DROP CONSTRAINT id
5.进阶
1. 操作符
1.1 WHERE(无法与聚合函数一起使用)
之前举的一个查询为女生的同学的例子,传统方法为 for 循环,这样耗时耗力,使用 where 我们可以很简单的秒了
# 基本语法
SELECT column_name,column_name # 需要查询的列
FROM table_name
WHERE column_name operator value; # 这里为一些条件语句,接下来会提及
1.2 IN
当我们需要查询某一键符合多个值的时候,这个便派上了用场
create table grade(
name nchar(10),
grade int
);
insert into grade values ('a', 100);
insert into grade values ('b', 100);
insert into grade values ('c', 90);
insert into grade values ('a', 100);
insert into grade values ('b', 95);
insert into grade values ('c', 93);
# 基本语法
SELECT column_name(s)
FROM table_name
WHERE column_name IN (value1,value2,...);
# 查询名字是a,b,c中间一人的数据
select * from grade
where name in ('a','b','c'); # 括号里的元素要与键的数据类型相同
# 查询成绩是 90 93 100 之间的同学数据
select * from grade
where grade in (90, 93 ,100);
1.3 BETWEEN
我们需要查询某一键符合在某一范围时,这个便派上了用场
# 基本语法
SELECT column_name(s)
FROM table_name
WHERE column_name BETWEEN value1 AND value2;
select * from grade
where grade between 95 and 100;
1.4 LIKE
LIKE 操作符用于在 WHERE 子句中搜索列中的指定模式,主要就是匹配模式的书写,可能会涉及到正则表达式,下面给出 菜鸟课程 总结的通配符,对正则不熟悉的同学可参考我的另外一篇博文传送门。
通配符 | 匹配对象 |
---|---|
% | 替代 0 个或多个字符 |
_ | 替代一个字符 |
[charlist] | 字符列中的任何单一字符 |
[^charlist] 或 [!charlist] | 不在字符列中的任何单一字符 |
MySQL 中使用 REGEXP 或 NOT REGEXP 运算符 (或 RLIKE 和 NOT RLIKE) 来操作正则表达式
# 查询以 c或d或e开头的同学名字的数据
select * from grade
where name REGEXP '^[cde]';
# 查询以 c到e字母开头的同学名字的数据
select * from grade
where name REGEXP '^[c-e]';
# 查询 不以 c到e字母开头的同学名字的数据
select * from grade
where name REGEXP '^[^c-e]';
# 查询一个字母的同学名字的数据
select * from grade
where name like '_';
2. SELECT DISTINCT
以上面 班级任课老师表 为例,如果我们想要任课老师的教职工号,我们可以用 select 来只对 任课老师教职工号 进行选择,但查询的结果会有重复,C 的教职工号出现两次,我们可以使用 SELECT DISTINCT 进行查询而不会出现重复的情况。一个重要的特点就是每一条查询结果都各不相同。
# 查询参加考试的同学
select distinct name
from grade;
# 查询考试分数的种类
select distinct grade
from grade;
# 查询学生及其分数的种类
select distinct grade,name
from grade;
3. JOIN
join 用于将多个表联系在一起,大致可分为四种join方法
join 类型 | 描述 |
---|---|
INNER JOIN | 如果表中有至少一个匹配,则返回行 |
LEFT JOIN | 即使右表中没有匹配,也从左表返回所有的行 |
RIGHT JOIN | 即使左表中没有匹配,也从右表返回所有的行 |
FULL JOIN | 只要其中一个表中存在匹配,则返回行 |
create table TeaNo(
name varchar(5) NOT NULL,
Tno varchar(5) NOT NULL ,
sex varchar(5) NOT NULL ,
PRIMARY KEY (Tno)
);
create table Teach(
Tno varchar(5) NOT NULL ,
Teacher varchar(2) NOT NULL ,
Class varchar(3) NOT NULL ,
FOREIGN KEY (Tno) references TeaNo(Tno)
);
insert into TeaNo values ('C','03','男');
insert into TeaNo values ('B','02','男');
insert into TeaNo values ('A','01','女');
insert into TeaNo values ('D','04','女');
insert into Teach values ('03', 'C', '二班');
insert into Teach values ('01', 'A', '一班');
insert into Teach values ('03', 'C', '三班');
insert into Teach values ('03', 'B', '四班');
# inner join
select TeaNo.name,TeaNo.sex,Teach.Class
from TeaNo inner join Teach
on TeaNo.Tno = Teach.Tno;
# A,女,一班
# C,男,二班
# C,男,三班
# C,男,四班
# LEFT JOIN B 和 D 老师都在右边找不到对应的信息但仍返回左边一行,空缺地方为NULL
select TeaNo.name,TeaNo.sex,Teach.Class
from TeaNo left outer join Teach
on TeaNo.Tno = Teach.Tno;
# A,女,一班
# B,男,
# C,男,二班
# C,男,三班
# C,男,四班
# D,女,
# Right JOIN
select TeaNo.name,TeaNo.sex,Teach.Class
from TeaNo right outer join Teach
on TeaNo.Tno = Teach.Tno;
# C,男,二班
# A,女,一班
# C,男,三班
# C,男,四班
# FULL JOIN 似乎语法并不支持
4. 别名
在进行查询时,可以通过创建别名让列名称或者表名称的可读性更强。
# 列的别名
SELECT name AS TeacherName
FROM TeaNo;
# 表的别名 多用于多表查询时
SELECT name
FROM TeaNo AS TeacherName;
5. SQL 函数
在数据处理上,正是由于这些可以直接使用的函数,让数据处理更加简便,下列是菜鸟课程上总结的函数
函数名 | 作用 |
---|---|
FORMAT() | 格式化某个字段的显示方式 |
LEN() | 返回某个文本字段的长度 |
ROUND() | 对某个数值字段进行指定小数位数的四舍五入 |
UCASE() | 将某个字段转换为大写 |
LCASE() | 将某个字段转换为小写 |
AVG() | 返回平均值 |
COUNT() | 返回行数 |
NOW() | 返回当前的系统日期和时间 |
FIRST() | 返回第一个记录的值 |
LAST() | 返回最后一个记录的值 |
MAX() | 返回最大值 |
MID() | 从某个文本字段提取字符,MySql 中使用 |
SubString(字段,1,end) | 从某个文本字段提取字符 |
MIN() | 返回最小值 |
SUM() | 返回总和 |
# FORMAT() 括号里的元素都是必须的
SELECT FORMAT(column_name,format) FROM table_name;
# 得到系统时间
select date_format(NOW(),'%Y-%m-%d') As data;
6. HAVING
由于WHERE 关键字无法与聚合函数一起使用,因此引入Having,HAVING 子句可以让我们筛选分组后的各组数据。
# 基本语法
SELECT column_name, aggregate_function(column_name)
FROM table_name
WHERE column_name operator value
GROUP BY column_name
HAVING aggregate_function(column_name) operator value;
# aggregate_function(column_name) 为聚合函数,不能与 where 连用
7. Union 与 Union All
二者用于合并两个或多个 SELECT 语句的结果。
注意事项:UNION 内部的每个 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每个 SELECT 语句中的列的顺序必须相同。
二者不同之处:Union 用于取并集去重,Union All 用于取并集不去重。
create table country1(
Name varchar(10),
Eng varchar(10)
);
create table country2(
Name varchar(10),
Eng varchar(10),
amount int
);
insert into country1 values ('中国', 'CN');
insert into country1 values ('美国', 'USA');
insert into country1 values ('英国', 'UK');
insert into country1 values ('日本', 'Japan');
insert into country2 values ('德国','German',100);
insert into country2 values ('澳大利亚','Australia',100);
insert into country2 values ('俄罗斯','Russia',100);
insert into country2 values ('德国','German',100);
# 取并集 去重
select Name,Eng
from country1
UNION
select Name,Eng
from country2;
# 取并集不去重
select Name,Eng
from country1
UNION ALL
select Name,Eng
from country2;
8.日期问题
日期处理问题是对数据进行提取,分类的重要环节,比如,在数学建模中,我们经常需要对一家公司的收入按月份或者年份进行提取,数据库内置的函数就会有奇效。下列是菜鸟课程总结的一些函数。
函数名称 | 函数用处 |
---|---|
DATE(date) | 提取 date 中的日期部分 |
DATE_ADD(date,INTERVAL expr type) | 从日期加上指定的时间间隔。date 为 合法的日期 ,expr 为 指定的时间间隔 |
DATE_FORMAT() | 用于以不同的格式显示日期/时间数据。 |
DATE_SUB(date,INTERVAL expr type) | 从日期减去指定的时间间隔。date 为 合法的日期 ,expr 为 指定的时间间隔 |
DATEDIFF(date1,date2) | 返回两个日期之间的天数date1 和 date2 参数是合法的日期或日期/时间表达式 |
EXTRACT() | 用于返回日期/时间的单独部分,如年,月,日 |
9. 视图
根据本人的理解,视图就是将一个或者多个表之间关联起来并作为一个虚拟表返回,这个虚拟表是动态改变的,我们下次再次将多个表关联起来时只需要操作视图即可。
在 SQL 中,视图是基于 SQL 语句的结果集的可视化的表。视图包含行和列,就像一个真实的表。视图中的字段就是来自一个或多个数据库中的真实的表中的字段。您可以向视图添加 SQL 函数、WHERE 以及 JOIN 语句,也可以呈现数据,就像这些数据来自于某个单一的表一样。
视图总是显示最新的数据!每当用户查询视图时,数据库引擎通过使用视图的 SQL 语句重建数据。
视图所查询出来的数据只能进行查看,不能增删改。
# 基本语法
CREATE VIEW view_name AS
SELECT column_name(s)
FROM table_name
WHERE condition
10. AND OR
将多个条件结合进行筛选
and : 连接的条件都需要满足
or : 连接的条件有一个满足就可
11. GROUP BY 和 ORDER BY
ORDER BY 为排序操作,根据指定的属性来进行排序,可以指定升序以及降序
# 默认为升序排列
select *
from grade
order by grade;
# 降序排列,先按成绩,成绩一样再按分数
select *
from grade
order by grade,name desc;
12. EXISTS
SELECT column_name(s)
FROM table_name
WHERE EXISTS
(SELECT column_name FROM table_name WHERE condition);
# 首先执行 外部查询 SELECT column_name(s) FROM table_name
# 将查询到的每一条数据传给内查询,看内查询能否查询到结果,查询不到返回Flase,反之为True
# 内查询返回为 True 则保留外查询这一条记录
13.SELECT TOP
用于规定要返回的记录的数目,如果查询返回的结果很多,输出花费比较多,我们可以使用此语句来限定数目
# 适用于 SQL Server / MS Access
SELECT TOP number|percent column_name(s)
FROM table_name;
# 适用于 MySQL
SELECT column_name(s)
FROM table_name
LIMIT number;
# 适用于 Oracle
SELECT column_name(s)
FROM table_name
WHERE ROWNUM <= number;
select *
from Teach
limit 3;
6. 数据导出
# 导出数据
select * from table into outfile '路径';
# 导入数据
load data local infile '路径' into table 表名 fields terminated by ‘\t’
在此之前,需要修改一下数据导出的默认路径
使用 show variables like '%secure%'; 查看secure-file-priv设置,
2. secure_file_prive=null ––限制mysqld 不允许导入导出
secure_file_priv=/path/ – --限制mysqld的导入导出只能发生在默认的/path/目录下
secure_file_priv=’’ – --不对mysqld 的导入 导出做限制
3. 在 mysql 的安转目录下找到 my.ini 文件,修改secure_file_prive 为 '' 使其不对mysqld 的导入 导出做限制
4. 打开计算机管理>>服务与应用程序>>服务>>mysql(版本不同名字可能不同)>>右键重新启动>>完成重启
5. 重复 1 步骤,看看修改是否成功,若没有成功可以试试重启电脑。
6. 之后再进行导入导出操作