前言
开发平台:Win10 64位
开发环境:Qt Creator 13.0.0
构建环境:Qt 5.15.2 +MSVC2019 64位
sqlite版本:sqlite3
文章目录
- 一、Sqlite是什么
- 二、sqlite使用步骤
- 2.1 下载
- 2.2 安装
- 2.3 使用
- 三、Qt集成sqlite3
- 3.1 关键问题
- 3.2 封装sqlite
- 四、参考文献
- 4.1 https://blog.csdn.net/Javachichi/article/details/138106632
- 4.2 https://zhuanlan.zhihu.com/p/24993071
- 4.3 https://blog.csdn.net/weixin_50670076/article/details/136350060
- 4.4 https://www.cnblogs.com/sfy5848/p/4825771.html
- 4.5 https://blog.csdn.net/only_a_Heroic_car/article/details/119605906
- 4.6 https://blog.csdn.net/p154613730/article/details/85144718
- 4.7 https://blog.csdn.net/lms1008611/article/details/81271712
一、Sqlite是什么
SQLite 是一个用 C 语言编写的开源、轻量级、快速、独立且高可靠性的 SQL 数据库引擎,它提供了功能齐全的数据库解决方案。SQLite 几乎可以在所有的手机和计算机上运行,它被嵌入到无数人每天都在使用的众多应用程序中。
此外,SQLite 还具有稳定的文件格式、跨平台能力和向后兼容性等特点。SQLite 的开发者承诺,至少在 2050 年之前保持该文件格式不变。
二、sqlite使用步骤
2.1 下载
官方下载地址: link
windows平台下载这个预编译的就可以。从后面解释可以看到,压缩包里面是一些命令行工具,用来管理sqlite数据库的。
2.2 安装
下载完后解压得到三个文件,都是用来执行命令行工具的可执行应用。
- sqldiff.exe: 命令行实用程序,用于显示 SQLite 数据库之间的内容差异
- sqlite3.exe:命令行实用程序,执行 SQLite 数据库操作和 SQL 语句。
- sqlite3_analyzer.exe:命令行实用程序,测量和显示多少和如何有效的空间被用于单个的表和索引与SQLite数据库文件
配置环境变量: 为了在命令提示符或 PowerShell 中从任何地方运行 SQLite,需要将 SQLite 的路径添加到电脑的 PATH 环境变量中。
- 打开控制面板并选择 “系统”。
- 点击 “高级系统设置”。
- 在 “系统属性” 对话框中,点击 “环境变量” 按钮。
- 在 “系统变量” 部分,滚动找到并选中 “Path”,然后点击 “编辑”。
- 在 “编辑环境变量” 对话框中,点击 “新建”,然后输入您的 SQLite 目录的路径,比如 C:\sqlite-tools-win-x64-3450300。
- 点击 “确定” 保存更改。
测试安装: win+r打开一个新的命令提示符或 PowerShell 窗口,并输入 sqlite3。如果您看到了 SQLite 的欢迎消息和一个命令提示符,那么说明您已经成功安装了 SQLite。其实这一步和双击sqlite3.exe实现的功能是一样的,都是调用命令行,只不过可以省去双击sqlite3.exe这一步。
2.3 使用
根据提示输入.help,获取命令功能提示
C:\Users\qwer>sqlite3
SQLite version 3.45.3 2024-04-15 13:34:05 (UTF-16 console I/O)
Enter ".help" for usage hints.
Connected to a transient in-memory database.
Use ".open FILENAME" to reopen on a persistent database.
sqlite> .help
.archive ... Manage SQL archives
.auth ON|OFF Show authorizer callbacks
.backup ?DB? FILE Backup DB (default "main") to FILE
.bail on|off Stop after hitting an error. Default OFF
.cd DIRECTORY Change the working directory to DIRECTORY
.changes on|off Show number of rows changed by SQL
.check GLOB Fail if output since .testcase does not match
.clone NEWDB Clone data into NEWDB from the existing database
.connection [close] [#] Open or close an auxiliary database connection
.crnl on|off Translate \n to \r\n. Default ON
.databases List names and files of attached databases
.dbconfig ?op? ?val? List or change sqlite3_db_config() options
.dbinfo ?DB? Show status information about the database
.dump ?OBJECTS? Render database content as SQL
.echo on|off Turn command echo on or off
.eqp on|off|full|... Enable or disable automatic EXPLAIN QUERY PLAN
.excel Display the output of next command in spreadsheet
.exit ?CODE? Exit this program with return-code CODE
.expert EXPERIMENTAL. Suggest indexes for queries
.explain ?on|off|auto? Change the EXPLAIN formatting mode. Default: auto
.filectrl CMD ... Run various sqlite3_file_control() operations
.fullschema ?--indent? Show schema and the content of sqlite_stat tables
.headers on|off Turn display of headers on or off
.help ?-all? ?PATTERN? Show help text for PATTERN
.import FILE TABLE Import data from FILE into TABLE
.indexes ?TABLE? Show names of indexes
.limit ?LIMIT? ?VAL? Display or change the value of an SQLITE_LIMIT
.lint OPTIONS Report potential schema issues.
.load FILE ?ENTRY? Load an extension library
.log FILE|on|off Turn logging on or off. FILE can be stderr/stdout
.mode MODE ?OPTIONS? Set output mode
.nonce STRING Suspend safe mode for one command if nonce matches
.nullvalue STRING Use STRING in place of NULL values
.once ?OPTIONS? ?FILE? Output for the next SQL command only to FILE
.open ?OPTIONS? ?FILE? Close existing database and reopen FILE
.output ?FILE? Send output to FILE or stdout if FILE is omitted
.parameter CMD ... Manage SQL parameter bindings
.print STRING... Print literal STRING
.progress N Invoke progress handler after every N opcodes
.prompt MAIN CONTINUE Replace the standard prompts
.quit Stop interpreting input stream, exit if primary.
.read FILE Read input from FILE or command output
.recover Recover as much data as possible from corrupt db.
.restore ?DB? FILE Restore content of DB (default "main") from FILE
.save ?OPTIONS? FILE Write database to FILE (an alias for .backup ...)
.scanstats on|off|est Turn sqlite3_stmt_scanstatus() metrics on or off
.schema ?PATTERN? Show the CREATE statements matching PATTERN
.separator COL ?ROW? Change the column and row separators
.session ?NAME? CMD ... Create or control sessions
.sha3sum ... Compute a SHA3 hash of database content
.shell CMD ARGS... Run CMD ARGS... in a system shell
.show Show the current values for various settings
.stats ?ARG? Show stats or turn stats on or off
.system CMD ARGS... Run CMD ARGS... in a system shell
.tables ?TABLE? List names of tables matching LIKE pattern TABLE
.timeout MS Try opening locked tables for MS milliseconds
.timer on|off Turn SQL timer on or off
.trace ?OPTIONS? Output each SQL statement as it is run
.version Show source, library and compiler versions
.vfsinfo ?AUX? Information about the top-level VFS
.vfslist List all available VFSes
.vfsname ?AUX? Print the name of the VFS stack
.width NUM1 NUM2 ... Set minimum column widths for columnar output
sqlite>
- SQLite 新建数据库
直接执行 .open filename.db 打开或创建一个 SQLite 数据库。如果文件不存在,SQLite 会自动创建它。
示例:打开或创建名为 test.db 的 SQLite 数据库文件。
sqlite> .open test.db
只要创建个数据库就可以了,下面创建表及其其他操作,在可视化工具中执行就可以,笔者使用navicat来操作。navicat的安装使用教程科查看参考文献4.3。
三、Qt集成sqlite3
Qt5以上版本可以直接使用SQLite(Qt自带驱动)。
3.1 关键问题
首先需要一个驱动,就是QSqlDatabase,这个类是管理整个数据库的核心,另外一个是关键类是QSqlQuery,用来对数据库内容增删改查的。
在使用之前要明确几个问题
- 数据库,可以直接生成一个数据库吗?还是需要先创建一个数据库再使用?
- 数据库的位置:如果使用已经存在的数据库,这个数据库放在哪里?同exe同一个文件夹?如果自动生成,生成在哪里?同exe同一个文件夹?
- 哪些函数来生成数据库、打开数据库、表的操作、增删改查的操作?
首先回答下第一个问题:可以在程序中自动生成一个数据库,不用通过上面第二节中讲的先创建一个数据库,因为Qt已经添加了sqlite驱动就是QSqlDatabase类。
第二个问题:如果使用默认的方式,需要将数据库放在exe文件的上一级目录中,因为程序会在这个目录中找有没有这个数据库,如果没有,那就在这个目录下自动创建一个,如果有,那就使用这个数据库。
当然可以自定义数据库的位置,那么程序就会去这个自定义路径下去找或者自动生成数据库。
第三个问题:主要的函数有下面几个
1)QSqlDatabase QSqlDatabase::addDatabase((const QString & type, const QString & connectionName = QLatin1String( defaultConnection )
这个就是数据库生成对象,这里面的参数一个是数据库类型,就是使用的什么数据库,第二个参数就是自定义一个连接名,就是要告诉QSqlDatabase咱们定个名,这样你使用这个名的时候QSqlDatabase知道去操作那个数据库。
这个返回的类型是QSqlDatabase,就是一个数据库实例,有了他就有权利去操作数据库的生成删除,开启关闭了。
2)void QSqlDatabase::setDatabaseName ( const QString & name ),这个函数就是设置数据库的函数,它是可以自动生成数据库的,取决于参数中的这个东西他找不找得到,如果找不到就会按规则自动生成。
这里的参数形式笔者举个例来说明下,比如有一个test.db的数据库,那么调用的时候可以这样
setDatabaseName("test.db")
这里一定要带后缀名,这样,程序会在上面我们说的默认路径下即exe文件的上一级目录中搜索这个数据库,如果找到了,那就直接用,如果没找到,那就创建一个同名的数据库。
第二个问题就是我们想自定义这个数据库的路径(不管已存在还是未存在的),那就需要这样
setDatabaseName("c:\\databases\\test.db")
这样程序就会去我们给的这个路径中去找,找到使用,找不到,在这个路径下生成同名数据库。
3)void QSqlDatabase::removeDatabase ( const QString & connectionName ) [static]
3.2 封装sqlite
上面一小节说明了创建打开数据库,下面把封装好的头文件和源文件贴下
#ifndef SQLITEHELPER_H
#define SQLITEHELPER_H
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QHash>
#include <QString>
#include <QVariant>
class SqliteHelper
{
public:
SqliteHelper();
///
/// \brief OpenSqlite 打开数据库
/// \param dbName 数据库名
/// \param connectName 连接名
/// \return
///
bool OpenSqlite(const QString &dbName,const QString &connectName);
///
/// \brief CloseSqlite 关闭数据库
/// \return
///
void CloseSqlite();
/**
* @brief getConnectName 获取连接名
* @return
*/
QString getConnectName() const;
/**
* @brief isExistTable 判断数据表是否存在
* @param tableName 数据表名
* @return
*/
bool isExistTable(const QString &tableName);
/**
* @brief createTable 创建数据表
* @param tableName 数据表名
* @param fields 字段(字段名和类型,如{"name", "varchar(5)"})
* @param pks 主键
* @return
*/
bool createTable(const QString &tableName, const QHash<QString, QString> &fields, const QList<QString> &pks);
/**
* @brief selectData 选择数据(适用于单表选择)
* @param tableName 数据表名
* @param fields 选择字段
* @param data [OUT]数据(按fields的顺序)
* @return
*/
bool selectData(const QString &tableName, const QList<QString> &fields, QList<QVariantList> &data);
/**
* @brief selectData 选择数据(适用于单表选择)
* @param tableName 数据表名
* @param fields 选择字段
* @param whereConditions where条件
* @param data [OUT]数据(按fields的顺序)
* @return
*/
bool selectData(const QString &tableName, const QList<QString> &fields, const QHash<QString, QVariant> &whereConditions,
QList<QVariantList> &data);
/**
* @brief selectDataBySql 选择数据(通过sql查询, 多表联合查询)
* @param sql select语句
* @param fields 选择字段
* @param data [OUT]数据(按fields的顺序)
* @return
*/
bool selectDataBySql(const QString &sql, const QList<QString> &fields, QList<QVariantList> &data);
/**
* @brief insertRowData 插入行数据
* @param tableName 数据表名
* @param fields 插入字段
* @param data 插入数据(数据与字段的顺序需一致)
* @return
*/
bool insertRowData(const QString &tableName, const QList<QString> &fields, const QVariantList &data);
/**
* @brief insertRowsData 插入多行数据
* @param tableName 数据表名
* @param fields 字段
* @param data 插入数据(数据与字段的顺序需一致)
* @return
*/
bool insertRowsData(const QString &tableName, const QList<QString> &fields, const QList<QVariantList> &data);
/**
* @brief updateData 更新数据
* @param tableName 数据表名
* @param data 更新的字段数据(字段名和值,如{"age", 27})
* @param whereConditions where条件(字段名和值,如{"age", 27})
* @return
*/
bool updateData(const QString &tableName, const QHash<QString, QVariant> &data,
const QHash<QString, QVariant> &whereConditions);
/**
* @brief deleteData 删除数据
* @param tableName 数据表名
* @param whereConditions where条件(字段名和值,如{"age", 27})
* @return
*/
bool deleteData(const QString &tableName, const QHash<QString, QVariant> &whereConditions);
/**
* @brief exec 执行sql语句
* @param sql sql语句
* @return 执行结果
*/
bool exec(const QString &sql);
/**
* @brief hasTransactions 是否支持事务
* @return
*/
bool hasTransactions();
/**
* @brief transaction 开启事务
* @return
*/
bool transaction();
/**
* @brief commit 事务提交
* @return
*/
bool commit();
/**
* @brief rollback 事务回滚
* @return
*/
bool rollback();
/**
* @brief size 记录数量(在执行select语句后,可用该函数获取select的大小)
* @return
*/
int size();
/**
* @brief getTableFieldsInfo 获取数据表字段
* @param tableName 数据表名
* @param fieldsName [OUT]字段
* @return
*/
bool getTableFieldsInfo(const QString &tableName, QList<QString> &fieldsName);
/**
* @brief lastQuerySql 获取最新一次执行的sql语句
* @return
*/
QString lastQuerySql();
/**
* @brief lastError 获取最新的错误信息
* @return 错误信息
*/
QString lastError() const;
private:
/**
* @brief checkTableInfo 校验数据表信息(数据表和字段是否存在)
* @param tableName 数据表名
* @param fields 字段
* @return
*/
bool checkTableInfo(const QString &tableName, const QList<QString> &fields);
protected:
QSqlDatabase sqlDb;
QString m_connectName;
QSqlQuery m_query;
QString m_lastError;
};
#endif // SQLITEHELPER_H
#include "sqlitehelper.h"
#include <QDebug>
#include <QSqlError>
#include <QSqlDriver>
//240522 这个构造函数会被引用的时候实例化
SqliteHelper::SqliteHelper() {
}
bool SqliteHelper::OpenSqlite(const QString &dbName, const QString &connectName)
{
//QPSQL为数据库类型,OTHER为连接名。
if (QSqlDatabase::connectionNames().contains(connectName)) {
//240522 返回一个对象实例,并将值复制给sqlDb
sqlDb = QSqlDatabase::database(connectName);
} else {
sqlDb = QSqlDatabase::addDatabase("QSQLITE", connectName);
}
m_connectName = connectName;
if (sqlDb.isOpen()) {
sqlDb.close();
}
//240522 设置数据库名称,这个名称可以是数据库名,也可以是数据库所在的路径
sqlDb.setDatabaseName(dbName);
if (sqlDb.open()) {
m_query = QSqlQuery(sqlDb);
return true;
}
m_lastError = sqlDb.lastError().text();
return false;
}
void SqliteHelper::CloseSqlite()
{
sqlDb.close();
}
QString SqliteHelper::getConnectName() const
{
return m_connectName;
}
bool SqliteHelper::isExistTable(const QString &tableName)
{
return sqlDb.tables().contains(tableName);
}
///
/// \brief SqliteHelper::createTable 创建表
/// \param tableName 表名
/// \param fields 字段
/// \param pks 主键
/// \return
///
bool SqliteHelper::createTable(const QString &tableName, const QHash<QString, QString> &fields,
const QList<QString> &pks)
{
//如果存在就返回
if (isExistTable(tableName)) {
m_lastError = QString("Table [%1] is already exist").arg(tableName);
return false;
}
//不存在,开始拼接字符串
QString sql = QString("create table %1 (").arg(tableName);
for (auto iter = fields.begin(); iter != fields.end(); ++iter) {
sql.append(QString("%1 %2, ").arg(iter.key(), iter.value()));
}
sql.append(QString("primary key ("));
qDebug()<<sql;
for (const auto &item : pks) {
sql.append(QString("%1, ").arg(item));
}
qDebug()<<sql;
sql.remove(sql.length() - 2, 2);
sql.append("))");
qDebug()<<sql;
if (m_query.exec(sql)) {
return true;
}
m_lastError = m_query.lastError().text();
return false;
}
bool SqliteHelper::selectData(const QString &tableName, const QList<QString> &fields, QList<QVariantList> &data)
{
if (!checkTableInfo(tableName, fields)) {
return false;
}
QString sql = QString("select ");
for (const auto &item : fields) {
sql.append(QString("%1, ").arg(item));
}
sql.remove(sql.length() - 2, 2);
sql.append(QString(" from %1").arg(tableName));
if (m_query.exec(sql)) {
while (m_query.next()) {
QVariantList temp;
for (const auto &item : fields) {
temp.append(m_query.value(item));
}
data.append(temp);
}
return true;
}
m_lastError = m_query.lastError().text();
return false;
}
bool SqliteHelper::selectData(const QString &tableName, const QList<QString> &fields,
const QHash<QString, QVariant> &whereConditions, QList<QVariantList> &data)
{
QList<QString> tempFields;
tempFields.append(fields);
tempFields.append(whereConditions.keys());
if (!checkTableInfo(tableName, tempFields)) {
return false;
}
QString sql = QString("select ");
for (const auto &item : fields) {
sql.append(QString("%1, ").arg(item));
}
sql.remove(sql.length() - 2, 2);
sql.append(QString(" from %1").arg(tableName));
sql.append(QString(" where "));
for (auto iter = whereConditions.begin(); iter != whereConditions.end(); ++iter) {
sql.append(QString("%1 = ? and ").arg(iter.key()));
}
sql.remove(sql.length() - 5, 5);
m_query.prepare(sql);
for (auto iter = whereConditions.begin(); iter != whereConditions.end(); ++iter) {
m_query.addBindValue(iter.value());
}
if (m_query.exec()) {
while (m_query.next()) {
QVariantList temp;
for (const auto &item : fields) {
temp.append(m_query.value(item));
}
data.append(temp);
}
return true;
}
m_lastError = m_query.lastError().text();
return false;
}
bool SqliteHelper::selectDataBySql(const QString &sql, const QList<QString> &fields, QList<QVariantList> &data)
{
if (m_query.exec(sql)) {
while (m_query.next()) {
QVariantList temp;
for (const auto &item : fields) {
temp.append(m_query.value(item));
}
data.append(temp);
}
return true;
}
m_lastError = m_query.lastError().text();
return false;
}
bool SqliteHelper::insertRowData(const QString &tableName, const QList<QString> &fields, const QVariantList &data)
{
if (!checkTableInfo(tableName, fields)) {
return false;
}
QString sql = QString("insert into %1(").arg(tableName);
for (const auto &item : fields) {
sql.append(QString("%1, ").arg(item));
}
sql.remove(sql.length() - 2, 2);
sql.append(QString(") values"));
QString tempValue = QString("(");
for (int i = 0; i < fields.count(); ++i) {
tempValue.append(QString("?, "));
}
tempValue.remove(tempValue.length() - 2, 2);
tempValue.append(QString(")"));
sql.append(tempValue);
m_query.prepare(sql);
for (int i = 0; i < fields.count(); ++i) {
m_query.addBindValue(data.at(i));
}
if (m_query.exec()) {
return true;
}
m_lastError = m_query.lastError().text();
return false;
}
bool SqliteHelper::insertRowsData(const QString &tableName, const QList<QString> &fields, const QList<QVariantList> &data)
{
if (!checkTableInfo(tableName, fields)) {
return false;
}
QString sql = QString("insert into %1(").arg(tableName);
for (const auto &item : fields) {
sql.append(QString("%1, ").arg(item));
}
sql.remove(sql.length() - 2, 2);
sql.append(QString(") values"));
QString tempValue = QString("(");
for (int i = 0; i < fields.count(); ++i) {
tempValue.append(QString("?, "));
}
tempValue.remove(tempValue.length() - 2, 2);
tempValue.append(QString(")"));
for (int i = 0; i < data.count(); ++i) {
sql.append(QString("%1, ").arg(tempValue));
}
sql.remove(sql.length() - 2, 2);
m_query.prepare(sql);
for (const auto &item : data) {
for (int i = 0; i < fields.count(); ++i) {
m_query.addBindValue(item.at(i));
}
}
if (m_query.exec()) {
return true;
}
m_lastError = m_query.lastError().text();
return false;
}
bool SqliteHelper::updateData(const QString &tableName, const QHash<QString, QVariant> &data,
const QHash<QString, QVariant> &whereConditions)
{
QList<QString> fields;
fields << data.keys() << whereConditions.keys();
if (!checkTableInfo(tableName, fields)) {
return false;
}
QString sql = QString("update %1 set ").arg(tableName);
for (auto iter = data.begin(); iter != data.end(); ++iter) {
sql.append(QString("%1 = ?, ").arg(iter.key()));
}
sql.remove(sql.length() - 2, 2);
sql.append(" where ");
for (auto iter = whereConditions.begin(); iter != whereConditions.end(); ++iter) {
sql.append(QString("%1 = ? and ").arg(iter.key()));
}
sql.remove(sql.length() - 5, 5);
m_query.prepare(sql);
for (auto iter = data.begin(); iter != data.end(); ++iter) {
m_query.addBindValue(iter.value());
}
for (auto iter = whereConditions.begin(); iter != whereConditions.end(); ++iter) {
m_query.addBindValue(iter.value());
}
if (m_query.exec()) {
return true;
}
m_lastError = m_query.lastError().text();
return false;
}
bool SqliteHelper::deleteData(const QString &tableName, const QHash<QString, QVariant> &whereConditions)
{
QList<QString> fields = whereConditions.keys();
if (!checkTableInfo(tableName, fields)) {
return false;
}
QString sql = QString("delete from %1 where ").arg(tableName);
for (auto iter = whereConditions.begin(); iter != whereConditions.end(); ++iter) {
sql.append(QString("%1 = ? and ").arg(iter.key()));
}
sql.remove(sql.length() - 5, 5);
m_query.prepare(sql);
for (auto iter = whereConditions.begin(); iter != whereConditions.end(); ++iter) {
m_query.addBindValue(iter.value());
}
if (m_query.exec()) {
return true;
}
m_lastError = m_query.lastError().text();
return false;
}
bool SqliteHelper::exec(const QString &sql)
{
if (m_query.exec(sql)) {
return true;
}
m_lastError = m_query.lastError().text();
return false;
}
bool SqliteHelper::hasTransactions()
{
if (m_query.driver()->hasFeature(QSqlDriver::Transactions)) {
return true;
}
m_lastError = QString("This database don't support tasnsactions");
return false;
}
bool SqliteHelper::transaction()
{
return sqlDb.transaction();
}
bool SqliteHelper::commit()
{
return sqlDb.commit();
}
bool SqliteHelper::rollback()
{
return sqlDb.rollback();
}
int SqliteHelper::size()
{
int count = -1;
m_query.first();
if (m_query.next()) {
if (m_query.driver()->hasFeature(QSqlDriver::QuerySize)) {
count = m_query.size();
} else {
m_query.last();
// m_query.at()是返回当前记录的编号(从0开始),所以最后一条记录的编号 +1 就为记录数
count = m_query.at() + 1;
}
}
m_query.first();
return count;
}
bool SqliteHelper::getTableFieldsInfo(const QString &tableName, QList<QString> &fieldsName)
{
QString sql = QString("PRAGMA table_info('%1')").arg(tableName);
if (m_query.exec(sql)) {
while (m_query.next()) {
fieldsName.append(m_query.value(1).toString());
}
return true;
}
m_lastError = m_query.lastError().text();
return false;
}
QString SqliteHelper::lastQuerySql()
{
return m_query.lastQuery();
}
QString SqliteHelper::lastError() const
{
return m_lastError;
}
bool SqliteHelper::checkTableInfo(const QString &tableName, const QList<QString> &fields)
{
if (!isExistTable(tableName)) {
m_lastError = QString("Table [%1] is not exist").arg(tableName);
return false;
}
QList<QString> fieldsName;
QStringList noFieldsName;
if (getTableFieldsInfo(tableName, fieldsName)) {
for (const auto &item : fields) {
if (!fieldsName.contains(item)) {
noFieldsName << item;
}
}
if (noFieldsName.count() > 0) {
m_lastError = QString("Table [%1] have no fields [%2]").arg(tableName).arg(noFieldsName.join(','));
return false;
}
} else {
return false;
}
return true;
}