sqlite是一款非常轻便小巧的数据库,以C语言开发,已流行了数十年,据说是世界上部署最多的数据库。为什么是部署最多的呢?因为它根本不需要数据库服务器,且可以在任意设备、任意操作系统上部署。因此,很多应用程序,无论是Windows的、Linux的、Apple的、甚至嵌入式的,它们都内嵌了sqlite数据库。可想而知,就部署数量而言,确实没什么数据库可以和它竞争。为什么它可以做到在任意设备任意操作系统上部署呢?一是因为它的一个数据库就是一个单一的文件;二是因为它的数据库程序是纯C写的,只要编译好就可以运行在这些地方,并且据说只有一个C文件。
sqlite的安装特别简单。如果不想从源码编译的话,直接下载二进制文件即可运行。
sqlite下载页面:https://www.sqlite.org/download.html
本文以Windows为例,因此下载 “Precompiled Binaries for Windows” 这下面的。
这下面有 sqlite-dll-win-x64-3440200.zip (1.24MB) 和 sqlite-tools-win-x64-3440200.zip (4.71MB) 这2个。我们只要下载后者即可。前面那个dll的可能是应用程序调用sqlite的API用的,和本次实验无关。
下载完并解压之后,我们在这个解压好的文件夹下可以看到 3 个exe文件,而本次我们只需用到 sqlite3.exe.
有一些对局记录,可以利用sqlite进行管理。这些对局记录很简单,只包含日期、对手ID、结果这三个要素。而结果可以用1、-1、0来分别表示胜、负、平。
这些对局记录已写入csv文件,样例 20231216.csv 如下:
2023-12-16,57793,1
2023-12-16,41864,1
2023-12-16,41864,-1
根据以上所描述的对局记录的特点,很容易地设计表 go_records 如下:
CREATE TABLE go_records
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
date DATE,
player INT,
result INT
);
这里使用自增id是为了避免将来在删除某条记录时,因存在其他三要素完全一致的记录而造成误删。
貌似设计好了,但这里有一个问题:将来我们可以利用 sqlite 的.import
命令来导入csv文件时,由于自增id的存在就会引起导入失败,因为csv文件中没有自增id这一列。
怎么处理呢?
笔者不是数据库专家,没有想到特别好的办法。想到的一个办法是:再创一张临时表;先将csv导入到临时表,再将临时表导入到上面的 go_records 表中。临时表不需要id,而临时表导入到最终表的过程中,因使用 INSERT INTO 语句,从而可以实现自创并自增id.
临时表的设计如下:
CREATE TABLE tmp_go_records
(
date DATE,
player INT,
result INT
);
而导入完csv文件之后,将临时表再导入到 go_records 表中的语句如下:
INSERT INTO go_records (date, player, result)
SELECT date,player,result FROM tmp_go_records;
步骤如下:
sqlite3 .\go.db
sqlite3
.open C:\users\Finix\sqlite3\go.db
值得一提的是,.help
命令可以看到各个帮助选项。
DROP TABLE IF EXISTS go_records;
DROP TABLE IF EXISTS tmp_go_records;
CREATE TABLE go_records
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
date DATE,
player INT,
result INT
);
CREATE TABLE tmp_go_records
(
date DATE,
player INT,
result INT
);
.mode csv
.import ./records/20231216.csv tmp_go_records
.import ./records/20231215.csv tmp_go_records
以上是导入了2天的csv数据。注意,以上语句不能加分号,否则无效但又不会报错。
INSERT INTO go_records (date, player, result)
SELECT date,player,result FROM tmp_go_records;
如果不太放心一个数据库文件,可以再备份一个。命令如下:
.save go.db.bak
.exit
这么一张只有4列的表(其中一列还是自增id)能有什么有趣的SQL呢?
select count(distinct player) from go_records;
select count(*) from go_records where date in ('2023-12-16');
# 总胜率
SELECT result,cnt, sum(cnt) over() as total_cnt,
concat(round((cnt*100.0/sum(cnt) over()), 2), '%') as rate
FROM (
SELECT result, count(*) AS cnt FROM go_records GROUP BY result
) src;
select player, result, cnt, sum(cnt) over(PARTITION BY player) as total_cnt,
concat(round((cnt*100.0/(sum(cnt) over(PARTITION BY player))), 2), '%') as rate
from (
select count(*) as cnt, player, result from go_records where player=88667 group by player, result
) src
order by result desc;
select * from (
select player, result, cnt, sum(cnt) over(PARTITION BY player) as total_cnt,
round((cnt*100.0/(sum(cnt) over(PARTITION BY player))), 2) rate,
concat(round((cnt*100.0/(sum(cnt) over(PARTITION BY player))), 2), '%') as percent
from (
select player, result, count(*) as cnt from go_records group by player, result
) src
) final
where result > 0
order by final.rate desc;
select distinct src.player from (
(select player from go_records where date='2023-12-15') t1
inner join
(select player from go_records where date='2023-12-16') t2
on t1.player=t2.player
) src;
关于这最后一个问题,如果是打印出同一棋手出现在不同的多天,则按以上方法需写多个inner join
,这显然是不能扩展和无法接受的。笔者一时之间还没有想出特别好的SQL的解决方案,留待以后的思考吧。
(END)