пятница, 7 октября 2011 г.

Некорректный SQL-запрос SELECT в QSqlRelationalTableModel

Было замечено, что в QTableView, настроенном на QRelationalTableModel отображает не все строки таблицы, в частности строки, где значение хотя бы одного поля внешнего ключа (поля, у которых есть QRelation) равно NULL. Более того, оказалось, что эти строки отсутствуют и в модели.

Исследование показало, что QRelationalTableMode.selectStatement возвращает запрос следующего вида:
SELECT tbl.fld1,
       relTblAl_1.fld1 AS reftbl1_fld1_2,
       relTblAl_2.fld1,
       relTblAl_3.fld1,
       tbl.fld2,
       tbl.fld3,
FROM   tbl,
       reftbl1 relTblAl_1,
       reftbl1 relTblAl_2,
       reftbl2 relTblAl_3
WHERE  ( tbl.fkfld1 = relTblAl_1.pkfld
         AND tbl.fkfld2 = relTblAl_2.pkfld
         AND tbl.fkfld3 = relTblAl_3.pkfld )
ORDER  BY tbl.sortfld ASC 

Это INNER JOIN, который действительно не выбирает строки с пустыми ссылками. Выход напрашивается - переписать selectStatement, что бы он возвращал правильный запрос, использующий LEFT JOIN. Тогда выберутся все строки таблицы.

Запрос должен быть примерно таким:
SELECT tbl.fld1,
       relTblAl_1.fld1 AS reftbl1_fld1_2,
       relTblAl_2.fld1,
       relTblAl_3.fld1,
       tbl.fld2,
       tbl.fld3,
FROM   tbl
       LEFT JOIN reftbl1 relTblAl_1
         ON tbl.fkfld1 = relTblAl_1.pkfld
       LEFT JOIN reftbl1 relTblAl_2
         ON tbl.fkfld2 = relTblAl_2.pkfld
       LEFT JOIN reftbl2 relTblAl_3
         ON tbl.fkfld3 = relTblAl_3.pkfld
ORDER  BY tbl.sortfld ASC 

Унаследуем класс от QRelationalTableMode и перепишем метод:

class MDRelationalTableModel (QSqlRelationalTableModel):
    def selectStatement(self):
        u"""Вариант без использования метаданных, не чистите, может пригодиться"""
        tname = self.tableName()
        fields = []
        joins = []
        rec = self.record()
        for cid in xrange(rec.count()):
            fname = unicode(rec.fieldName(cid))
            relation = self.relation(cid)
            if relation.isValid():
                reftable = unicode(relation.tableName())
                alias = u'relTblAl_{0}'.format(cid)
                reffld = unicode(relation.indexColumn())
                fields.append(u'{0}.{1}'.format(alias, relation.displayColumn()))
                joins.append(u'left join {reftable} {alias} on {tname}.{fname} = {alias}.{reffld}'.format(**locals()))
            else:
                fields.append(u'{0}.{1}'.format(tname, fname))
        filter = unicode(self.filter())
        filter = u'1=1' if not filter else filter
        return QString(u'select {0} from {1} {2} where({3}) {4}'.format(u', '.join(fields), tname, 
                       u' '.join(joins), filter, unicode(self.orderByClause())))

Теперь модель содержит все необходимые строк. Думаю, написать тоже самое на C++ для чистого Qt тоже не составит никакого труда, благо модель хранит все необходимые для этого данные и всегда можно посмотреть код QRelationalTableMode.selectStatement.

Комментариев нет:

Отправить комментарий