具有LeftJoin模式和多个关系的QSqlRelationalTableModel不显示任何内容

时间:2015-08-19 23:18:40

标签: qt left-join qtsql

当我只设置一个关系时,一切正常

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 模式时,这一切都很好(无论关系计数如何)
我该如何解决?是关系表的问题命名吗?是P.S。对不起我的英文:)

2 个答案:

答案 0 :(得分:2)

我意识到这个问题已经很老了,但是当它出现在谷歌寻找特定问题时,我认为我会分享我的解决方案。

如果像我一样,你仍然坚持使用Access,那么你也可以继承QSqlRelationalTableModel并重新实现selectStatement方法以返回正确的SQL。在C ++中,这很简单,因为我可以复制并更正原始源代码。我正在使用PyQt并使其成为Python类。我以为我会在这里分享,让其他人受益。它没有经过彻底的测试,但对我的情况非常有效。

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

答案 1 :(得分:0)

所以,实际上,问题是 MS Access的特定SQL语法

当存在多个关系列且模式为LeftJoin时,QSqlRelationTableModel生成SQL,这对于Access不正确。
我的解决方案是使用SQLite。