一、DBMS (DataBase Management System)
DBMS = 多个数据库(DB) + 管理程序
关系型数据库(RDBMS)就是建立在关系模型基础上的数据库 (如Oracle, mySql, Sql server),SQL就是关系型数据库的查询语言。
相比于SQL,NoSQL泛指非关系型数据库,包括了键值型数据库(如Redis)、文档型数据库(如MongoDB)、搜索引擎(如Elasticsearch)和列存储等,除此以外还包括图形数据库。
列式数据库是相对于行式存储的数据库,Oracle、MySQL、SQL Server等数据库都是采用的行式存储(Row-based),而列式数据库是将数据按照列存储到数据库中,这样做的好处是可以大量降低系统的I/O,适合于分布式文件系统,不足在于功能相对有限。
NoSQL数据库四大类型:键值数据库、列族数据库、文档数据库和图形数据库
二、Sql语句
SQL 分为:数据操作语言 (DML) ,数据查询语言DQL, 数据定义语言 (DDL), 数据控制语言(DCL)
DML——数据操作语言:
- UPDATE - 更新数据库表中的数据 / REPLACE
- DELETE - 从数据库表中删除数据
- INSERT INTO - 向数据库表中插入数据
DQL——数据查询语言:
基本结构是由SELECT子句,FROM子句,WHERE子句组成的查询块:
SELECT <字段名表>
FROM <表或视图名>
WHERE <查询条件>
执行顺序*:
- FROM
- ON
- JOIN
- WHERE
- GROUP BY
- HAVING
- WINDOW functions
- SELECT
- DISTINCT
- UNION
- ORDER BY
- LIMIT and OFFSET
SELECT DISTINCT 列名称 FROM 表名称
SELECT 列名称 FROM 表名称 WHERE 列 运算符 值
/*例子*/
SELECT * FROM Persons WHERE FirstName='Thomas' AND LastName='Carter' ORDER BY Company DESC
INSERT INTO 表名称 VALUES (值1, 值2,....)
INSERT INTO table_name (列1, 列2,...) VALUES (值1, 值2,....)
UPDATE 表名称 SET 列名称 = 新值 WHERE 列名称 = 某值
REPLACE INTO 表名称 VALUES (值1, 值2,....) --有没有都插入
DELETE FROM 表名称 WHERE 列名称 = 值
DDL——数据定义语言:
创建或删除表格,也可以定义索引(键),规定表之间的链接,以及施加表间的约束。
- CREATE DATABASE - 创建新数据库
- ALTER DATABASE - 修改数据库
- CREATE TABLE - 创建新表
- ALTER TABLE - 变更(改变)数据库表 (ADD, DROP, ALTER (Sql server), MODIFY column)
- DROP TABLE - 删除表
- CREATE INDEX - 创建索引(搜索键)
- DROP INDEX - 删除索引
--创建表
CREATE TABLE Persons
(
Id_P int NOT NULL PRIMARY KEY,
LastName varchar(255) NOT NULL,
FirstName varchar(255) UNIQUE,
Address varchar(255),
City varchar(255),
CONSTRAINT uc_PersonID UNIQUE (Id_P,LastName), --多个列定义 UNIQUE 约束
FOREIGN KEY (Id_O) REFERENCES Persons(Id_O) --外键约束
)
--创建索引
CREATE INDEX index_name
ON table_name (column_name)
DCL—数据控制语言 (GRANT 授权,REVOKE,COMMIT 提交,ROLLBACK 回滚)
--查看指定用户的权限
SHOW GRANTS FOR '用户名'@ 'IP地址'
--给用户分派在指定的数据库上的指定的权限
GRANT 权限1, … , 权限n ON 数据库.表名 TO '用户名'@'IP地址'
GRANT CREATE,ALTER,DROP,INSERT,UPDATE,DELETE,SELECT ON mydb1.* TO 'user1'@'localhost';
GRANT ALL ON 数据库.* TO '用户名'@'IP地址'
--撤销权限
REVOKE 权限1, … , 权限n ON 数据库.表名 FROM '用户名'@'IP地址
提交的三种方式
- 显式提交,用COMMIT直接完成提交为显式提交
- 隐式提交,用SQL语句间接完成的提交为隐式提交,这些命令有:ALTER , CONNECT, CREATE, DISCONNECT, DROP, EXIT, GRANT, QUIT, REVOKE
- 自动提交 把AUTOCOMMIT 设为ON 系统自动提交: SET AUTOCOMMIT ON;
1. Join语句
用于根据两个或多个表中的列之间的关系,从这些表中查询数据。
- (INNER) JOIN: 如果表中有至少一个匹配,则返回行
- LEFT JOIN: 即使右表中没有匹配,也从左表返回所有的行
- RIGHT JOIN: 即使左表中没有匹配,也从右表返回所有的行
- FULL JOIN: 只要其中一个表中存在匹配,就返回行
SELECT column_name(s)
FROM table_name1
INNER JOIN table_name2
ON table_name1.column_name=table_name2.column_name
SELECT column_name(s)
FROM table_name1
INNER JOIN table_name2
USING(column_name)
2. Union (ALL) 操作
合并两个或多个 SELECT 语句的结果集。UNION 内部的 SELECT 语句必须拥有相同数量的列。列也必须拥有相似的数据类型。同时,每条 SELECT 语句中的列的顺序必须相同。
Union和Union All区别:
- Union: 对两个结果集进行并集操作, 不包括重复行; 会对获取的结果进行排序操作
- Union All: 对两个结果集进行并集操作; 不会对获取的结果进行排序操作
3. SQL通配符
通配符 | 描述 |
---|---|
% | 代表零个或多个字符 |
_ | 仅替代一个字符 |
[charlist] | 字符列中的任何单一字符 |
[^charlist] 或者 [!charlist] | 不在字符列中的任何单一字符 |
SELECT * FROM Persons WHERE City LIKE '%lon%'
4. SQL函数
函数 | 描述 | 示例 |
---|---|---|
CAST() | 转换为指定数据类型 | CAST(expr as 数据类型) |
UCASE() | 把字段的值转换为大写 | SELECT UCASE(column_name) FROM table_name |
FIRST() | 返回指定的字段中第一个记录的值 | SELECT FIRST(column_name) FROM table_name |
MID() | 用于从文本字段中提取字符 (start起始值是 1) | SELECT MID(column_name,start[,length]) FROM table_name |
LEFT() | 返回某个被请求的文本域的左侧部分 | LEFT(str,len) |
concat() | 把一堆字符串连起来 | CONCAT(str1,str2,...) |
CONCAT_WS() | 字符串用指定字符连起来 | CONCAT_WS(separator,str1,str2,...) |
YEAR() | 返回指定日期的年份 数值 | SELECT YEAR(GETDATE()) |
SUBSTRING() | 字符串截取,pos从1开始, 空格不被计入 | substring(str,pos, len) |
substring_index() | 字符串截取,sep分隔符,num的正负决定截取方向 | substring_index(string,sep,num) |
数学函数 | ||
ROUND() | 把数值字段舍入为指定的小数位数。(四舍五入) | SELECT ROUND(column_name,decimals) FROM table_name |
TRUNCATE(x,y) | 对操作数进行截取操作,结果保留小数点后面指定y位 | |
CEIL(x), CEILING(x) FLOOR(x) | 不超最小,大于最小 | |
RAND() | 返回一个随机浮点值v,范围在0到1之间(即0≤v≤1.0) | 参数可以填随机数种子seed |
MOD(x,y) | 返回x被y除后的余数 |
时间函数 | ||
Now() | 返回当前时间和日期 | YYYY-MM-DD HH:MM:SS |
TIMESTAMPDIFF() | 间隔的时间戳大小,返回int | TIMESTAMPDIFF(YEAR,'2021-3-5','2021-5-4') |
DATEDIFF() | 间隔的日期 | |
Year()/Month()/Day()/ Hour()/Minute()/Second() | 返回日期 | |
TIME(expr) | 提取表达式expr 时间或日期时间的时间部分,并返回一个字符串 |
datatime和timestamp主要区别:
timestamp会跟随设置的时区变化而变化,而datetime保存的是绝对值不会变化
判断函数 | ||
IF() | 满足条件返回v1否则返回v2 | IF(expr, value1, v2) |
IFNULL() | value1不为空(null)则显示value1 ,否则显示value2 | IFNULL(value1,value2) |
Case | 计算条件列表的表达式,并返回可能的结果之一 | case (expression) when 判断 then value1 when 判断 then value2 else value3 end |
案例:
SELECT COUNT(id) total_pv,
COUNT(case when score is not null then 1 else null end) complete_pv,
COUNT(DISTINCT IF(score is not null,exam_id,null)) complete_exam_cnt
FROM exam_record
SQL函数大全汇总_樱花花的博客-CSDN博客_sql函数
MySQL :: MySQL 5.7 Reference Manual :: 12.8 String Functions and Operators
5. SQL窗口函数(OLAP函数(Online Anallytical Processing,联机分析处理))
窗口函数的基本语法如下:
<窗口函数> over (partition by <用于分组的列名>
order by <用于排序的列名>)
<窗口函数>包括:
- 专用窗口函数,包括rank, dense_rank, row_number等专用窗口函数。
- 聚合函数,如sum. avg, count, max, min等
因为窗口函数是对where或者group by子句处理后的结果进行操作,所以窗口函数原则上只能写在select子句中。
简单来说,窗口函数有以下功能:
- 同时具有分组和排序的功能
- 不减少原表的行数 (partition by 相比 group by)
排序函数 (括号内什么都不填) | |||
rank() | 如果有并列名次的行,会占用下一名次的位置 | select *, rank() over (partition by 班级 order by 成绩 desc) as ranking from 班级表 | |
dense_rank() | 如果有并列名次的行,不占用下一名次的位置 | ||
row_number() | 不考虑并列名次的情况, 数字连续 | ||
取值函数 | |||
first_value() | 返回分区中的第一个记录值 | 括号里填返回的值 | |
last_value() | 返回分区中的最后一个记录值 | ||
nth_value() | 返回窗口中第N个expr的值 expr可以是表达式,也可以是列名 | nth_value(expr, n) | |
lag() | 分区中位于当前行前n行的记录值 第三个参数是 超出记录窗口时的默认值 | lag(expr, n) | |
lead() | 分区中位于当前行后n行的记录值 | lead(expr, n) |
图示:
1、排名函数
2、聚合函数
窗口函数应用:
- topN的万能模板:
select * from (select *, row_number() over (partition by 要分组的列名 order by 要排序的列名 desc) as ranking from 表名) as a where ranking = N;
- “累计求和”问题的万能模板是:
select 列1,列2, sum(列名) over (order by 用于排序的列名) as 累计值的别名 from 表名;
6. SQL 正则表达式
6.1 Like
MySQL可以通过 LIKE ...% 来进行模糊匹配。like匹配整个列,如果被匹配的文本仅在列值中出现,LIKE并不会找到它,相应的行也不会返回(当然,使用通配符除外)
6.2 regexp
REGEXP在列值内进行匹配。如果被匹配的匹配的文本在列值中出现,REGEXP将会找到它,相应的行将被返回
--匹配ok结尾的字段
SELECT name
FROM user
WHERE name REGEXP 'ok$'
6.3 not regexp
返回不匹配的列
6.4 regexp函数
regexp_instr():返回与正则表达式模式匹配的子字符串的起始索引
REGEXP_INSTR(expr, pattern[, position[, occurrence[, return_option[, match_type]]]])
regexp_like() :比较给定的字符串,如果字符串相同则返回,如果为真返回1,否则返回0
regexp_replace() :通过匹配字符来替换给定的字符串。
regexp_substr():从给定的字符串中返回子串
7. 公用表表达式(CTE)
Mysql8支持公用表表达式,包括非递归和递归两种.
公用表表达式CTE允许使用命名的临时结果集,这是通过允许在SELECT语句和某些其他语句前面使用WITH子句来实现的.
- 使用CTE的原因:不能在同一查询中两次引用派生表,那样的话,查询会根据派生表的引用次数计算两次或多次,这会引发严重的性能问题.使用CTE后,子查询只会计算一次
- CTE和派生表区别:CTE可以引用其他的CTE,而派生查询不能引用其他派生查询
非递归CTE
--非递归CTE
WITH DERIVED AS (subquery) SELECT ... FROM derived, t1 ...
--派生查询不能引用其他派生查询:
SELECT ... FROM (SELECT ... FROM ...) AS d1, (SELECT ... FROM d1 ...)AS d2...
-- ERROR: 1146 (42S02):Table 'db.d1' doesn't exist
--CTE可以引用其他的CTE:
WITH d1 AS (SELECT ... FROM ...),d2 AS (SELECT ... FROM d1 ...)
SELECT FROM d1,d2 ...
递归CTE
递归CTE是一种特殊的CTE,其子查询会引用自己的名字.WITH子句必须以WITH RECURSIVE开头.递归CTE子查询包括两部分:seed查询和recursive查询,由UNION [ALL]或UNION DISTINCT分隔.
seed查询被执行一次以创建初始数据子集;recursive查询被重复执行以返回数据的子集,直到获得完整的结果集。当迭代不会生成任何新行时,递归会停止。
WITH RECURSIVE cte AS
(SELECT ... FROM table_name /* seed SELECT */
UNION ALL
SELECT ... FROM cte, table_name) /* "recursive" SELECT */
SELECT ... FROM cte;
实例:打印1——10数字
mysql> WITH RECURSIVE cte(n) AS
(SELECT 1 /* seed query */
UNION ALL
SELECT n+1 FROM cte WHERE n<10 /* recursive query */
)
SELECT * FROM cte;
三、事务Transactions
-
四大特性(ACID)
事务特性 | 描述 |
---|---|
原子性(Atomicity) | sql 语句的执行,要么全部成功,要么全部失败 |
一致性(Consistency) | 事务结束前后,数据 整体不变 |
隔离性(Isolation) | 多个事务独立运行,互不干扰 |
持久性(Durability) | 事务一旦提交,对数据的改变 永久有效 |
-
隔离级别 Isolation level
隔离级别由低到高 | 脏读 | 不可重复读 | 幻读 | 数据库默认 |
---|---|---|---|---|
读未提交 (Read Uncommitted) | √ | √ | √ | |
读已提交 (Read Commited) | × | √ | √ | Oracle |
重复读取(Repeatable Read) | × | × | √ | Mysql |
序列化 (Serializable) | × | × | × |
Lock: Nolock < ReadLock < RWLock < Mutex
-
并发问题
事务 | 读写 | 对应最高隔离等级 |
---|---|---|
脏读(Dirty Read) | 读取了修改但 "未提交" 的数据 | 读未提交 (Read Uncommitted) |
不可重复读 (Nonrepeatable Read) | 读取过的数据, 再次读取出来发现值不同 偏向“update” | 读已提交 (Read Commited) |
幻读(Phantom Read) | 第 1 次和第 2 次读出来的记录数不一样 偏向“insert”和“delete” | 重复读取(Repeatable Read) |
-
事务的结束
- '显示'结束:commit、rollback
- '隐式'结束:DDL、DCL(隐含了 commit)。强行退出 sql*plus,事务将自动回滚。
四、触发器Trigger
-
创建触发器
create [or replace] trigger 触发器名
触发时间 {before | after} -- view 中是 instead of
触发事件 {insert | update | delete} -- dml、ddl、database
on 触发对象 -- table、view、schema、database
触发频率 {for each row} -- 行级触发器。默认:语句级触发器
[follows 其它触发器名] -- 多个触发器执行的 前后顺序
[when 触发条件]
begin
pl/sql 语句;
end;
/*
功能:after insert or update or delete 时,执行语句
命名:tr_aiud_student_info
*/
create or replace trigger scott.tr_aiud_student_info
after insert or update or delete on scott.student_info
for each row
begin
case
when inserting then
dbms_output.put_line('插入成功!');
when updating then
dbms_output.put_line('更新成功!');
when deleting then
dbms_output.put_line('删除成功!');
else
dbms_output.put_line('无操作!');
end case;
end;
/
-
查询触发器
权限范围,由大到小:dba_* > all_* > user_*
select * from dba_triggers;
select * from all_triggers;
select * from user_triggers;
-
删除触发器
drop trigger 触发器名;
select 'DROP TRIGGER ' || t.owner || '.' || t.trigger_name || ';' 删除
from all_triggers t
where t.owner = 'SCOTT'
and t.table_name = 'STUDENT_INFO';
-
触发器分类
DML 触发器,DDL触发器,Database触发器,insteadof触发器
图解|从根上彻底理解MySQL的索引 - 蝉沐风 - 博客园
五、PL/SQL (oracle数据库)
- PL/SQL高级数据库程序设计语言(对Oracle数据库进行访问)集成于数据库服务器。
- PL/SQL是对SQL语言存储过程语言的扩展
- PL/SQL包括两部分,一部分是数据库引擎部分(数据库PL/SQL);另一部分是可嵌入到许多产品(如C语言,JAVA语言等)工具中的独立引擎(工具PL/SQL)。
PL/SQL 可用的SQL语句
- 在 PL/SQL中只能用 SQL语句中的 DML 部分:INSERT,UPDATE,DELETE,SELECT INTO,COMMIT,ROLLBACK,SAVEPOINT
- 如果要在PL/SQL中使用DDL(如CREATE table 等)的话,只能以动态的方式来使用
PL/SQL块结构和组成元素
PL/SQL程序由三个块组成,即声明部分、执行部分、异常处理部分
PL/SQL块的结构:
DECLARE
/* 声明部分: 在此声明PL/SQL用到的变量,类型及游标,以及局部的存储过程和函数 */
BEGIN
/* 执行部分: 过程及SQL 语句 , 即程序的主要部分 */
EXCEPTION
/* 执行异常部分: 错误处理 */
END;
其中 执行部分是必须的。
PL/SQL块可以分为三类:
- 无名块:动态构造,只能执行一次。
- 子程序:存储在数据库中的存储过程、函数及包等。当在数据库上建立好后可以在其它程序中调用它们。
- 触发器:当数据库发生操作时,会触发一些事件,从而自动执行相应的程序。
plsql基础教程(自学用) - 梦里仙人掌 - 博客园
六、Hive SQL (HQL)
HQL与SQL区别:
- HQL 不支持行级别的增、改、删,所有数据在加载时就已经确定,不可更改。
- 不支持事务 Transaction。
- 支持分区存储。
Hive 的 SQL 特点:
- Hive 更适合于分析复杂的数据集。SQL更适合于快速分析不太复杂的数据集。
- Hive 执行时间长
- 不支持等值连接,一般使用left join、right join 或者inner join替代
- 不能智能识别concat(‘;’,key),只会将‘;’当做SQL结束符号
- HiveQL中String类型的字段若是空(empty)字符串, 即长度为0, 那么对它进行IS NULL的判断结果是False,使用left join可以进行筛选行
- 不支持 ‘< dt <’这种格式的范围查找,可以用dt in(”,”)或者between替代
- 配置单元在HDFS中写入和查询数据。SQL需要多次读写。
- SQL支持联机事务处理(OLTP)。配置单元不支持OLTP。
1. DDL语句
1.1 数据库操作
-- 创建数据库
create database if not exists hivetest;
create database if not exists hivetest location 'hdfs路径'; --指定数据库在HDFS上存放的位置
-- 显示数据库
show databases;
-- 查看数据库详情
desc database hivetest;
-- 切换当前数据库
use 目标数据库名称;
-- 删除空数据库
drop database if exists 库名;
drop database 库名 cascade; -- 如果数据库不为空,可以采用cascade命令,强制删除
七、MongoDB
1. 介绍
MongoDB 将数据存储为一个文档,数据结构由键值(key=>value)对组成。MongoDB 文档类似于 JSON 对象。字段值可以包含其他文档,数组及文档数组。
- Mongo支持丰富的查询表达式。查询指令使用JSON形式的标记,可轻易查询文档中内嵌的对象及数组。
- MongoDb 使用update()命令可以实现替换完成的文档(数据)或者一些指定的数据字段 。
- Mongodb中的Map/reduce主要是用来对数据进行批量处理和聚合操作。
- Map和Reduce。Map函数调用emit(key,value)遍历集合中所有的记录,将key与value传给Reduce函数进行处理。
- Map函数和Reduce函数是使用Javascript编写的,并可以通过db.runCommand或mapreduce命令来执行MapReduce操作。
- GridFS是MongoDB中的一个内置功能,可以用于存放大量小文件。
- MongoDB允许在服务端执行脚本,可以用Javascript编写某个函数,直接在服务端执行,也可以把函数的定义存储在服务端,下次直接调用即可。
- MongoDB支持各种编程语言:RUBY,PYTHON,JAVA,C++,PHP,C#等多种语言。
- MongoDB区分类型和大小写。
- 文档中的键/值对是有序的。
- MongoDB的文档不能有重复的键。
2. MongoDB和SQL
SQL术语/概念 | MongoDB术语/概念 | 解释/说明 |
---|---|---|
database | database | 数据库 |
table | collection | 数据库表/集合 |
row | document | 数据记录行/文档 |
column | field | 数据字段/域 |
index | index | 索引 |
table joins | 表连接,MongoDB不支持;但MongoDB可以嵌入文档 | |
primary key | primary key | 主键,MongoDB自动将_id字段设置为主键 |
3. MongoDB操作
3.1 数据库操作
创建数据库,如果存在则切换到指定数据库
use DATABASE_NAME
显示所有数据库
show dbs
删除当前数据库,默认为 test
db.dropDatabase()
3.2 集合(表)操作
创建集合,其中可选参数指定有关内存大小及索引的选项
db.createCollection(name, options)
集合只有在内容插入后才会真正创建
查看已有集合:
show collections 或 show tables
删除集合:
db.集合名.drop()
3.3 文档操作
插入文档:
db.COLLECTION_NAME.insert(document) 主键已经存在则会报错
或
db.collection.insertOne() 或 db.collection.replaceOne() 主键存在会更新,不存在会插入
插入一个多个文档:
db.collection.insertMany(
[ <document 1> , <document 2>, ... ],
{
writeConcern: <document>,
ordered: <boolean>
}
)
项目实战
学籍管理系统-python实现+mysql - 知乎
踩坑汇总
1. MySql报错only_full_group_by
MySql从5.7版本开始默认开启only_full_group_by规则,规则核心原则如下,没有遵循原则的sql会被认为是不合法的sql
- order by后面的列必须是在select后面存在的
- select、having或order by后面存在的非聚合列必须全部在group by中存在
问题解决
- 第一种,修改sql使其遵守only_full_group_by规则(费时间)
- 第二种,将MySql的版本降到5.7以下(不推荐)
- 第三种,关闭only_full_group_by规则
2. 同一Select语句使用别名运算报错
处于同级一样从上一个虚拟表中取值(参考sql执行顺序),故找不到对应列
3. sql窗口函数last_value取的值不是分组中最后一个的值
因为默认是取到分组(可选)第一项到当前行的, 即
last_value()默认统计范围是 rows between unbounded preceding and current row
如果Order的值变化了,基本上也就是当前行的值了,如果没有变化就取相同Order系列中的最后一项。
问题解决
- 第一种,添加 rows between unbounded preceding and unbounded following
last_value(col) over (order by col rows between unbounded preceding and unbounded following)
- 第二种,改用 first_value然后desc排序
4. mysql的utf8编码不能支持表情符号
解决方法:更改 MySQL 数据库的编码为 UTF8MB4。utf8mb4 编码是 utf8 编码的超集,兼容 utf8,并且能存储 4 字节的表情字符。
需要修改 MySQL 配置文件
修改mysql配置文件myf(Linux系统),Windows 系统为 my.ini。
myf一般在etc/mysql/myf位置。找到后请在以下三部分里添加如下内容:
[client]
default-character-set = utf8mb4
[mysql]
default-character-set = utf8mb4
[mysqld]
character-set-client-handshake = FALSE
character-set-server = utf8mb4
collation-server = utf8mb4_unicode_ci
init_connect='SET NAMES utf8mb4'
更改数据库和已经建好的表也转换成 utf8mb4
更改数据库编码:ALTER DATABASE DB_NAME CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci;
更改表编码:ALTER TABLE TABLE_NAME CONVERT TO CHARACTER SET utf8mb4 COLLATEutf8mb4_general_ci;
Ps:如有必要,还可以更改列的编码。
References
Oracle 事务详解(transaction)_鱼丸丶粗面的博客-CSDN博客_oracle事务
Oracle 触发器详解(trigger)_鱼丸丶粗面的博客-CSDN博客_oracle触发器详解
MySql报错only_full_group_by的解决办法 - 知乎
通俗易懂的学会:SQL窗口函数 - 知乎
Hive(总)看完这篇,别说你不会Hive!_子清.的博客-CSDN博客_hive
一文学完所有的Hive Sql(两万字最全详解)_五分钟学大数据的博客-CSDN博客_hive sql
Mysql8之公用表表达式(CTE) - 知乎
更多推荐
各种DBMS和Sql语言
发布评论