【问题标题】:QSqlRelationalTableModel with LeftJoin mode and multiple relations displays nothing具有 LeftJoin 模式和多个关系的 QSqlRelationalTableModel 不显示任何内容
【发布时间】:2015-08-19 23:18:39
【问题描述】:

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

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.对不起我的英语:)

【问题讨论】:

    标签: qt left-join qtsql


    【解决方案1】:

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

    如果您像我一样被 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
    

    【讨论】:

      【解决方案2】:

      所以,真的,问题在于 MS Access 的特定 SQL 语法

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

      【讨论】:

      • ms 访问有一些优势,您可以直观地在数据库中设置各种连接,然后在 python 中简单地读取它们。无需在 python 中编写 select 语句。另外,access 有真正的日期字段,而 sqlite 没有。最重要的是,您可以创建复杂的报告。
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-11-15
      • 2011-10-10
      • 2012-09-28
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多