Russian Qt Forum
Сентябрь 30, 2024, 10:21 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: QSqlRelationTableModel для одного столбца вывести несколько связанных значений  (Прочитано 9203 раз)
break
Гипер активный житель
*****
Offline Offline

Сообщений: 846


Просмотр профиля
« : Май 09, 2009, 17:16 »

хочу для одного столбца вывести несколько связанных значений, то есть

таблица ПОЕЗДКИ (TRAVELS) имеет вторичный ключ на таблицу ВОДИТЕЛИ (DRIVERS)

в таблице ВОДИТЕЛИ есть поля ИМЯ, ФАМИЛИЯ, ОТЧЕСТВО

Хочется выводить окошко с таблицей поездок так чтобы выводились и ИМЯ, ФАМИЛИЯ, ОТЧЕСТВО в соответствие со столбцом DRIVER_ID из TRAVELS

QSqlRelationTableModel позволяет установить соответствие только одному столбцу, заводить в БД втаблице TRAVELS еще поля дублирующие DRIVER_ID кажется глупым - т.к. перестраивать БД из-за нужд интерфейса что-то не то.
Слепливать все поля в одно при выводе  ИМЯ + ФАМИЛИЯ + ОТЧЕСТВО - тоже не хочется т.к. надо сортировать и фильтровать отдельно по имени и фамилии.

Если бы было можно создать псевдостолбцы на основе QSqlRelationTableModel с соответствие DRIVER_ID но выбором конкретного поля из DRIVERS.

какие могут быть варианты?

раньше в DELPHI мог сделать простой запрос:

select t.*, d.*
from TRAVELS t, DRIVERS d
where (t.DRIVER_ID = d.ROW_ID)

и получить желаемое
« Последнее редактирование: Май 09, 2009, 17:18 от break » Записан
ритт
Гость
« Ответ #1 : Май 09, 2009, 17:29 »

QSqlRelationTableModel не подойдёт.

select t.*, d.*
from TRAVELS t, DRIVERS d
where (t.DRIVER_ID = d.ROW_ID)
столбцов сколько?)
Записан
break
Гипер активный житель
*****
Offline Offline

Сообщений: 846


Просмотр профиля
« Ответ #2 : Май 10, 2009, 00:06 »

Цель не бесконечное количество столбцов - например я мог бы указать так

select t.START_DATE_TIME, t.DESCRIPT, d.NAME, d.SURNAME, d.PATRONIMYC, d.B_DATE
from TRAVELS t, DRIVERS d
where (t.DRIVER_ID = d.ROW_ID)
Записан
ритт
Гость
« Ответ #3 : Май 10, 2009, 03:57 »

видимо, я чего-то не понял...
чем плох последний запрос? и почему нужна именно QSqlRelationTableModel?
Записан
break
Гипер активный житель
*****
Offline Offline

Сообщений: 846


Просмотр профиля
« Ответ #4 : Май 11, 2009, 13:10 »

QSqlRelationModel нужен т.к. я написал небольшой класс "справочник" - представляющий из себя форму с табличкой (TableView) сделал это для того чтобы во многих местах программы использовать один интерфейс для пльзователя и собственно не переписывать один и тот же код.
Справочник может работать в режимер редактирования и в режиме выбора записей, сразу встроен фильтр по столбцам - во общем н важно...



Когдато то аналогичное делал и в делфи, но тамошние компоненты позволяли задать не имя таблицы а запросы SelectSQL, UpdateSQL и т.д. - тоесть полная свобода в плане псевдостолбцов.
Пока вижу единственным решением писать свою модель с поддержкой аналогичных возможностей (это и планировал), но сейчас времени нет на это, поэтому решил узнать как другие с такой ситуацией борятся - может есть более простой путь или кто-то уже писал модель похожую (в плане задания SQL запросов) на всякие датасеты из делфи - типа FIBSQLDataset и т.д.

h файл:

Код:
#ifndef __DRIVERS_DICT_H__
#define __DRIVERS_DICT_H__

#include <QtGui>
#include <QtSql>
#include "ui_TableDict.h"

#define ROW_ID_FIELD_NAME "ROW_ID"

struct CColumnProps
{
QString  sDisplayName;
bool     bVisible;
bool     bReadOnly;
QVariant vDefaultValue;

CColumnProps() {}
CColumnProps( QString displayName, bool visible, bool readOnly, QVariant defaultValue )
{
sDisplayName  = displayName;
bVisible      = visible;
bReadOnly     = readOnly;
vDefaultValue = defaultValue;
}
};

class CColumnFilter : public QSortFilterProxyModel
{
QVector<QString> m_filterValues;
public:
void setExpCount( int nVal )                 { m_filterValues.resize( nVal ); }
void setColumnExp( int column, QString exp ) { m_filterValues[ column ] = exp; }
QVariant data( const QModelIndex &index, int role ) const;

protected:
virtual bool filterAcceptsRow ( int source_row, const QModelIndex & source_parent ) const;
};

class CTableDict : public QDialog
{
Q_OBJECT;

Ui::TableDict m_ui;                              // User interface
CColumnFilter            m_mdlColFilter;         // Модель содержащая условия фильтра по колонкам
QSqlRelationalTableModel m_mdlData;              // Главная модель данных
QList<int>               m_readOnlyRows;
QString                  m_sDictName;

bool                     m_bCanAddRow;
bool                     m_bCanDelRow;
bool                     m_bEditMode_Allowed;
bool                     m_bSelectMode_Allowed;

int                      m_nFirstVisibleColumn;

QHash<QString, CColumnProps> m_columnProps;

QAbstractItemView::EditTriggers m_enabledEditTriggers;
QAbstractItemView::EditTriggers m_disabledEditTriggers;

public:
enum DictWorkMode
{
dwmSelect,
dwmEdit,

dwmCount
};

// Construction/destruction
CTableDict( QWidget * parent = 0 );
virtual ~CTableDict();

void refreshData();

inline QHash<QString, CColumnProps>& columnProps() { return m_columnProps; }
inline QList<int>&                   readOnlyRows()     { return m_readOnlyRows; }

inline void setTableName( QString sVal )        { m_mdlData.setTable( sVal ); }
void setDictName( QString sVal );
void setWorkMode( DictWorkMode mode );

inline void setCanAddRow ( bool bVal )          { m_bCanAddRow  = bVal; }
inline void setCanDelRow ( bool bVal )          { m_bCanDelRow  = bVal; }
inline void setEditMode_Allowed   ( bool bVal ) { m_bEditMode_Allowed   = bVal; }
inline void setSelectMode_Allowed ( bool bVal ) { m_bSelectMode_Allowed = bVal; }

inline QSqlRelationalTableModel& dataModel()    { return m_mdlData; }

private slots:
void colFilter_currentColumnChanged ( const QModelIndex & current, const QModelIndex & previous );
void data_currentChanged ( const QModelIndex & current, const QModelIndex & previous );
void data_columnResized( int logicalIndex, int oldSize, int newSize );

void on_tvColumnFilter_cellChanged ( int row, int column );
void on_tvData_doubleClicked ( const QModelIndex & index );

void on_btnRefresh_pressed();
void on_btnAddRow_pressed();
void on_btnDelRow_pressed();

void on_btnSelect_pressed();
void on_btnCancel_pressed();
void on_btnCommit_pressed();
void on_btnRollback_pressed();

void on_btnChangeWorkMode_pressed();

protected:
virtual void keyPressEvent(QKeyEvent * event);
virtual void showEvent ( QShowEvent * event );

private:
DictWorkMode m_workMode;
QString dwmToDispString( DictWorkMode mode );
void updateColumnFilter_LeftColumn();
void returnCurrent_RowID();
};

#endif // __DRIVERS_DICT_H__

cpp файл:

Код:
#include "TableDict.h"
#include <QSqlError>

// TODO: Написать делегат для столбцов с датами
// TODO: Написать делегат для столбцов с timestamp
// TODO: Вывести значет восклицательного знака когда данные изменилсь - спросить при выходе

QVariant CColumnFilter::data( const QModelIndex &index, int role ) const
{
if ( role == Qt::BackgroundRole )
{
// подсвечиваем фон удаленных записей красным цветом
if ( headerData( index.row(), Qt::Vertical ) == QLatin1String("!") )
return QColor( Qt::red );
};

return QSortFilterProxyModel::data( index, role );
}

bool CColumnFilter::filterAcceptsRow( int source_row, const QModelIndex & source_parent ) const
{
QString exp;
QModelIndex index;
for ( int i = 0; i < m_filterValues.count(); ++i )
{
exp   = m_filterValues[i];
index = sourceModel()->index( source_row, i, source_parent );
if ( !sourceModel()->data( index ).toString().contains( exp, Qt::CaseInsensitive ) )
return false;
};
return true;
}

CTableDict::CTableDict( QWidget * parent ) :
QDialog( parent ),
m_mdlData( 0, QSqlDatabase::database( QLatin1String(QSqlDatabase::defaultConnection), false ) )
{
//Q_INIT_RESOURCE( SD_Images );

m_ui.setupUi( this );

m_bCanAddRow  = false;
m_bCanDelRow  = false;
m_bEditMode_Allowed   = true;
m_bSelectMode_Allowed = true;

m_nFirstVisibleColumn = -1;

m_ui.tvData->setModel( &m_mdlColFilter );
QObject::connect( m_ui.tvData->selectionModel(),
  SIGNAL(currentChanged ( const QModelIndex &, const QModelIndex & )),
  this, SLOT(data_currentChanged ( const QModelIndex &, const QModelIndex & )) );

QObject::connect( m_ui.tvData->horizontalHeader(), SIGNAL(sectionResized ( int, int, int )),
  this, SLOT(data_columnResized ( int, int, int )) );

QObject::connect( m_ui.tvColumnFilter->selectionModel(),
  SIGNAL(currentColumnChanged ( const QModelIndex &, const QModelIndex & )),
  this, SLOT(colFilter_currentColumnChanged ( const QModelIndex &, const QModelIndex & )) );

m_mdlData.setEditStrategy( QSqlTableModel::OnManualSubmit );

m_mdlColFilter.setDynamicSortFilter( true );
m_mdlColFilter.setSourceModel( &m_mdlData );

m_ui.tvColumnFilter->horizontalHeader()->setVisible( false );
m_ui.tvColumnFilter->verticalHeader()->setVisible( false );

m_enabledEditTriggers = QAbstractItemView::DoubleClicked |
QAbstractItemView::EditKeyPressed |
QAbstractItemView::AnyKeyPressed |
QAbstractItemView::SelectedClicked;

m_disabledEditTriggers = QAbstractItemView::NoEditTriggers;

setWorkMode( dwmEdit );
}

CTableDict::~CTableDict()
{
//Q_CLEANUP_RESOURCE(SD_Images);
}

void CTableDict::refreshData()
{
m_mdlData.select();

// для таблицы с фильром  устанавливаем на 1 столбец больше, т.к. 1 столбец используется
// для выравнивания относительно вертикального хидера таблицы данных
m_ui.tvColumnFilter->setColumnCount( m_mdlData.columnCount()+1 );
// установка числа столбцов-выражений по которым будет фильтрация
m_mdlColFilter.setExpCount( m_mdlData.columnCount() );

QSqlRecord rec = m_mdlData.record();

m_nFirstVisibleColumn = rec.count()-1;
for ( int i = 0; i < rec.count(); ++i )
{
QString sFieldName = rec.field( i ).name();

// если для этой колонки были заданы параметры
if ( m_columnProps.contains( sFieldName ) )
{
CColumnProps colProps = m_columnProps.value( sFieldName );

m_ui.tvData         -> setColumnHidden( i, !colProps.bVisible );
m_ui.tvColumnFilter -> setColumnHidden( i+1, !colProps.bVisible );

// setDefaultValue пока использовать не получилось, вставляю значения по умолчанию в addRow
rec.field( i ).setDefaultValue( colProps.vDefaultValue );

// в Qt::DisplayRole названия колонок для вывода на экран
m_mdlData.setHeaderData( i, Qt::Horizontal, QVariant( colProps.sDisplayName ), Qt::DisplayRole );

// определение индекса первого видимого столбца
if ( colProps.bVisible )
if ( m_nFirstVisibleColumn > i )
m_nFirstVisibleColumn = i;
};
};

m_ui.tvData->resizeColumnsToContents();
m_ui.tvData->horizontalHeader()->setStretchLastSection( true );

m_ui.tvData->setFocus();
if ( m_workMode == dwmSelect )
m_ui.tvData->selectRow( 0 );
else
m_ui.tvData->setCurrentIndex( m_mdlColFilter.index(0, m_nFirstVisibleColumn) );

m_mdlColFilter.invalidate(); //// странный глюк раньше работало без этого!!! ( до SDK 2009.02 )
data_currentChanged ( QModelIndex(), QModelIndex() );
}

void CTableDict::setDictName( QString sVal )
{
m_sDictName = sVal;
}

void CTableDict::setWorkMode( DictWorkMode mode )
{
m_workMode = mode;

QString sTitle = QString::fromUtf8("Справочник: ") + m_sDictName;
QString sMode = dwmToDispString( m_workMode );

sTitle = sTitle + " (" + sMode + ")";
setWindowTitle( sTitle );

m_ui.btnSelect     -> setVisible( m_workMode == dwmSelect );
m_ui.btnCancel     -> setVisible( m_workMode == dwmSelect );

m_ui.btnCommit   -> setVisible( m_workMode == dwmEdit );
m_ui.btnRollback -> setVisible( m_workMode == dwmEdit );

QString styleSheet;
if ( m_workMode == dwmSelect )
{
styleSheet = QString("QPushButton { background-image: url(:/images/acSelect_Mode.png) }");
m_ui.btnChangeWorkMode->setStyleSheet( styleSheet );
m_ui.tvData->setSelectionBehavior( QAbstractItemView::SelectRows );
}
else
{
styleSheet = QString("QPushButton { background-image: url(:/images/acEdit_Mode.png) }");
m_ui.btnChangeWorkMode->setStyleSheet( styleSheet );
m_ui.tvData->setSelectionBehavior( QAbstractItemView::SelectItems );
};
}

void CTableDict::colFilter_currentColumnChanged ( const QModelIndex & current, const QModelIndex & previous )
{
Q_UNUSED( previous );

// не даем редактировать нулевую колонку фильтра предназначенную для выравнивания относительно таблицы данных
if ( current.column() == 0 )
m_ui.tvColumnFilter->setEditTriggers( m_disabledEditTriggers );
else
m_ui.tvColumnFilter->setEditTriggers( m_enabledEditTriggers );
}

void CTableDict::data_currentChanged ( const QModelIndex & current, const QModelIndex & previous )
{
Q_UNUSED( previous );

bool bSelectMode = ( m_workMode == dwmSelect );

if ( bSelectMode )
{
m_ui.tvData->setEditTriggers( m_disabledEditTriggers );
m_ui.btnAddRow->setEnabled( false );
m_ui.btnDelRow->setEnabled( false );
return;
};

QModelIndex _current = m_mdlColFilter.mapToSource( current );

QSqlRecord rec = m_mdlData.record( _current.row() );
int row_id = rec.field( ROW_ID_FIELD_NAME ).value().toInt();

bool bReadOnlyRow = ( m_readOnlyRows.indexOf( row_id ) != -1 );

m_ui.btnAddRow->setEnabled( m_bCanAddRow );
m_ui.btnDelRow->setEnabled( m_bCanDelRow && (bReadOnlyRow==false) );

if ( bReadOnlyRow )
{
m_ui.tvData->setEditTriggers( m_disabledEditTriggers );
return;
};

bool bReadOnlyColumn = false;

QString sFieldName = rec.field( _current.column() ).name();
if ( m_columnProps.contains( sFieldName ) )
{
CColumnProps colProps = m_columnProps[ sFieldName ];
bReadOnlyColumn = colProps.bReadOnly;
};

if ( bReadOnlyColumn )
m_ui.tvData->setEditTriggers( m_disabledEditTriggers );
else
m_ui.tvData->setEditTriggers( m_enabledEditTriggers );
}

void CTableDict::data_columnResized( int logicalIndex, int oldSize, int newSize )
{
Q_UNUSED( oldSize );

updateColumnFilter_LeftColumn();

// при изменении ширины колонок таблицы - меняем также ширину колонок фильтра
// logicalIndex + 1 -- т.к. всегда присутствует колонка 0 для выравнивания относительно таблицы данных
m_ui.tvColumnFilter->setColumnWidth( logicalIndex + 1, newSize );
}

void CTableDict::on_tvColumnFilter_cellChanged ( int row, int column )
{
QString exp = m_ui.tvColumnFilter->item( row, column )->data( Qt::DisplayRole ).toString();

m_mdlColFilter.setColumnExp( column-1, exp );
m_mdlColFilter.invalidate();
}

void CTableDict::on_tvData_doubleClicked ( const QModelIndex & index )
{
returnCurrent_RowID();
}

void CTableDict::on_btnRefresh_pressed()
{
refreshData();
}

void CTableDict::on_btnAddRow_pressed()
{
// перед вставкой записи необходимо отминить фильтрацию записей, иначе ее может быть не видно
for ( int i = 0; i < m_ui.tvColumnFilter->columnCount();  ++i )
{
QTableWidgetItem * pItem =  m_ui.tvColumnFilter->item( 0, i );
if ( pItem )
pItem->setData( Qt::DisplayRole, QVariant() );
};
m_mdlColFilter.invalidate();

int row = m_mdlColFilter.rowCount();
m_mdlColFilter.insertRows(row, 1);

// вставка значений по умолчанию в нововставленную запись
QSqlRecord rec = m_mdlData.record();
for ( int i = 0; i < rec.count(); ++i )
{
QString sFieldName = rec.field( i ).name();
// если для этой колонки были заданы параметры
if ( m_columnProps.contains( sFieldName ) )
{
CColumnProps colProps = m_columnProps.value( sFieldName );
m_mdlColFilter.setData( m_mdlColFilter.index(row, i), colProps.vDefaultValue );
};
};

m_ui.tvData->setFocus();
m_ui.tvData->setCurrentIndex( m_mdlColFilter.index(row, m_nFirstVisibleColumn) );
}

void CTableDict::on_btnDelRow_pressed()
{
int row = m_ui.tvData->selectionModel()->currentIndex().row();
m_mdlColFilter.removeRow( row );
m_mdlColFilter.invalidate();

m_ui.tvData->setFocus();
m_ui.tvData->setCurrentIndex( m_mdlColFilter.index(row-1, m_nFirstVisibleColumn) );
}

void CTableDict::on_btnSelect_pressed()
{
returnCurrent_RowID();
}

void CTableDict::on_btnCancel_pressed()
{
done( 0);
}

void CTableDict::on_btnCommit_pressed()
{
m_mdlData.submitAll();
refreshData();
}

void CTableDict::on_btnRollback_pressed()
{
m_mdlData.revertAll();
refreshData();
}

void CTableDict::on_btnChangeWorkMode_pressed()
{
if ( ( m_workMode == dwmSelect ) && ( m_bEditMode_Allowed ) )
setWorkMode( dwmEdit );
else if ( ( m_workMode == dwmEdit ) && ( m_bSelectMode_Allowed ) )
setWorkMode( dwmSelect );

refreshData();
}

void CTableDict::keyPressEvent(QKeyEvent * event)
{
// для убирания закрытия по клавишам Enter/Esc
if ( ( event->key() == Qt::Key_Escape ) ||
( event->key() == Qt::Key_Return ) )
return;

QDialog::keyPressEvent( event );
}

void CTableDict::showEvent ( QShowEvent * event )
{
QDialog::showEvent( event );

setFocusProxy( m_ui.tvData );
m_ui.tvData->setFocus();

updateColumnFilter_LeftColumn();
}

QString CTableDict::dwmToDispString( DictWorkMode mode )
{
static const QString dwmDispStrings[ dwmCount ] =
{
QString::fromUtf8( "Режим Выбора" ),
QString::fromUtf8( "Режим Редактирования" )
};
return dwmDispStrings[ mode ];
}

void CTableDict::updateColumnFilter_LeftColumn()
{
// если для таблицы данных виден вертикальный заголовок (с номерами строк), то делаем в таблице фильтра
// нулевую колонку соответствующую по ширине этому заголовку
if ( m_ui.tvData->verticalHeader()->isVisible() )
m_ui.tvColumnFilter->setColumnWidth( 0, m_ui.tvData->verticalHeader()->width() );
else
m_ui.tvColumnFilter->setColumnWidth( 0, 0 );
}

void CTableDict::returnCurrent_RowID()
{
if ( m_workMode != dwmSelect ) return;

if ( !m_mdlData.rowCount() )
{
done( 0 );
return;
};

QSqlRecord rec = m_mdlData.record( m_ui.tvData->currentIndex().row() );
QSqlField  field = rec.field( ROW_ID_FIELD_NAME );

int row_id = field.value().toInt();
done( row_id );
}
Записан
mishabard
Гость
« Ответ #5 : Июль 07, 2011, 15:37 »

Здравствуйте break! У меня примерно такая же задача появилась.
Вы решили это проблему? Если да то как?
Записан
RVZ
Гость
« Ответ #6 : Июль 13, 2011, 11:00 »

Вопроса я не понял но как ответ может подойдет.
Предлагаю добавить еще одну таблицу с рейсам и процедуру создания рейса
Допустим

Водители                  Маршруты           Рейсы
 --------------         -------------       -------------------------
| v_id  | Name |       | m_id | Name|     | r_id | r_No| v_id | m_id |
 --------------         -------------       -------------------------

Тогда при отработке конструктора формы оформления рейса (Где r_No уникальный регистрационный номер рейса - который в свою очередь может быть не уникальным в таблице рейсов то можно отправлять хоть по десеть водителей по одному маршруту одним приказом и на нескольких Улыбающийся или на одной машине) создается запись в таблице рейсов(по средствам хранимой процедуры) тогда можно держать информацию о том какие водители по каким маршрутам ездили при добавлении таблицы Транспортные средства(и столбца с их id к рейсам) еще и на каких машинах и так далее развивая реляционную структуру БД
Записан
break
Гипер активный житель
*****
Offline Offline

Сообщений: 846


Просмотр профиля
« Ответ #7 : Июль 16, 2011, 21:18 »

mishabard - проблему пока не решил, надобность в ее быстром решении отпала. Думаю самый верный вариант написания правильных классов для работы с БД. То есть написание такого Query (модели), которому можно задать selectSQL, updateSQL, deleteSQL и т.д. Чтобы была возможность через этот Query работать с несколькими таблицами, как будто это один набор данных (так увидит это пользователь). Как например это делается в аналогичных классах для Delphi - FIBc.

RVZ - вы задачу не поняли совсем, вы предлагаете связь многие ко многим - в моем случае она не нужна.
Есть водители и есть поездки. В моей задаче с каждой поездкой может быть связан один и только один водитель. Но у этого водителя куча дополнительных параметров в своей таблице. Вот и стоит вопрос как вытягивать их все, а не одно какое-то поле для представления в TableView. При этом не модифицируя метанные лишними дублирующимися полями.
Записан
Странник
Гость
« Ответ #8 : Июль 17, 2011, 14:42 »

можно унаследоваться от QSqlQueryModel. в ней реализовано read-only представление результатов произвольного sql-запроса, остается дописать поддержку редактирования. есть правда мнение, что в случае с несколькими связанными таблицами все равно получится нечто неудобоваримое и непрозрачное в использовании.
Записан
break
Гипер активный житель
*****
Offline Offline

Сообщений: 846


Просмотр профиля
« Ответ #9 : Июль 18, 2011, 00:27 »

Цитировать
есть правда мнение, что в случае с несколькими связанными таблицами все равно получится нечто неудобоваримое и непрозрачное в использовании
Практика использования FIBDataset в свое время для меня доказала обратное. Простой пример - хочу вывести ту же самую таблицу поездок. В поездке имею ID водителя, ID траспорта, вывожу таблицу

дата поездки | фамилия водителя | имя водителя | возраст водителя | номер транспортного средства | фото транспортного средства |

то есть есть примерно такой запрос

Код:
select t.*, d.*, tr.*
from TRAVELS t, DRIVERS d, TRANSPORT tr
where ( t.DRIVER = d.ROW_ID ) and ( t.TRANSPORT_ID = tr.ROW_ID )

по сути простой JOIN

но для удаления поездки, я хочу иметь запрос

Код:
delete t.*
from TRAVELS t
where t.ROW_ID = ?ROW_ID

т.е. хочу удалять только поездку а не транспорт и водителей (что вполне логично)

а для изменения поездок, хочу иметь такой запрос

Код:
update TRAVELS(DATE_TIME, DRIVER_ID, TRANSPORT_ID)
set
DATE_TIME = ?NEW_DATE_TIME,
DRIVER_ID = ?NEW_DRIVER_ID,
TRANSPORT_ID = ?NEW_TRANSPORT_ID


Там так и работало.
Записан
Страниц: [1]   Вверх
  Печать  
 
Перейти в:  


Страница сгенерирована за 0.089 секунд. Запросов: 23.