大概一年前的这个时候,为了深入了解数据库的内部原理和实现,我决定编写一个数据库系统的原型程序,这个行动断断续续坚持了一年,写了接近3W行C代码,现在除了事务和并发(只做到了存储引擎层面的并发),其他的功能基本都实现了。数据库是底层软件,实现起来有一定难度,为了编写这个程序(暂且就叫做XSQL吧),我阅读了很多关于开源数据库内部原理的书籍资料,总结了主流数据库的结构,然后依据其原理写出了XSQL。接下来我打算把实现这个程序中遇到的问题,解决的办法和涉及的技术写成一个专题,这样可以让更多同学更加深入的理解数据库系统的内部原理,还可以让他人发现我实现过程中的不足之处,指出优化方法。
XSQL中的数据类型(绿色暂未实现):
CHAR |
定长字符串 |
VARCHAR |
变长字符串 |
FLOAT |
单精度浮点型 |
DOUBLE |
双精度浮点型 |
INT |
整型 |
SMALLINT |
短整型 |
BIGINT |
长整型 |
DATETIME |
时间戳 |
TEXT |
文本 |
FILE |
文件 |
XSQL支持的函数:
字符串函数 |
LENGTH(exp) |
返回字符串exp的长度 |
SUBSTR(exp1,exp2) |
返回串exp2在exp1中出现的下标,如果不存在则返回-1 |
|
时间函数 |
GETDATE() |
返回当前的系统时间字符串 |
TODATE(exp) |
将时间戳exp转化为时间字符串 |
|
TOTIMESTAMP(exp) |
将时间字符串exp转化为时间戳 |
|
数学函数 |
POWER(exp1,exp2) |
返回exp1的exp2次幂 |
SIN(exp) |
返回exp的正弦值 |
|
COS(exp) |
返回exp的余弦值 |
|
SQRT(exp) |
返回exp的平方根 |
|
ABS(exp) |
返回exp的绝对值 |
|
聚合函数 |
MAX(exp) |
返回最大的exp |
MIN(exp) |
返回最小的exp |
|
SUM(exp) |
返回所有行的exp的和 |
|
COUNT(exp) |
返回行数 |
|
AVG(exp) |
返回所有行的exp平均值 |
在XSQL中执行如下命令建立学生选课关系表:
CREATE TABLE student
(
s# INT AUTO_INCREMENT NOT NULL ,
name CHAR(20) ,
age SMALLINT CHECK(age BETWEEN 15 AND 20) ,
sex CHAR(2) CHECK(sex IN('男','女')) ,
address VARCHAR(50) ,
reg_time DATETIME ,
PRIMARY KEY(s#)
)
INSERT INTO student(s#,name,age,sex ,address ,reg_time)
VALUES (NULL,'wang',20,'男','哈尔滨',totimestamp(getdate()))
INSERT INTO student(s#,name,age,sex ,address ,reg_time)
VALUES (NULL,'li',19,'男','哈尔滨',totimestamp(getdate()))
INSERT INTO student(s#,name,age,sex ,address ,reg_time)
VALUES (NULL,'chen',17,'男','哈尔滨',totimestamp(getdate()))
INSERT INTO student(s#,name,age,sex ,address ,reg_time)
VALUES (NULL,'wu',18,'男','哈尔滨',totimestamp(getdate()))
INSERT INTO student(s#,name,age,sex ,address ,reg_time)
VALUES (NULL,'luo',20,'女','哈尔滨',totimestamp(getdate()))
INSERT INTO student(s#,name,age,sex ,address ,reg_time)
VALUES (NULL,'zhou',20,'女','哈尔滨',totimestamp(getdate()))
CREATE TABLE course
(
c# INT AUTO_INCREMENT NOT NULL,
cname CHAR(15),
teacher CHAR(15),
PRIMARY KEY(c#)
)
INSERT INTO course(c#,cname,teacher) VALUES(NULL,'数据库','li')
INSERT INTO course(c#,cname,teacher) values(NULL,'数学','ma')
INSERT INTO course(c#,cname,teacher) values(NULL,'化学','zhou')
INSERT INTO course(c#,cname,teacher) values(NULL,'物理','shi')
INSERT INTO course(c#,cname,teacher) values(NULL,'操作系统','wen')
CREATE TABLE sc
(
s# INT NOT NULL,
c# INT NOT NULL,
grade SMALLINT CHECK(grade BETWEEN 0 AND 100),
PRIMARY KEY(s#,c#),
FOREIGN KEY(s#) REFERENCES student(s#),
FOREIGN KEY(c#) REFERENCES course(c#)
)
INSERT INTO sc(s#,c#,grade) values(1,1,55)
INSERT INTO sc(s#,c#,grade) values(1,2,78)
INSERT INTO sc(s#,c#,grade) values(1,3,85)
INSERT INTO sc(s#,c#,grade) values(1,4,91)
INSERT INTO sc(s#,c#,grade) values(1,5,69)
INSERT INTO sc(s#,c#,grade) values(2,1,85)
INSERT INTO sc(s#,c#,grade) values(3,1,90)
INSERT INTO sc(s#,c#,grade) values(3,2,86)
INSERT INTO sc(s#,c#,grade) values(3,3,95)
INSERT INTO sc(s#,c#,grade) values(4,1,71)
INSERT INTO sc(s#,c#,grade) values(4,4,63)
INSERT INTO sc(s#,c#,grade) values(5,1,70)
INSERT INTO sc(s#,c#,grade) values(5,2,65)
INSERT INTO sc(s#,c#,grade) values(5,3,80)
INSERT INTO sc(s#,c#,grade) values(5,5,65)
INSERT INTO sc(s#,c#,grade) values(6,1,78)
INSERT INTO sc(s#,c#,grade) values(6,3,90)
查询演示:
--列出student表的所有行
SELECT s#,name ,age ,sex ,address ,todate(reg_time)
FROM student
--列出course表的所有行
SELECT*
FROM course
--检索学习课程号为2的学生学号与姓名
SELECT s#,name
FROM student
WHERE EXISTS (
SELECT *
FROM sc
WHERE c#=2 AND s#=student.s#
)
--检索学习数学的学生学号,姓名和入学时间
SELECT name,student.s# ,todate(reg_time) as '入学时间'
FROM student,sc,course
WHERE cname='数学' AND student.s#=sc.s# AND sc.c#=course.c#
--检索学习全部课程的学生姓名
SELECT name
FROM student
WHERE NOT EXISTS(
SELECT *
FROM course
WHERE NOT EXISTS(
SELECT *
FROM sc
WHERE s#=student.s# ANDc#=course.c#
)
)
--检索名字小于等于平均长度的学生信息
SELECT *
FROM student
WHERE LENGTH(name) <= (SELECT AVG(LENGTH(name)) FROM student)
--检索大于等于平均年龄且最高分数和最低分数之差不超过10分或者小于平均年龄且之差不超过15的学生信息
SELECT *
FROM student
WHERE
(
10 > (SELECT MAX(grade) - MIN(grade)
FROM SC
WHERE s# =student.s#) AND age >= (SELECT AVG(age) FROM student)
) OR (
15 > (SELECT MAX(grade) - MIN(grade)
FROM SC
WHERE s# =student.s#) AND age < (SELECT AVG(age) FROM student)
)