数据库
介绍
数据库分两种,关系型(Relational Database)和非关系型(Not Only SQL),它们的区别:
RDBMS:使用表格存储数据;具有强大的查询性能;但只能存储在硬盘,读写性能较差
NoSQL:提供多种存储数据的结构;不支持SQL,查询方面差;但可使用RAM,数据量大时有优势
数据库必须符合ACID:
Atomicity:事务若执行失败,数据库能恢复到事务开始前的状态
Consistency:写入的数据必须符合预设规则,否则事务执行失败
Isolation:允许并发事务的存在,是线程安全的
Durability:数据及其修改是永久存在的(体现在保存于硬盘上)
SQL(Structured Query Language,结构化查询语言)是用于操作数据库的语言
不同数据库提供的SQL不一样,所以常出现在数据库的名字里
目前常用的开源数据库有:
SQLite:轻量的数据库
MySQL
PostegreSQL
Navicat 是一款能直观地操作数据库的应用
SQL标准
杂碎知识
虽然不同数据库的SQL语法有差别(实现上也不同),但它们都遵循SQL标准
SQL是不区分大小写的
称表的一行为一条记录
通过CREATE DATABASE dbName创建数据库
文本数据需要添加单引号或双引号;当文本数据是一个数字时,需要仔细检查
常用数据类型
文本类型:
CHAR(N):长度固定为N的字符串,文本长度不足则用空格补充,N最长一般为65535
VARCHAR(N):长度可变的字符串,最长为N
TEXT:适用于长文本
数值类型:
INT/INTEGER:4字节整型
SMALLINT:2字节整型
BIGINT:8字节整型
FLOAT(D):小数位最多为D的浮点数
DECIMAL(M, D):总位数为M,小数位数为D的浮点数
时间类型:
DATE:存储日期YYYY-MM-DD
TIME:存储时间HH:MM:SS
DATETIME:存储日期时间YYYY-MM-DD HH:MM:SS
其它:
BLOB:存储大量二进制数据(如图片、音频等)
ENUM:枚举类型
查询语句
查询结果是类似于表的结构,对数据库的许多操作需要配合查询语句一起使用
SELECT系列语句用于确定列,WHERE系列语句用于确定行
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 # 查询列 SELECT * FROM tableName; # 查询整个表SELECT columnNameList FROM tableName; # 查询特定列, 列间用逗号隔开# 查询时过滤记录 SELECT DISTINCT ... ; # DISTINCT : 查询后,自动去除完全重复的记录SELECT ... WHERE condition ; # WHERE condition : 按condition 过滤记录# condition 的编写: 在查询时, 若表达式返回真, 则加入查询结果 # 格式 返回真的情况 columnName > / = / < value # 和值比较 columnName IS NULL # 空值 columnName BETWEEN val1 AND val2 # 介于val1, val2间的值 columnName IN (val1, ...) # 等于括号内任意值 columnName LIKE val # LIKE 模糊查询 columnName REGEXP val # 正则表达式匹配 columnName GLOB val # SQLite支持GLOB匹配 # condition 语句编写 # 括号优先级最高; 若不熟悉运算符的优先级, 可以直接打括号 exp1 AND exp2 # 逻辑与 exp1 OR exp2 # 逻辑或 NOT exp # exp的否定# 在condition 后可添加ORDER BY 语句, 表示按列排序 # 默认为ASC (上到下升序), DESC 为上到下降序 # 若有复数个列, 则按顺序排序(可认为是先排最后列, 再排第一列) SELECT ... FROM ... WHERE ... ORDER BY columnNames ASC / DESC ;# 例: 对下表查询SELECT * FROM table ORDER BY name, id; (先排id, 再排name) id | name 3 a 1 b 2 a1 b 2 a 3 a2 a 3 a 1 b# 起别名: # 在查询语句中, 所有涉及表名、列名的语句, 可在名字后添加 AS 别名, 查询结果将使用别名 SELECT * FROM a AS newName;
LIKE通配符:
%:匹配任意数目的字符
_:匹配单个字符
若字段中含通配符,可通过ESCAPE转义字符:LIKE 'a\_b' ESCAPE '\'
对表内容的操作
对表内容的操作一定记得联合查询语句一起使用,否则可能会修改所有记录
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 # 更改记录内容 # WHERE 指定要修改的行, SET 指定要修改的列 UPDATE tableName SET column1= value1,...,columnN= valueN WHERE condition ;# 删除具体记录(配合查询语句) DELETE FROM tableName WHERE condition ;# 插入新记录 INSERT tableName (columnList) VALUES (valueList);# 以上为对列进行修改的语句, 而更新表的列需要用到ALTER 系列语句 # 添加列 ALTER TABLE tableName ADD columnName valueType# 删除列(SQLite不支持) ALTER TABLE tableName DROP COLUMN columnName# 更改列的数据类型及其约束 ALTER TABLE tableName MODIFY COLUMN columnName valueType# 修改列名 ALTER TABLE tableName CHANGE columnName newName;# 需要检查的情况(需要设置主键约束): # 记录不存在则插入、存在则更新 INSERT OR REPLACE INTO ... # SQLiteREPLACE INTO ... # MySQL INSERT IGNORE INTO ... # 记录不存在则插入、存在则忽略操作
对表整体的操作
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 # 创建表 # 列名不是数据, 不用加引号 CREATE TABLE tableName ( columnName dataType, # 列名 该列的数据类型 逗号分隔开不同列 ... ); # 用oldTable的查询结果作为内容, 创建新表 CREATE TABLE newTable AS SELECT * FROM oldTable;# 用oldTable的查询结果作为内容, 插入 已创建 的表里 INSERT INTO exTable (columnList) SELECT * FROM oldTable;# 修改表名 ALTER TABLE tableName RENAME TO newName;# 删除表 DROP TABLE tableName;# 删除表的所有数据, 保留表 TRUNCATE TABLE tableName; # 一次性删除所有记录DELETE FROM tableName; # 找出所有行, 逐个删除(DELETE 还可用于删除特定行)
部分关键字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 # 创建/ 删除表时, 判断表是否不存在/ 存在: CREATE TABLE IF NOT EXISTS ...; # 若表不存在, 才创建DROP TABLE IF EXISTS ...; # 若表存在, 才删除# 数据的约束(创建表时, 在数据类型后指定; 或者使用ALTER ... MODIFY修改) NOT NULL # 该列数据一定非空UNIQUE # 该列数据各不相同PRIMARY KEY # 主键约束:该列数据各不相同, 且不为空DEFAULT value # 插入记录时, 若未指定该列的值, 则使用默认值value AUTO_INCREMENT # 用于整型的自动增长 COMMENT # 注释(放在最后) # 格式特殊的外键约束: # 用于绑定两表的两列, 本表是从表, otherTable是主表 # 从表添加记录时, 外键约束列的数据必须存在于主表内 # 从表删除记录时, 主表对应的记录不删除 FOREIGN KEY columnName REFERENCES otherTable(columnName) dataType;# 外键约束的关键字: CASCADE # 主表修改/ 删除记录时, 从表对应外键字段随之修改/ 删除, 常用 RESTRICT # 和CASCADE相反 SET NULL # 主表删除记录时, 从表对应外键字段设置为空# 也可将修改和删除拆开: ON UPDATE CASCADE / ON DELETE CASCADE / ON UPDATE RESTRICT / ON DELETE RESTRICT# 可以在数据类型后指定, 也可以在后面批量设置约束: CREATE ... ( ..., CONSTRAINT 约束名 UNIQUE (要添加的列) );
Qt Sql
前言
Qt提供了两种通过C++修改数据库的方法:一是模型/视图套件;二是提供查询接口,直接输入SQL语句
它对SQLite的支持是最好的,可直接使用;而有些数据库的驱动需要自行编译,例如MySQL
MySQL驱动编译
QSqlDatabase
该类的对象是将数据从硬盘加载到内存上的桥梁
连接数据库:
1 2 3 4 5 6 7 8 9 10 QSqlDatabase db = QSqlDatabase::addDatabase ("QSQLITE" ); db.setHostName ("127.0.0.1" ); db.setPort (3306 ); db.setUserName ("root" ); db.setPassword ("1" ); db.setDatabase ("mdb.db" ); db.open ();
Qt Query
在Qt中,可直接利用QSqlQuery对象操作数据库
1 2 3 4 5 6 7 8 9 QSqlQuery* query {new QSqlQuery (db)}; query->exec ("SQL语句" ); while (query->next ()) { query->value (列名); }
Qt Model/View/Delegate
模型是为了操作数据库而提供的接口,视图能让用户直观地操作数据库,有两种常用模型和视图
所有模型都有lastError()方法
委托 是模型和视图的中间层,用于渲染视图 和编辑模型
QSqlTableModel/QTableView
以表为结构的模型/以表形式展现的视图
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 QSqlTableModel* model {new QSqlTableModel (this , db); } model->setEditStrategy (QSqlTableModel::OnManualSubmit); model->setTable ("tableName" ); model->select (); ui->tv->setModel (model); model->index (int , int ) rowCount () / columnCount () data (QModelIndex) removeRow (int )/removeColumn (int ) submitAll () revertAll () ui->tv->currentIndex () setCurrentIndex (QModelIndex)
关于视图单元格的选中模式(枚举QAbstractItemView::SelectionMode):
NoSelection:无法选中
SingleSelection:只能单选
MultiSelection:允许多选
ExtendedSelection:允许配合Ctrl或Shift键进行选择
ContiguousSelection:允许配合Ctrl+Shift键进行批量选择
如果需要设置某列不可选中,可使用自定义委托:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 class ReadOnlyDelegate : public QItemDelegate {public : ReadOnlyDelegate (QWidget *parent = nullptr ) {} QWidget *createEditor (QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override { Q_UNUSED (parent) Q_UNUSED (option) Q_UNUSED (index) return nullptr ; } }; ReadOnlyDelegate* readOnlyDelegate = new ReadOnlyDelegate (this ); for (auto i = 1 ; i < 3 ; ++i) { ui->matrixTable->setItemDelegateForColumn (i, readOnlyDelegate); }
QStandardItemModel/QTreeView
QStandardItemModel是以项为基础的模型,主从结构分明,很适合展示树形结构
它的编辑要比QSqlTableView复杂,不能直接通过select()(也没有setTable()方法)完成数据的加载,而要使用QSqlQuery,因此单纯的矩阵结构应该用后者绘制
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 ... ManageModel->setColumnCount (2 ); ManageModel->setHorizontalHeaderLabels ({"用户组" , "组权限" }); group: gid grpname power memberCnt member: uid grp_id[含group (gid)的外键约束] username pwd QSqlQuery group{db}, member{db}; group.exec ("SELECT gid, grpname FROM group" ); while (group.next ()) { QList<QStandardItem*> rowItems; auto tmpItem {new QStandardItem (qroup.value ("grpname" ).toString ())}; rowItems << tmpItem << new QStandardItem (group.value ("power" ).toInt ()); member.exec ("SELECT username FROM member WHERE grp_id=" + QString::number (group.value ("gid" ).toInt ())); while (member.next ()) { tmpItem->appendRow (new QStandardItem (member.value ("username" ).toString ())); } model->appendRow (rowItems); } tv->expandAll () collapseAII ()
树形视图的更多方法
更多模型/视图