具有 LeftJoin 模式和多个关系的 QSqlRelationalTableModel 不显示任何内容
QSqlRelationalTableModel with LeftJoin mode and multiple relations displays nothing
当我只设置一个关系时,一切正常
model = new QSqlRelationalTableModel(this, db);
model->setJoinMode(QSqlRelationalTableModel::LeftJoin);
model->setTable("someTable");
model->setRelation(model->fieldIndex("city"), QSqlRelation("city", "id", "city"));
model->select();
但是当我设置多个关系时,QTableView什么也没有显示。
model = new QSqlRelationalTableModel(this, db);
model->setJoinMode(QSqlRelationalTableModel::LeftJoin);
model->setTable("someTable");
model->setRelation(model->fieldIndex("city"), QSqlRelation("city", "id", "city"));
model->setRelation(model->fieldIndex("country"), QSqlRelation("country", "id", "country"));
model->select();
当我设置 InnerJoin 模式时一切正常(无论关系数如何)
我该如何解决?问题命名关系table?
P.S。对不起我的英语:)
所以,实际上,问题是 MS Access 的特定 SQL 语法。
QSqlRelationTableModel 生成 SQL,这对于 Access 是不正确的,当存在多个关系列且模式为 LeftJoin 时。
我的解决方案是使用SQLite.
我意识到这个问题已经很老了,但是当它出现在 Google 寻找特定问题时,我想我会分享我的解决方案。
如果您像我一样受困于 Access,您也可以子class QSqlRelationalTableModel 并重新实现 selectStatement 方法以 return 正确 SQL。在 C++ 中,这很容易,因为我猜你可以复制并更正原始源代码。我正在使用 PyQt 并将其设为 Python class。我想我会在这里分享它让其他人受益。它没有经过全面测试,但对我的情况来说效果很好。
from PyQt4.QtSql import *
# Reimplementation of the selectStatement for use with Access databases
# since the standard method creates Access incompatible SQL statements on LEFT JOIN
class QSqlRelationalAccessTableModel(QSqlRelationalTableModel):
joinMode = QSqlRelationalTableModel.InnerJoin
relations = {}
def selectStatement(self):
query = ''
if not self.tableName():
return query
if not self.relations:
return QSqlRelationalTableModel.selectStatement(self)
tList = ''
fList = ''
where = ''
driver = self.database().driver()
rec = self.record()
tables = []
# Count how many times each field name occurs in the record
fieldNames = {}
fieldList = []
for idx in range(rec.count()):
relation = self.relation(idx)
if relation.isValid():
name = relation.displayColumn()
if driver.isIdentifierEscaped(name, QSqlDriver.FieldName):
name = driver.stripDelimiters(name, QSqlDriver.FieldName)
relRec = self.database().record(relation.tableName())
for i in range(relRec.count()):
if name.lower() == relRec.fieldName(i).lower():
name = relRec.fieldName(i)
break
else:
name = rec.fieldName(idx)
fieldNames[name] = fieldNames.get(name, 0) + 1
fieldList.append(name)
for idx in range(rec.count()):
relation = self.relation(idx)
if relation.isValid():
relTableAlias = 'relTblAl_%d' % idx
if len(fList):
fList += ', '
fList += relTableAlias + '.' + relation.displayColumn()
# If there are duplicate field names they must be aliased
if fieldNames[fieldList[idx]] > 1:
relTableName = relation.tableName().rsplit('.', 1)[0]
if driver.isIdentifierEscaped(relTableName, QSqlDriver.TableName):
relTableName = driver.stripDelimiters(relTableName, QSqlDriver.TableName)
displayColumn = relation.displayColumn()
if driver.isIdentifierEscaped(displayColumn, QSqlDriver.FieldName):
displayColumn = driver.stripDelimiters(displayColumn, QSqlDriver.FieldName)
fList += ' AS %s_%s_%s' % (relTableName, displayColumn, fieldNames[fieldList[idx]])
fieldNames[fieldList[idx]] -= 1
if self.joinMode == QSqlRelationalTableModel.InnerJoin:
# Original Qt comment:
# this needs fixing!! the below if is borken.
# Use LeftJoin mode if you want correct behavior
tables.append(relation.tableName() + ' ' + relTableAlias)
if where:
where += ' AND '
where += self.tableName() + '.' + driver.escapeIdentifier(rec.fieldName(idx), QSqlDriver.FieldName)
where += ' = ' + relTableAlias + '.' + relation.indexColumn() + ')'
else:
tables.append(' LEFT JOIN')
tables.append(relation.tableName() + ' ' + relTableAlias)
tables.append('ON')
clause = self.tableName() + '.' + driver.escapeIdentifier(rec.fieldName(idx), QSqlDriver.FieldName)
clause += ' = ' + relTableAlias + '.' + relation.indexColumn() + ')'
tables.append(clause)
else:
if len(fList):
fList += ', '
fList += self.tableName() + '.' + driver.escapeIdentifier(rec.fieldName(idx), QSqlDriver.FieldName)
if self.joinMode == QSqlRelationalTableModel.InnerJoin and len(tables):
tList += ', '.join(tables)
if len(tList):
tList = ', ' + tList
else:
# left join!
tList += ' '.join(tables)
if not len(fList):
return query
# Assemble query parts
tList = self.tableName() + tList
if self.joinMode == QSqlRelationalTableModel.LeftJoin:
tList = '(' * len(self.relations) + tList
query = 'SELECT ' + fList + ' FROM ' + tList
if self.joinMode == QSqlRelationalTableModel.InnerJoin:
query = self.qAppendWhereClause(query, where, self.filter())
else:
# left join!
if self.filter():
query += ' WHERE (' + self.filter() + ')'
if self.orderByClause():
query += ' ' + self.orderByClause()
return query
# Make joinmode accessible
def setJoinMode(self, joinMode):
self.joinMode = joinMode
QSqlRelationalTableModel.setJoinMode(self, joinMode)
# Keep track of relations
def setRelation(self, column, relation):
if relation.isValid():
self.relations[column] = relation
else:
if column in self.relations:
del self.relations[column]
QSqlRelationalTableModel.setRelation(self, column, relation)
def qAppendWhereClause(self, query, clause1, clause2):
if not len(clause1) and not len(clause2):
return
if not len(clause1) or not len(clause2):
query += ' WHERE (' + clause1 + clause2 + ')'
else:
query += ' WHERE (' + clause1 + ') AND (' + clause2 + ') '
return query
当我只设置一个关系时,一切正常
model = new QSqlRelationalTableModel(this, db);
model->setJoinMode(QSqlRelationalTableModel::LeftJoin);
model->setTable("someTable");
model->setRelation(model->fieldIndex("city"), QSqlRelation("city", "id", "city"));
model->select();
但是当我设置多个关系时,QTableView什么也没有显示。
model = new QSqlRelationalTableModel(this, db);
model->setJoinMode(QSqlRelationalTableModel::LeftJoin);
model->setTable("someTable");
model->setRelation(model->fieldIndex("city"), QSqlRelation("city", "id", "city"));
model->setRelation(model->fieldIndex("country"), QSqlRelation("country", "id", "country"));
model->select();
当我设置 InnerJoin 模式时一切正常(无论关系数如何)
我该如何解决?问题命名关系table?
P.S。对不起我的英语:)
所以,实际上,问题是 MS Access 的特定 SQL 语法。
QSqlRelationTableModel 生成 SQL,这对于 Access 是不正确的,当存在多个关系列且模式为 LeftJoin 时。
我的解决方案是使用SQLite.
我意识到这个问题已经很老了,但是当它出现在 Google 寻找特定问题时,我想我会分享我的解决方案。
如果您像我一样受困于 Access,您也可以子class QSqlRelationalTableModel 并重新实现 selectStatement 方法以 return 正确 SQL。在 C++ 中,这很容易,因为我猜你可以复制并更正原始源代码。我正在使用 PyQt 并将其设为 Python class。我想我会在这里分享它让其他人受益。它没有经过全面测试,但对我的情况来说效果很好。
from PyQt4.QtSql import *
# Reimplementation of the selectStatement for use with Access databases
# since the standard method creates Access incompatible SQL statements on LEFT JOIN
class QSqlRelationalAccessTableModel(QSqlRelationalTableModel):
joinMode = QSqlRelationalTableModel.InnerJoin
relations = {}
def selectStatement(self):
query = ''
if not self.tableName():
return query
if not self.relations:
return QSqlRelationalTableModel.selectStatement(self)
tList = ''
fList = ''
where = ''
driver = self.database().driver()
rec = self.record()
tables = []
# Count how many times each field name occurs in the record
fieldNames = {}
fieldList = []
for idx in range(rec.count()):
relation = self.relation(idx)
if relation.isValid():
name = relation.displayColumn()
if driver.isIdentifierEscaped(name, QSqlDriver.FieldName):
name = driver.stripDelimiters(name, QSqlDriver.FieldName)
relRec = self.database().record(relation.tableName())
for i in range(relRec.count()):
if name.lower() == relRec.fieldName(i).lower():
name = relRec.fieldName(i)
break
else:
name = rec.fieldName(idx)
fieldNames[name] = fieldNames.get(name, 0) + 1
fieldList.append(name)
for idx in range(rec.count()):
relation = self.relation(idx)
if relation.isValid():
relTableAlias = 'relTblAl_%d' % idx
if len(fList):
fList += ', '
fList += relTableAlias + '.' + relation.displayColumn()
# If there are duplicate field names they must be aliased
if fieldNames[fieldList[idx]] > 1:
relTableName = relation.tableName().rsplit('.', 1)[0]
if driver.isIdentifierEscaped(relTableName, QSqlDriver.TableName):
relTableName = driver.stripDelimiters(relTableName, QSqlDriver.TableName)
displayColumn = relation.displayColumn()
if driver.isIdentifierEscaped(displayColumn, QSqlDriver.FieldName):
displayColumn = driver.stripDelimiters(displayColumn, QSqlDriver.FieldName)
fList += ' AS %s_%s_%s' % (relTableName, displayColumn, fieldNames[fieldList[idx]])
fieldNames[fieldList[idx]] -= 1
if self.joinMode == QSqlRelationalTableModel.InnerJoin:
# Original Qt comment:
# this needs fixing!! the below if is borken.
# Use LeftJoin mode if you want correct behavior
tables.append(relation.tableName() + ' ' + relTableAlias)
if where:
where += ' AND '
where += self.tableName() + '.' + driver.escapeIdentifier(rec.fieldName(idx), QSqlDriver.FieldName)
where += ' = ' + relTableAlias + '.' + relation.indexColumn() + ')'
else:
tables.append(' LEFT JOIN')
tables.append(relation.tableName() + ' ' + relTableAlias)
tables.append('ON')
clause = self.tableName() + '.' + driver.escapeIdentifier(rec.fieldName(idx), QSqlDriver.FieldName)
clause += ' = ' + relTableAlias + '.' + relation.indexColumn() + ')'
tables.append(clause)
else:
if len(fList):
fList += ', '
fList += self.tableName() + '.' + driver.escapeIdentifier(rec.fieldName(idx), QSqlDriver.FieldName)
if self.joinMode == QSqlRelationalTableModel.InnerJoin and len(tables):
tList += ', '.join(tables)
if len(tList):
tList = ', ' + tList
else:
# left join!
tList += ' '.join(tables)
if not len(fList):
return query
# Assemble query parts
tList = self.tableName() + tList
if self.joinMode == QSqlRelationalTableModel.LeftJoin:
tList = '(' * len(self.relations) + tList
query = 'SELECT ' + fList + ' FROM ' + tList
if self.joinMode == QSqlRelationalTableModel.InnerJoin:
query = self.qAppendWhereClause(query, where, self.filter())
else:
# left join!
if self.filter():
query += ' WHERE (' + self.filter() + ')'
if self.orderByClause():
query += ' ' + self.orderByClause()
return query
# Make joinmode accessible
def setJoinMode(self, joinMode):
self.joinMode = joinMode
QSqlRelationalTableModel.setJoinMode(self, joinMode)
# Keep track of relations
def setRelation(self, column, relation):
if relation.isValid():
self.relations[column] = relation
else:
if column in self.relations:
del self.relations[column]
QSqlRelationalTableModel.setRelation(self, column, relation)
def qAppendWhereClause(self, query, clause1, clause2):
if not len(clause1) and not len(clause2):
return
if not len(clause1) or not len(clause2):
query += ' WHERE (' + clause1 + clause2 + ')'
else:
query += ' WHERE (' + clause1 + ') AND (' + clause2 + ') '
return query