背景:
业务数据源有mdb格式(文件型数据库),shp格式(存储地理实体信息)
一开始,产品的同学让我做个命令检查图层字段,我直接写完可以用了。因为客户单位检查项的增加,又要做个命令来检查另外一个图层的另外的字段。我发现这五六个需求都是类似的。于是就想将业务检查需求做为一个sql语句,查询结果就是不符合检查需求的数据。
//因为shp不支持sql的查询,于是设计了创建临时表的方式满足繁杂的业务质检的需求。
//可能涉及mdb的若干个二维表与shp文件的若干个图层的检查。
//二维表与图层可混查,在sql语句中,图层名前面加TEMP_以区分。
//为了提升性能,临时表的字段只有使用到的字段,不是图层所有字段。
于是,最终只有一个命令,可以使用sql来进行二维表与图层的混合字段检查,产品的同学根据需求去编辑不同的sql质检语句即可。
目录
一,取出查询的字段和表
//查询的字段列表(需要输出的)m_fieldList(qstringlist)
//查询涉及的表的列表 m_tableList(qstringlist)
if ("SELECT" != m_dataViewSql.left(6).toUpper())
{
printf(QString("只支持SELECT查询[%1]").arg(m_dataViewSql));
return;
}
else
{
m_dataViewSql = m_dataViewSql.toUpper();
int indexStart = m_dataViewSql.indexOf("SELECT") + 6;
int indexEnd = m_dataViewSql.indexOf("FROM");
QString fields = m_dataViewSql;
if (-1 != indexEnd)
{
fields = m_dataViewSql.left(indexEnd - 1);
}
fields = fields.right(fields.size() - indexStart).trimmed();
if ("*" == fields)
{
printf(QString("暂不支持*查询[%1]").arg(m_dataViewSql));
return;
}
m_fieldList = fields.split(",");
for (int i = 0; i < m_fieldList.size(); ++i)
{
QString field = m_fieldList.at(i).trimmed();
int index = field.indexOf(".");
if (-1 != index)
{
m_fieldList[i] = field.right(field.size() - index - 1);
}
}
//从多个from后取得所有表名
indexStart = 0;
while (1)
{
indexEnd = m_dataViewSql.indexOf("FROM", indexStart);
if (-1 == indexEnd)
{
break;
}
indexStart = indexEnd + 4;
indexEnd = m_dataViewSql.indexOf("WHERE", indexStart);
QString tables = m_dataViewSql;
if (-1 != indexEnd)
{
tables = m_dataViewSql.left(indexEnd - 1);
}
tables = tables.right(tables.size() - indexStart).trimmed();
QStringList tablenames = tables.split(",");
for (int i = 0; i < tablenames.size(); ++i)
{
QString srcData = tablenames.at(i);
QString name = cutTempName(srcData, 0, true);
if (!name.isEmpty() && -1 == m_tableList.indexOf(name))
{
m_tableList.append(name);
}
}
}
if (m_fieldList.empty())
{
printf("查询的字段为空");
return;
}
}
二,从表名列表获取需要创建的临时表名
存到列表中: m_tempFieldmap
for (int i = 0; i < m_tableList.size(); ++i)
{
QString tableName = m_tableList.at(i);
if ("TEMP_" == tableName.left(5))
{
QString tName = tableName.right(tableName.size() - 5);
m_tempTableList.append(tName);
QStringList fields;
m_tempFieldmap.insert(tName, fields);
}
}
三,截取所有表及其需要的字段
if (m_tempTableList.size())
{
int index = 0;
int iStart = 0;
while (1)
{
index = m_dataViewSql.indexOf("TEMP_", index);
if (index != -1)
{
QString TempName = cutTempName(m_dataViewSql, index);
if (!TempName.isEmpty())
{
QStringList tempNameAndField = TempName.split(".");
if (tempNameAndField.size() == 2)
{
QString tempTable = tempNameAndField.at(0);
tempTable = tempTable.right(tempTable.size() - 5);
QString tempFeild = tempNameAndField.at(1);
if (-1 == m_tempFieldmap[tempTable].indexOf(tempFeild))
{
m_tempFieldmap[tempTable].append(tempFeild);
}
}
}
index += 5;
}
else
{
break;
}
}
}
四,创建临时表
//逐个建表插入数据
for (int i = 0; i < m_tempTableList.size(); ++i)
{
g_pInterface->iDataSetProgressValue(i + 1, m_tempTableList.size());
if (createTable(m_tempTableList.at(i)))
{
writeTable(m_tempTableList.at(i));
}
}
bool createTable(const QString &layerName)
{
Layer *pLayer = nullptr;
QString layName = m_szqDM + layerName;
g_pInterface->GetLayer(pLayer, layName);
if (nullptr == pLayer)
{
printf(QString("获取图层[%1]打开失败!").arg(layName));
return false;
}
QStringList needField = m_tempFieldmap[layerName]; //所需建表的字段名称列表
QString tableFields; //sql括号内
Fields *pFields = pLayer->Fields();
//遍历需要建表的字段,映射到mdb的字段类型
for (int i = 0; i < needField.size(); ++i)
{
QString field = needField.at(i);
Field *pfield = pFields->field(field);
if (pfield == nullptr)
{
printf(QString("图层[%1]无拓展属性[%2]").arg(layerName).arg(field));
continue;
}
int length = pfield->Length();
int type = pfield->filedType();
QString acessSqlType;
switch (type)
{
case QVariant::Bool:
acessSqlType = "BIT";
break;
case QVariant::Int:
acessSqlType = "INT";
break;
case QVariant::LongLong:
acessSqlType = "INTEGER";
break;
case QVariant::Double:
acessSqlType = "FLOAT";
break;
case QVariant::Char:
case QVariant::String:
acessSqlType = QString("CHAR(%1)").arg(length);
break;
case QVariant::DateTime:
case QVariant::Date:
acessSqlType = "DATETIME";
break;
default:
printf(QString("未配置类型[%1]").arg(type));
acessSqlType = "SMALLINT";
break;
}
tableFields += field + " " + acessSqlType + ",";
}
if (tableFields.isEmpty())
{
printf(QString("图层[%1]无拓展属性,不创建临时表。").arg(layerName));
return false;
}
tableFields = tableFields.left(tableFields.size() - 1);
QString CreatTempTableSql = QString("CREATE TABLE TEMP_%1 (ENTITYPOINT CHAR(16), %2)")
.arg(layerName).arg(tableFields);
if (m_sqlQuery.exec(CreatTempTableSql))
{
//printf(QString("sql[%1]建表成功!").arg(CreatTempTableSql));
}
else
{
//先输出建表错误信息,否则会被覆盖
QString strErrors = m_sqlQuery.lastError().text();
//printf(QString("执行sql[%1]失败!").arg(CreatTempTableSql));
printf(strErrors);
QString dropTableSql = "DROP TABLE TEMP_" + layerName + " ";
if (!m_sqlQuery.exec(dropTableSql))
{
QString strErrors = m_sqlQuery.lastError().text();
//printf(QString("执行sql[%1]失败!").arg(CreatTempTableSql));
printf(strErrors);
}
return false;
}
return true;
}
void writeTable(const QString &layerName)
{
Layer *pLayer = nullptr;
QString layName = m_szqDM + layerName;
GetLayer(pLayer, layName);
if (nullptr == pLayer)
{
printf(QString("获取图层[%1]打开失败!").arg(layName));
return;
}
//组prepare sql,在最前面加上实体指针,后续查询结果需要使用到
QStringList needField = m_tempFieldmap[layerName]; //所需建表的字段名称列表
QString tableFields = "ENTITYPOINT,"; //实体指针
QString questions = "?,";
for (const QString &field : needField)
{
tableFields += field + ",";
questions += "?,";
}
if (tableFields.length() <= 16)
{
printf(QString("%1无拓展属性").arg(layName));
return;
}
tableFields = tableFields.left(tableFields.length() - 1);
questions = questions.left(questions.length() - 1);
QString insertSql = QString("INSERT INTO TEMP_%1(%2) VALUES(%3) ").arg(layerName).arg(tableFields).arg(questions);
m_sqlQuery.prepare(insertSql);
//开事务
if (!m_targetdb.transaction())
{
printf("writeTable Error 354. " + m_targetdb.lastError().text());
return;
}
QList< Entity *> pEntList = pLayer->items();
int step = 0;
for ( Entity *pEnt : pEntList)
{
qlonglong longEnt = (qlonglong)pEnt;
m_sqlQuery.addBindValue(QString::number(longEnt));
//printf(QString("实体指针测试写表[%1]").arg(QString::number(longEnt)));
for (const QString &field : needField)
{
QString data = pEnt->getXData(field).toString();
m_sqlQuery.addBindValue(data);
}
if (m_sqlQuery.exec())
{
//printf(QString("执行sql[%1]成功!").arg(insertSql));
}
else
{
QString strErrors = m_sqlQuery.lastError().text();
//printf(QString("执行sql[%1]失败!").arg(insertSql));
printf(strErrors);
break;
}
}
//提交
if (!m_targetdb.commit())
{
printf("writeTable Error 385. " + m_targetdb.lastError().text());
//回滚
if (!m_targetdb.rollback())
{
printf("writeTable Error 389. " + m_targetdb.lastError().text());
}
}
}
五,执行sql语句
在查询结果里将检查结果输出。
runSQL(m_dataViewSql);
void runSQL(const QString &sql)
{
QString strSql = sql;
//如果是图层,查询加上实体指针。否则不用修改,直接执行
if (m_isLayer)
{
strSql = "SELECT " + m_LayerName + ".ENTITYPOINT, " + strSql.right(strSql.size() - 7);
}
if (m_sqlQuery.exec(strSql))
{
while (m_sqlQuery.next())
{
//取出结果,按业务需求写数据。
QVariantList dataViewData;
iDataEntity *pEnt;
QString dataEntpStr;
QString fieldData;
if (m_isLayer)
{
if (m_sqlQuery.value("ENTITYPOINT").isValid())
{
dataEntpStr = m_sqlQuery.value("ENTITYPOINT").toString().trimmed();
pEnt = (iDataEntity *)(dataEntpStr.toLongLong());
}
}
//写入继承属性
QString fieldName;
for (int i = 0; i < m_fieldList.size(); ++i)
{
fieldName = m_fieldList.at(i);
if (m_sqlQuery.value(fieldName).isValid())
{
fieldData = m_sqlQuery.value(fieldName).toString().trimmed();
}
dataViewData.append(fieldData);
}
//后面略
。
。
。
。
}
}
else
{
QString strErrors = m_sqlQuery.lastError().text();
printf(strErrors);
}
}
六,删除临时表
for (int i = 0; i < m_tempTableList.size(); ++i)
{
deleteTable(m_tempTableList.at(i));
}