crossplatform.ru

Здравствуйте, гость ( Вход | Регистрация )

2 страниц V  < 1 2  
Ответить в данную темуНачать новую тему
> просмотр таблицы с меняющимися данными, запоминание и выделение тек строки
Tonal
  опции профиля:
сообщение 9.2.2012, 9:45
Сообщение #11


Активный участник
***

Группа: Участник
Сообщений: 452
Регистрация: 6.12.2007
Из: Новосибирск
Пользователь №: 34

Спасибо сказали: 69 раз(а)




Репутация:   17  


Про Т2 и прзиционирование:
Что-то я не очень понял, если для Т2 используется та же структура, то почему не использовать ID для позиционирования.
В противном случае не ясно что делать при наличии 2х и более одинаковых (или очень близких) значений PARAM в массиве.
Как-то ты же должна задавать на этом массиве порядок/сортировку для выборки.
Иначе можно получить данные совершенно не в том порядке и позиционирование будет бессмысленно, потому что в "уже просмотренные" попадут совсем ни те данные.
Если порядок таки задан - то и запоминать позицию нужно ориентируясь на него.

Пример:
Цитата
Поступает 1-й раз массив со значениями PARAM (1.23, 2.34, 300.45, 445.00),
оператор начинает его просмотр,
делает текущей запись со значением 300.45,

Куда позиционироватся, если следующий запрос вернёт:
PARAM (445.00, 2.34, 300.45, 1.23)
или
PARAM (1.23, 2.34, 300.45, 300.45, 300.45, 300.45, 445.00)
или
PARAM (3.38, 1.32, 300.45001, 455.01, 300.44999)
И что это будет значить для оператора?

Про способы использования:
1. Я имел в виду, что ты всё оставляешь как сейчас, но при генерации текста запроса вместо данных подставляешь заполнители (например символы "?").
После этого биндишь переменные (как в справке по QSqlQuery)
В этом случае данные в сервер попадают минуя преображение в строку у тебя в коде и обратно на сервере.
Меньше преобразований - меньше накапливается ошибок. :)
Причём объект QSqlQuery после prepare можно не удалять, а сохранить где-то, и для следующего запроса только подставить (забиндить) новые параметры.
В случае Т3 можно сделать кеширование. Например QMap где ключом будет состав изменяемых полей а значением - препарированный запрос.

2. В триггере перед изменением можно проверить - если поле опциональное, восстановить предыдущее значение, например:
if (new.param is null or new.param = null_double) then
new.param = old.param;


3. Всё то же самое - определение состава обновляемых полей и выбор варианта запроса. Или его можно собрать в процедуре и выполнить через execute statiment.
Практически тот же вариант 1, но перенесённый на сервер. :)

Кстати, что делается в случае если param == null_double для записи с новым ID?

Ну и я бы стал делать вариант 2 и 3 только в том случае, если данные в базу могут поступать из разных приложений. В противном случае это дополнительное усложнение схемы данных.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Steklova Olga
  опции профиля:
сообщение 9.2.2012, 21:04
Сообщение #12


Участник
**

Группа: Участник
Сообщений: 198
Регистрация: 27.9.2011
Из: Санкт-Петербург
Пользователь №: 2912

Спасибо сказали: 5 раз(а)




Репутация:   4  


Привет! :) Пишу на этот раз сразу несколько сообщений.
09.02.2012 часть 1
уточнения про Т2

1)
Цитата(Tonal @ 9.2.2012, 9:45) *
не ясно что делать при наличии 2х и более одинаковых (или очень близких) значений PARAM в массиве
У меня для Т2 предполагается, что в одном и том же массиве не могут приходить одинаковые значения.
Близкими значения могут быть, но при этом они все равно будут разными.

2)
Цитата(Tonal @ 9.2.2012, 9:45) *
Как-то ты же должна задавать на этом массиве порядок/сортировку для выборки.
Сортировка Т2 установлена всегда по возрастанию значения PARAM.

3)
Цитата(Tonal @ 9.2.2012, 9:45) *
Цитата
Поступает 1-й раз массив со значениями PARAM (1.23, 2.34, 300.45, 445.00),
оператор начинает его просмотр,
делает текущей запись со значением 300.45,

Куда позиционироватся, если следующий запрос вернёт:
PARAM (445.00, 2.34, 300.45, 1.23)
- на 300.45, так как массив с моей точки зрения не изменился, ведь он всегда сортируется по возрастанию,

Цитата(Tonal @ 9.2.2012, 9:45) *
или
PARAM (1.23, 2.34, 300.45, 300.45, 300.45, 300.45, 445.00)
- такой массив не может поступить,

Цитата(Tonal @ 9.2.2012, 9:45) *
или
PARAM (3.38, 1.32, 300.45001, 455.01, 300.44999)
- на 1.32 (на min значение), так как значение 300.45 с моей точки зрения изменилось.

09.02.2012 часть 2
-----------------------------------------------------------------------------
уточненная постановка задачи !!!

4) Пока разбиралась с программой, уточнила постановку задачи.
Она оказалась проще, чем я рассчитывала. Поняла ее так.


Есть цикл работы, например, 5 сек. Он может измениться.

Есть "анализатор" и несколько "устройств". Со всеми устройствами анализатор работает одинаково.
Анализатор (с каким-то своим внутренним циклом) собирает данные от устройства,
на основе этих данных рассчитывает массив значений параметра PARAM этого устройства
и посылает этот массив мне (не чаще раза в цикл работы 5 сек) для сохранения в БД.
(Поэтому, в Т2 у меня есть еще поле с номером устройства TO_OBJ_ID, внешний ключ.)

Передача массива для устройства в каждом рабочем цикле не является обязательной.
Если анализатор насчитал новые значения параметра для устройства, то он передает массив.
Если массив для устройства "устаканился" (определился с нужной точностью, а данные от устройства
в последнее время резко не менялись), то анализатор приостанавливает для этого устройства
передачу массива в БД.

Получается, что массив для Т2 будет поступать ко мне только при его изменении.
И я была не права, когда сказала, что
Цитата(Steklova Olga @ 3.2.2012, 21:20) *
Данные разных массивов:
- могут полностью совпадать,

И тогда логичнее, как мне кажется, был бы следующий вариант А.

А) Вариант для случая, когда массив будет поступать ко мне только при его изменении.
При поступлении очередного массива
- удалить старый массив, добавить новый массив,
сделать текущей первую запись.
(Не "отлавливать блох", проверяя, осталось ли в новом массиве значение PARAM, бывшее текущим.
Тогда и оператор сразу увидит, что массив изменился, а не будет пребывать в неведении.)


Цитата(Tonal @ 9.2.2012, 9:45) *
Что-то я не очень понял, если для Т2 используется та же структура, то почему не использовать ID для позиционирования.

- Когда я раньше, при поступлении очередного массива проверяла, какая запись была до этого текущей,
я не могла проверять ID, так как я удаляю старый массив вместе со всеми этими ID и добавляю новый.
Ведь ID - автоинкрементное поле.
- Теперь, в варианте А, в соотв. с новым пониманием постановки задачи, можно будет для позиционирования
использовать ID вместо PARAM.
ID - автоинкрементное поле.
- В Т1 можно позиционироваться по ID, так как там записи не удаляются.

Б) Вариант для случая, когда массив будет поступать ко мне каждый цикл, даже если массив не изменился.
Этот вариант для моей программы, получается, не нужен, но остается интерен мне теоретически, на будущее.
При поступлении очередного массива
- отсортировать новый массив по возрастанию PARAM (?),
- сравнить новый массив со старым (?),
- если они абсолютно совпадают, то оставить все как было,
- если массивы хоть немного отличаются, то удалить старый массив, добавить новый массив,
сделать текущей первую запись.
Здесь тоже можно будет для позиционирования использовать ID вместо PARAM.
ID - автоинкрементное поле.
-----------------------------------------------------------------------------

Сообщение отредактировал Steklova Olga - 10.2.2012, 10:15
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Steklova Olga
  опции профиля:
сообщение 10.2.2012, 10:14
Сообщение #13


Участник
**

Группа: Участник
Сообщений: 198
Регистрация: 27.9.2011
Из: Санкт-Петербург
Пользователь №: 2912

Спасибо сказали: 5 раз(а)




Репутация:   4  


09.02.2012 часть 3
5)
Цитата(Tonal @ 9.2.2012, 9:45) *
Кстати, что делается в случае если param == null_double для записи с новым ID?
Если для записи в Т3 поступит структура
с id, которого еще не было в таблице, и с param == null_double, то в Т3 будет добавлена запись.
В INSERT я укажу поле ID, равное id, и не укажу поле PARAM.
Неуказанное поле, насколько я понимаю, по умолчанию окажется == NULL.

6)
Цитата(Tonal @ 9.2.2012, 9:45) *
Про способы использования:
1. Я имел в виду, что ты всё оставляешь как сейчас, но при генерации текста запроса вместо данных подставляешь заполнители (например символы "?").
После этого биндишь переменные (как в справке по QSqlQuery)
В этом случае данные в сервер попадают минуя преображение в строку у тебя в коде и обратно на сервере.
Меньше преобразований - меньше накапливается ошибок.
Причём объект QSqlQuery после prepare можно не удалять, а сохранить где-то, и для следующего запроса только подставить (забиндить) новые параметры.
Это ясно (для Т1).

7)
уточняющий вопрос про prepare и addBindValue

Поля таблицы T2:
ID INTEGER NOT NULL (PK, автоинкрементное поле), N записи,
TO_OBJ_ID INTEGER NOT NULL (FK), N устройства,
PARAM DOUBLE PRECISION NOT NULL, значение параметра.
Таблица Т2 может содержать [0..200] значений PARAM для каждого TO_OBJ_ID.

А для обработки поступившего массива со значениями PARAM для одного конкретного TO_OBJ_ID
вариант а) лучше, чем б) и в)? (TO_OBJ_ID здесь одно и то же, PARAM - разные)
typedef struct {
    int obj_id;
    QVector<double> param;
} tobj;
tobj* obj;
...
int param_size = obj->param.size();
if (param_size > 200)
    param_size = 200;

а)
query.prepare("INSERT INTO T2 (TO_OBJ_ID, PARAM) VALUES (?, ?)");
for (int i = 0; i < param_size; i++) {
    query.addBindValue(obj->obj_id);
    query.addBindValue(obj->param[i]);
    query.exec();
}
б)
query.prepare(QString("INSERT INTO T2 (TO_OBJ_ID, PARAM) VALUES (%1, ?)").arg(obj->obj_id));
for (int i = 0; i < param_size; i++) {
    query.addBindValue(obj->param[i]);
    query.exec();
}
в)
query.prepare("INSERT INTO T2 (TO_OBJ_ID, PARAM) VALUES (" + QString::number(obj->obj_id) + ", ?)");
for (int i = 0; i < param_size; i++) {
    query.addBindValue(obj->param[i]);
    query.exec();
}

8 )
Цитата(Tonal @ 9.2.2012, 9:45) *
В случае Т3 можно сделать кеширование.
Например QMap где ключом будет состав изменяемых полей а значением - препарированный запрос.
Вы предлагаете использовать QMap<K, T>, структуру данных, которая содержит пары ключ-значение,
упрядоченные по возрастанию ключей?
Я понимаю так, что это будет
QMap<QString, QVariant> map;
где QString - наименование поля таблицы, QVariant - значения поля таблицы.
Вставляем в map только поступившие в Rec значения полей и их наименования,
для чего для каждого поля проверяем, равно ли его значение константе null_... для соотв. типа поля.
Из полученного списка keys получаем часть строки запроса с названиями полей,
дополняем строку соотв. кол-вом символов '?',
потом в цикле делаем addBindValue, занося в запрос значения полей из полученного списка values.

9) То, что Вы написали нужно проверять в триггере, понятно. Только я пока не создавала таких триггеров в базе, а делала проверки в тексте Qt.

10) С хранимками еще совсем не разбиралась. :unknw:

11)
Цитата(Tonal @ 9.2.2012, 9:45) *
Ну и я бы стал делать вариант 2 (с триггерами) и 3 (с хранимками) только в том случае,
если данные в базу могут поступать из разных приложений.
В противном случае это дополнительное усложнение схемы данных.
Уф, успокоили, что это необязательно для моего случая.
Мне еще не доводилось писать базы, данные в которые прямо записываются из разных приложений.
Сама из Qt-программы пишу данные в БД, выдаю результаты запросов к БД другим людям, модули которых
вместе с моими находятся в одном приложении.

Уважаю за терпение дочитавших до конца! Простите студента за многословие. :)

Сообщение отредактировал Steklova Olga - 10.2.2012, 10:16
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Tonal
  опции профиля:
сообщение 10.2.2012, 12:00
Сообщение #14


Активный участник
***

Группа: Участник
Сообщений: 452
Регистрация: 6.12.2007
Из: Новосибирск
Пользователь №: 34

Спасибо сказали: 69 раз(а)




Репутация:   17  


Т. е. с позиционированием ты разобралась. :)
Могу только добавить, что раз есть порядок, то можно позиционироваться на первый элемент больший или равный текущему или на последний меньший или равный, на выбор. :)

5. Неуказанное в insert-е поле заполняется значением по умолчанию для этого поля, которое можно указать при создании таблицы (или при создании домена). Если значение по умолчанию не указано, подставляется null.

6. Тут возможны варианты с транзакциями: сохраняется ли препарированность между транзакциями или для новой транзакции нужно препарить запросы по новой?
Если мне склероз не изменяет, Firebird в последних версиях не требовал переподготавливать запросы каждый раз, но не знаю как к этому относится драйвер в Qt.

7. Есть ещё вариант а' - использовать биндинг по имени или номеру. Тогда obj_id можно забиндить 1 раз перед циклом.
Я бы выбрал его. :)

8. Ну, если совсем втупую то примерно так (псевдокод на псевдопитоне):
# Где-то объявлен кеш запросов. Для С++ - QMap<QList<QString>, QSqlQuery>;
queryCach = {}

# Функция для сохранения записи в базу. Для C++ void updateRec(const tRec& rec);
def updateRec(rec):
  # Ключь состоит из списка имён ненулевых полей. Для С++ - QList<QString>
  keys = []
  # Список значений. Для С++ - QList<QVariant>
  vals = []
  if rec.param != null_double:
    keys.append('param')
    vals.append(rec.param)
  if rec.fint1 != null_qint32:
    keys.append('fint1')
    vals.append(rec.fint1)
  ... # то же самое для остальных полей

  # Вытаскиваем запрос по ключу
  query = queryCach.get(keys)
  # Если запроса нет в кеше - создаём и запоминаем:
  if query is None:
    # Каждое имя поле из текущего набора keys подставляем в строчку '%s = ?'
    # и сцепляем эти строчки через запятую
    sql_fld = ', '.join('%s = ?' % fld for fld in keys)
    # Подставляем строку с полями в шаблон запроса
    sql = 'update %s set %s where id = ?' % (table_name, sql_fld)
    query = QSqlQuery()
    query.prepare(sql)
    queryCach[keys] = query

  # Биндим значения и выполняем
  for val in vals:
    query.addBindValue(val)
  query.addBindValue(rec.id)
  query.exec()

Хотя можно вместо списка с QVariant использовать словарь и биндить по именам. :)
Главная идея, что сам запрос для конкретной конфигурации полей ты создаёшь только если она появилась и лишь один раз.
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение
Steklova Olga
  опции профиля:
сообщение 10.2.2012, 17:01
Сообщение #15


Участник
**

Группа: Участник
Сообщений: 198
Регистрация: 27.9.2011
Из: Санкт-Петербург
Пользователь №: 2912

Спасибо сказали: 5 раз(а)




Репутация:   4  


Привет! :)
6.
Цитата
сохраняется ли препарированность запроса между транзакциями (Firebird, Qt)
я тоже не знаю.

7.
Цитата
Есть ещё вариант а' - использовать биндинг по имени или номеру. Тогда obj_id можно забиндить 1 раз перед циклом. Я бы выбрал его.

То, что надо! :) Нашла тут примеры разных вариантов для prepare и bindValue.
//а1) Named binding using named placeholders:
query.prepare("INSERT INTO T2 (TO_OBJ_ID, PARAM) VALUES (:TO_OBJ_ID, :PARAM)");
query.bindValue(":TO_OBJ_ID", obj->obj_id);
for (int i = 0; i < param_size; i++) {
    query.bindValue(":PARAM", obj->param[i]);
    query.exec();
}
//а2) Positional binding using named placeholders:
query.prepare("INSERT INTO T2 (TO_OBJ_ID, PARAM) VALUES (:TO_OBJ_ID, :PARAM)");
query.bindValue(0, obj->obj_id);
for (int i = 0; i < param_size; i++) {
    query.bindValue(1, obj->param[i]);
    query.exec();
}
//а3) Binding values using positional placeholders:
query.prepare("INSERT INTO T2 (TO_OBJ_ID, PARAM) VALUES (?, ?)");
query.bindValue(0, obj->obj_id);
for (int i = 0; i < param_size; i++) {
    query.bindValue(1, obj->param[i]);
    query.exec();
}

//Заодно поняла, как забиндить значения NULL в поля разного типа. :)
query.prepare("UPDATE OR INSERT INTO T3 (ID, PARAM, FINT1, FSTR) "
              "VALUES (:ID, :PARAM, :FINT1, :FSTR) "
              "MATCHING (ID)");
query.bindValue(":ID", 1);
query.bindValue(":PARAM", QVariant(QVariant::Double)); //запишется NULL
query.bindValue(":FINT1", QVariant(QVariant::Int)); //запишется NULL
query.bindValue(":FSTR", QVariant(QVariant::String)); //запишется NULL
//
//query.bindValue(":PARAM", NULL); //запишется 0.0
//query.bindValue(":FINT1", NULL); //запишется 0
//query.bindValue(":FSTR", NULL); //запишется "0"
//
//query.bindValue(":FSTR", ""); //запишется ""
//
query.exec();

8. Спасибо, Tonal, за пример. :) Еще разбираюсь с ним...
Перейти в начало страницы
 
Быстрая цитата+Цитировать сообщение

2 страниц V  < 1 2
Быстрый ответОтветить в данную темуНачать новую тему
Теги
Нет тегов для показа


1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0




RSS Текстовая версия Сейчас: 25.11.2024, 23:21