[РЕШЕНО] Трудности при обновлении (update) объекта RowSet

Автор ost, 26 марта 2017, 17:15

0 Пользователи и 1 гость просматривают эту тему.

ost

Доброго.

Есть база данных SQLite, содержащая таблицу со многими полями среди значений которых встречаются пустые строки (''). Начитался, что это плохо и вместо них правильнее использовать NULL.

База доступна в LibreOffice Calc через ODBC.
Для решения задачи по замене всех пустых строк ('') на NULL написал такой макрос (здесь он упрощен за счет удаления цикла по перебору всех полей таблицы) на StarBasic, который не работает:

Sub Service3

RowSet = createUnoService("com.sun.star.sdb.RowSet") ' создаем
RowSet.DataSourceName = "SQLite_LO_test" ' указываем зарегистрированную базу данных, без *.odb!
RowSet.CommandType = com.sun.star.sdb.CommandType.COMMAND ' вызываем команду
RowSet.EscapeProcessing=FALSE

RowSet.Command = "SELECT uSumZ_VzOmsFF FROM U_tmp WHERE uSumZ_VzOmsFF=''"
RowSet.execute() ' выполняем

rowSet.Last 'мотнули курсор к концу
RowEND=(rowSet.RowCount())
rowSet.First ' вернулись к первой записи

If RowEND>0 Then
While RowSet.next() 'перебираем результат sql-запроса
RowSet.UpdateInt("uSumZ_VzOmsFF", NULL)
Wend
RowSet.Update()
End If

End Sub


Ругается на несовместимые типы в ходе выполнения строки внутри While -Wend . Понимаю, что "RowSet.UpdateInt", в данном случае, глупость, но как сделать правильно не знаю. Научите, пожалуйста.
Еще просьба. Поделитесь, пожалуйста, мнением о правильности/неправильности использования "пустой строки"/NULL для обозначения отсутствующих значений.

rami

#1
Цитата: ost от 26 марта 2017, 15:15Ругается на несовместимые типы в ходе выполнения строки внутри While -Wend . Понимаю, что "RowSet.UpdateInt", в данном случае, глупость, но как сделать правильно не знаю. Научите, пожалуйста.
Правильно ругается. В строке кода RowSet.UpdateInt("uSumZ_VzOmsFF", NULL) оба параметра должны быть числовые:
1. индекс столбца, а не его имя "uSumZ_VzOmsFF"
2. число, а не NULL

Попробуйте заменить RowSet.UpdateInt("uSumZ_VzOmsFF", NULL) на RowSet.updateNull(НомерСтолбца)

Я не совсем понимаю, зачем вы это делаете ??? Отсутствующие значения всегда NULL.

Используйте тестовый файл.

economist

Немного off про Null. Попробую от него отговорить в SQLite. И почему пустые строки это плохо?
SQLite вообще все хранит как текст, и Null вместо "" - тотально делать нет смысла.

Null - это полная неизвестность. "" - это известная пустота. Если рассуждать на примере ИНН физлица - то Null - это неизвестный ИНН (хотя с рождения он есть у всех), а ИНН="" - просто незаполненный. Смысл этой фразы в том, что Null - это большая пустота, чем "".

Настоящие проблемы возникнут при объединении таблиц с Null, ведь:

1) NULL не равно ни логическому значению FALSE, ни пустой строке "", ни 0 (нулю).
2) При сравнении NULL с любым значением будет получен результат NULL, а не FALSE и не 0.
3) NULL не равно NULL, а равно пустой строке "".

То есть вместо ="" и <>"" - нужно будет обрабатывать втрое больше сущностей.
Поэтому уж лучше везде пустые строки, чем Null. Они, эти Null, все равно будут попадаться в работе.
Например, во всех внешних соединениях таблиц - будут "дыры" (то есть поля набора данных, в которых нет соответствующей записи), и они заполнятся значением NULL. Это то же самое, что значение #Н/Д в функции =ВПР()/=VLOOKUP в Excel/Calc.

А еще если в поле есть хоть один Null - индекс по полю в SELECT-запросах использоваться не будет что означает 4-х кратное удлинение времени их выполнения.

И традиционный, от меня, камушек в огород ППО от MS. Многие не знают, а когда узнают - то сильно удивляются тому что движки Access 2000 и Access 2003 (JET/ACE) - по-разному отрабатывают Null, хотя расширение файла базы данных ОДИНАКОВО - *.mdb.

То есть один и то же запрос на разных компьютерах (в разных ОС) - может выполняться с одним и тем же файлом по-разному! Это апофеоз интероперабельности от Microsoft, который лично мне стоил нескольких суток нервотрепки.
Руб. за сто, что Питоньяк
Любит водку и коньяк!
Потому что мне, без оных, -
Не понять его никак...

ost

rami,
Спасибо. Пустые строки вместо NULL образовались в ходе импорта таблиц LO Calc в базу SQLite.

economist,
Благодарю за образность. Разница между NULL и пустой строкой теперь зрима. В этой связи подумаю, что оставить в базе.

Между тем макрос

Sub Service3

RowSet = createUnoService("com.sun.star.sdb.RowSet") ' создаем
RowSet.DataSourceName = "SQLite_LO_test" ' указываем зарегистрированную базу данных, без *.odb!
RowSet.CommandType = com.sun.star.sdb.CommandType.COMMAND ' вызываем команду
RowSet.EscapeProcessing=FALSE

RowSet.Command = "SELECT uSumZ_VzOmsFF FROM U_tmp WHERE uSumZ_VzOmsFF=''"
RowSet.execute() ' выполняем

rowSet.Last 'мотнули курсор к концу
RowEND=(rowSet.RowCount())
rowSet.First ' вернулись к первой записи

If RowEND>0 Then
While RowSet.next() 'перебираем результат sql-запроса
RowSet.UpdateNull(1)
Wend
RowSet.Update()
End If

End Sub

во время исполнения выдает ошибку на строке "RowSet.Update()" =(
Что же я делаю не так?

rami

Цитата: ost от 27 марта 2017, 13:39во время исполнения выдает ошибку на строке "RowSet.Update()" =(
Что же я делаю не так?
Нужно указать что обновляем, попробуйте заменить на RowSet.updateRow()

ost

Получаю следующее сообщение:

Ошибка времени выполнения BASIC.
Вызвано исключение
Type: com.sun.star.sdbc.SQLException
Message: Ошибка функциональной последовательности..


ost

Макрос отрабатывает без ошибок, но данные в базе не обновляются. =(

rami

Цитата: ost от 27 марта 2017, 16:37
Макрос отрабатывает без ошибок, но данные в базе не обновляются. =(
Какие данные должны обновиться? Сферические кони в вакууме ??? Или хотите вместо пустой ячейки увидеть в ней надпись NULL ???

ost

В точности так. Только не кони и не в вакууме, а вместо пустой строки надпись NULL. Именно ее показывает менеджер SQLiteStudio, если "видит" NULL в базе.

rami

А если закрыть базу с сохранением и снова открыть? (Жаль, что я не могу сам всё проверить)

ost

#11
Пробовал. Не помогает.
Возможно, как то поспособствует sql-предложение, формирующее тестовую базу (файл tmp.sql) или, собственно, база (файл test.sqlite3)

Макрос, который призван заменить все пустые строки на NULL в поле "oper1"


Sub Service3

RowSet = createUnoService("com.sun.star.sdb.RowSet") ' создаем
RowSet.DataSourceName = "SQLite_LO_test" ' указываем зарегистрированную базу данных, без *.odb!
RowSet.CommandType = com.sun.star.sdb.CommandType.COMMAND ' вызываем команду
RowSet.EscapeProcessing=FALSE

RowSet.Command = "SELECT oper1 FROM temp_table WHERE oper1=''"
RowSet.execute() ' выполняем

rowSet.Last 'мотнули курсор к концу
RowEND=(rowSet.RowCount())
rowSet.First ' вернулись к первой записи

If RowEND>0 Then
While RowSet.next() 'перебираем результат sql-запроса
RowSet.UpdateNull(1)
Wend
RowSet.RefreshRow()
End If

End Sub


Базу SQLite_LO_test.odb надо, конечно, создать, зарегистрировать ее в LibreOffice и прицепить к ней через odbc-драйвер (прямая ссылка на драйвер для win64 http://www.ch-werner.de/sqliteodbc/sqliteodbc_w64.exe) базу test.sqlite3.

Прошу прощения за сумбур.

UPD: а форум не дает прицеплять ни файлы *.sql, ни файлы *.sqlite3 =(
UPD: Выложил сюда https://yadi.sk/d/mvBk63kx3H4oVs архив 7zip с двумя этими файлами.


rami


ost

Жаль. =) Спасибо за время. Буду потихоньку биться головой в интернеты. Глядишь, чего-нибудь накопаю.

economist

#14
А если сразу выполнить:

UPDATE U_tmp SET uSumZ_VzOmsFF=null WHERE uSumZ_VzOmsFF=''


Объекты RowSet/рекордсэты полезны, если нужно курсором ходить туда-сюда (т.е. seek-ать). Здесь же задача просто за-Null-ить всё пустое?   
Руб. за сто, что Питоньяк
Любит водку и коньяк!
Потому что мне, без оных, -
Не понять его никак...