Форум: "Базы";
Текущий архив: 2004.05.02;
Скачать: [xml.tar.bz2];
ВнизПолтергейсты SQL Server или всё "намана"? Найти похожие ветки
← →
31512 © (2004-03-26 09:10) [0]Дело было так ... Моя мечта попробовать создать трёхуровневое приложение для базы данных (SQL Server 2000), с некоторых пор начала обретать физическую форму. Для создания самоей БД на сервере я сначала использовал Access. Ну, чего проще? Сляпал БД, вызвыл "Мастер преобразования в формат SQL Server" и ... собственно больше ничего не требуется. Но потом мне надоело каждый раз лезть в Access, когда возникала необходимость создать БД заново. Тогда я написал SQL-скрипт, как говорится, "в раз и навсегда", внося по мере необходимости в него ихменения. А заставить SQL серевер выполнить SQL-скрипт не так уж сложно, да и быстрее это раз в 30.
Но вот какая фигня стала происходить. В случае создания БД Accessом, достаточно было выставить нужные флаги у DataSetProviderов для серверного приложения, чтобы в клиентском приложении при вызове метода TClientDataSet.ApplyUpdates(ErrorCount : integer) все изменения аккуратно принимались, в том числе происходили и корректные изменения в самой БД. При этом никаких дополнительных действий принимать не требуется, т.е. используются самые стандартные возможности DBшных компонентов. Однако, если БД создана посредством скипта, то ничего подобного не наблюдается. Вставить новую запись - без проблем, но на удаление и изменение получаю ошибку "Запись была изменена или занята другим пользователем" сообщением "Не удалось найти запись". И только создание бизнес-правила в методе TRemoteDataModule.DataSetProviderBeforeUpdateRecord
позволяет выполнять всё корректно. Почему же возникают, как говорят в Одессе, такие "две большие разницы"?
У меня: D7, SQL Server 2000, WinXP SP1 RUS, Office XP(with FrontPage), ADO (MDAC 2.7), MIDAS.
← →
Nikolay M. © (2004-03-26 09:21) [1]
> В случае создания БД Accessом, достаточно было выставить
> нужные флаги у DataSetProviderов для серверного приложения,
> чтобы в клиентском приложении при вызове метода TClientDataSet.ApplyUpdates(ErrorCount
> : integer) все изменения аккуратно принимались
Возможно, необходимые флаги у полей сбрасываются (не выставляются)?
← →
31512 © (2004-03-26 09:32) [2]Для Nikolay M. Очень даже может бытьююю. И один из таких флагов ... в Делфи TField.ProviderFlags.pfInKey - идентефикация с помощью первичного ключа БД ....
А SQL скриптом как флаги у полей выставлять?
← →
31512 © (2004-03-26 09:35) [3]Вот, кстати, кусок скрипта, создание одной 2х таблиц
create table [ORGANISATION](
ID_ORGANISATION int identity(1, 1) not null primary key,
ORGANISATION_NAME nvarchar (255) collate Cyrillic_General_CI_AS not null default "Нет данных",
ORGANISATION_ADRESS nvarchar (255) collate Cyrillic_General_CI_AS not null default "Нет данных",
ORGANISATION_CODE nvarchar (255) collate Cyrillic_General_CI_AS not null default "Нет данных",
DBDATE datetime not null default CURRENT_TIMESTAMP,
DELETED bit not null default 0
)
create table [EMPLOYEE](
ID_EMPLOYEE int identity(1, 1) not null primary key,
ID_ORGANISATION int not null foreign key references ENERGO_DB.[dbo].[ORGANISATION](ID_ORGANISATION) on delete cascade,
EMPLOYEE_NAME nvarchar (255) collate Cyrillic_General_CI_AS not null default "Нет данных",
EMPLOYEE_POST nvarchar (255) collate Cyrillic_General_CI_AS not null default "Нет данных",
EMPLOYEE_PHONE nvarchar (255) collate Cyrillic_General_CI_AS not null default "Нет данных",
DBDATE datetime not null default CURRENT_TIMESTAMP,
DELETED bit not null default 0
)
← →
Nikolay M. © (2004-03-26 09:38) [4]
> TField.ProviderFlags.pfInKey - идентефикация с помощью первичного
> ключа БД
Не совсем понял твое понимание этого флага, надеюсь, оно правильное. Так же, как и понимание свойства TDataSetProvider.UpdateMode.
> А SQL скриптом как флаги у полей выставлять?
Не, давай мухи отдельно, котлеты отдельно. При чем тут скрипт, который выполняется на сервере и флаги, которые выставляются на клиенте?
← →
bushmen © (2004-03-26 09:39) [5]А зачем ты timestamp кидаешь в поле datetime? Есть же специальный тип в SQL - timestamp
← →
31512 © (2004-03-26 09:49) [6]Для bushmen
CURRENT_TIMESTAMP - Returns the current date and time. This function is equivalent to GETDATE(). Так написано в HELPе по Transact-SQL. Просто по умолчанию в поле надо писать текушую дату.
← →
31512 © (2004-03-26 09:55) [7]Для Nikolay M.
Тогда не ясно, почему такая разница для БД созданной Accessом и скриптом, если флаги выставляются на клиенте. В том-то и фикус-пикус, что одна и та же программа ведёт себя по разному с абсолютно одинаковыми БД по структуре, но разными по "происхождению". Ну имена у этих баз данных разные. Делов - то строчку подключения в ADOConnection на серверном прилодении подправить.
← →
Nikolay M. © (2004-03-26 10:29) [8]Попробуй руками выставлять необходимые флаги провайдера. Помню, тоже как-то приходилось так делать, но не вспомню, почему.
← →
31512 © (2004-03-26 10:34) [9]Для Nikolay M.
В том-то и дело, что с этого всё и начинается. Для провайдера и в том и в другом случае выставлены одни и те же флаги:
poFetchBlobsOnDemand = True
poFetchDetailsOnDemand = True
poIncFieldProp s= True
poCascadeDeletes = True
poCascadeUpdates = True
poAllowMultiRecordUpdates = True
poAutoRefresh = True
poAllowCommandText = True
poRetainServerOrder = True.
У остальных False.
← →
31512 © (2004-03-26 10:37) [10]Для Nikolay M.
Может чего с бизнес-правилами порекомендуешь? Я имею ввиду обработчик TRemoteDataModule.DataSetProviderBeforeUpdateRecord.
← →
Nikolay M. © (2004-03-26 10:48) [11]
> Для провайдера и в том и в другом случае выставлены одни
> и те же флаги:
Я имел ввиду флаги для полей.
> Может чего с бизнес-правилами порекомендуешь? Я имею ввиду
> обработчик TRemoteDataModule.DataSetProviderBeforeUpdateRecord
А что я тут могу порекомендовать?
← →
31512 © (2004-03-26 10:52) [12]Для Nikolay M.
Ты хочешь сказать, что после выполнения каждого запроса, я должен в получившемся DataSetе выставлять флаги полей? Можешь уточнить, что за флаги полей? Может мы про разные вещи говорим...
← →
Nikolay M. © (2004-03-26 11:11) [13]
> Можешь уточнить, что за флаги полей?
> TField.ProviderFlags.pfInKey - идентефикация с помощью первичного
> ключа БД ....
Для ключевый полей: pfInKey, для полей, которые будут изменяться: pfInUpdate.
← →
31512 © (2004-03-26 11:26) [14]Для Nikolay M.
Ага... Попробуем.
← →
31512 © (2004-03-26 15:01) [15]Выставляю флаги:
function TRemoteDataModule.DataSetProviderDataRequest(
Sender: TObject; Input: OleVariant): OleVariant;
var i : integer;
begin
ADOQuery.Close;
ADOQuery.SQL.Text:="select * from ORGANISATION";
ADOQuery.Open;
for i:=0 to ADOQuery.FieldCount - 1 do
begin
if i=0 then ADOQuery.Fields[i].ProviderFlags := [pfInKey] - это ключевое поле
else ADOQueryFields[i].ProviderFlags := [pfInUpdate];
end;
end;
Всё равно выходит банан...
← →
31512 © (2004-03-26 15:05) [16]Наверное не там флаги выставляю.....
← →
Nikolay M. © (2004-03-26 15:09) [17]Попробуй
if i=0 then ADOQuery.Fields[i].ProviderFlags := [pfInKey, pfInUpdate]
← →
31512 © (2004-03-26 15:33) [18]Интересно. Я сделал if i=0 then ADOQuery.Fields[i].ProviderFlags := [pfInKey, pfInUpdate]. И с начала болучил обычный банан через HandleReconcileError(DataSet, UpdateKind, E);
Потом я р\переноше тот же код в TRemoteDataModule.RemoteDataModuleCreate и теперь получаю бананы через обычный MessageBox... :smile:
← →
31512 © (2004-03-26 15:40) [19]Это бизнес-правило на удаление записей ...
procedure TRemoteDataModule.DataSetProviderBeforeUpdateRecord(
Sender: TObject; SourceDS: TDataSet; DeltaDS: TCustomClientDataSet;
UpdateKind: TUpdateKind; var Applied: Boolean);
var Delete_ID : string;
begin
if UpdateKind=ukDelete then
begin
ADOQuery.Open;
Delete_ID := DeltaDS["ID_ORGANISATION"];
ADOQuery.SQL.Text:="update ORGANISATION set DELETED=1 where ID_ORGANISATION = "+Delete_ID;
ADOQuery.ExecSQL;
ADOQuery.Close;
ADOQuery.SQL.Text:="select * from ORGANISATION";
ADOQuery.Open;
Applied := True;
end;
if UpdateKind=ukModify then
begin
?????
Applied := True;
end;
end;
Что можно написать для обновления? Вместо ?????
← →
Nikolay M. © (2004-03-26 15:50) [20]ОООО....
Сразу 3 вопроса (больше лень писать):
1) У тебя что, везде используется один и тот же объект ADOQuery? И для выборки, и для удаления, и для непонятно зачем повторной выборки?
2) Зачем это?
> ADOQuery.ExecSQL;
> ADOQuery.Close;
3) И это?
> ADOQuery.SQL.Text:="select * from ORGANISATION";
> ADOQuery.Open;
← →
31512 © (2004-03-26 16:28) [21]Для Nikolay M.
Одна таблица - один ADOQuery. ADOQuery - тут один, поскольку работаем всего с единственной таблицей БД.
ADOQuery.Close - там лишний, в баню его. Затесался незнамо как.
ADOQuery.SQL.Text:="select * from ORGANISATION";
ADOQuery.Open;
просто обновляем. Согласен, можно и по другому.
← →
31512 © (2004-03-26 16:33) [22]Теперь о решении проблемы.
Великий Romkin "полил свету на проблему эту, оказалось, что проблемы-то и нету"
У ключевых полей ADOQuery выставляем флаг pfInKey, а у DataSetProviderа UpdateMode := upWhereKeyOnly.
И всё работает.
Моё предположение таково:
Мастер Accessa тщательней конфигурирует поля, благодаря чему они корректно распознаются ADO и корректно передаются.
Возможно я заблуждаюсь.
← →
Romkin © (2004-03-26 16:45) [23]31512 © (26.03.04 16:33) [22] :))) Ты бы таки прочитал полностью вставку в статью.
"Запись изменена другим пользователем" выдается, когда в ответ на запрос на изменение, сформированный провайдером, сервер выдает, что никаких записей не изменено (есть такой статус).
Nikolay M. Сразу надо посылать на rsdn ;)
← →
Nikolay M. © (2004-03-26 16:54) [24]
> Romkin © (26.03.04 16:45) [23]
> Nikolay M. Сразу надо посылать на rsdn ;)
Дык самому интересно потелепатировать :)
> а у DataSetProviderа UpdateMode := upWhereKeyOnly
А до этого какое значение было? Видимо, на мой пост [4] особого внимания не обратил?
← →
Romkin © (2004-03-26 21:20) [25]ДЫк понимать надоть :)) Я специально вставку в статье сделал, что да как. Провайдер управляет всем, надо только ему объяснить.
Кстати, отнюдь не факт, что надо именно WhereKeyOnly, это от логики зависит :)
← →
31512 © (2004-03-29 11:31) [26]Всем спасибо! До следующих "полтергейстов" :).
← →
31512 © (2004-04-01 10:31) [27]!www.rsdn.ru! Полезные статьи!
*****************************************************************
Вот такая у меня фигня приключилась.
Я всё настроил. И всё работало. До тех пор, пока на серверном приложении, в RDM модуле я не внёс поля в ADOQuery с целью установления флагов. Теперь на клиентском приложении, в DBGride при попытке внести сразу две записи подряд, получаю сообщение "Key violation". Обойти это мне удалось, покамест, только явным установлением значения певичного ключа в DBGrid, ручками.
Вообще-то для пользователя нужно скрыть поле первичного ключа, незачем ему знать о его существовании.
Кстати, одна запись вносится успешно, без указания нового значения первичного ключа.
****************************************************************
← →
Nikolay M. © (2004-04-01 10:42) [28]Подозреваю, что если между вставками записей вставить переоткрытие НД, то все будет ок (это не решение, конечно, а только направлению к походу мысли).
← →
31512 © (2004-04-01 11:05) [29]Торможу. НД - это что?
← →
Nikolay M. © (2004-04-01 11:46) [30]Набор Данных
← →
31512 © (2004-04-01 11:52) [31]Если так он редактирует всегда оду и ту же строку - первую.
Тут дублирование ключа происходит...
← →
Nikolay M. © (2004-04-01 11:59) [32]
> Тут дублирование ключа происходит...
Угу. Потому что после первого инсерта оно у тебя не определено.
← →
31512 © (2004-04-01 12:16) [33]Как бы обойти это... Для пользоваетля не нужно ключевые поля показвывать...
← →
Nikolay M. © (2004-04-01 12:25) [34]
> Как бы обойти это...
Делай на сервере метод GenNewID, получай новую ID-шку, и присваивай ее ключевому полю в CDS.OnNewRecord.
http://rsdn.ru/article/db/midas_migration.xml#XSLTSECTION124143120120
> Для пользоваетля не нужно ключевые поля показвывать...
Ну и не показывай. Не мешай мух с котлетами.
← →
31512 © (2004-04-01 12:40) [35]Ошибка возникает на клиенте. Источник данных - ClientDataSet. Вот если бы ошибка возникала при вызове. ClientDataSet.ApplyUpdates, тогда другой разговор. С клиетом разбираться надо. А не хотелось бы. Ошибка возникает только в том случае, если на сервере в ADOQuery добавлять поля. Если не добавлять, то приходится программно устанавливать флаги. Не желательно отказываться от постоянного списка полей на сервере.
← →
31512 © (2004-04-01 13:58) [36]Следующий код издаёт звук, когда пытаешься вставить вторую подряд запись
procedure TForm1.ClientDataSet1BeforeInsert(DataSet: TDataSet);
begin
if DataSet["ID_ORGANISATION"] = NULL then
begin
Beep;
end;
← →
LittleSpo (2004-04-01 14:37) [37]ты лутьше таблички еще раз посмотри ,
точно там есть увсех табличек куды вставляешь запись первичные ключи
← →
31512 © (2004-04-01 15:25) [38]Табличек нет - есть CliendDataSet. Поля передаются с серверного приложения, причём тип поля TAutoIncField. Т.е. суть поля определяется корректно. Почему оно не работает как TAutoIncField - этого я понять не могу.
← →
31512 © (2004-04-01 15:29) [39]Вот что написано в Helpе
TAutoIncField is a persistent field object for an autoincrement field in a dataset.
Unit
DB
Description
TAutoIncField provides access to an autoincrement field in a table. Autoincrement fields can hold values in the range -2,147,483,648 to 2,147,483,647, and are used as a counter for the records in the dataset. Each record is given a unique value in an autoincrement field, with each record receiving the next highest integer from that of the previously inserted record. Autoincrement fields are commonly used to provide a unique primary key value for a record.
Note: Auto-incrementing fields are a function of the database back-end. An application should not attempt to set a field value for an autoincrement field (with or without a TAutoIncField object).
If you use the Fields editor at design time to create a persistent field component for the auto-incrementing field, you can access it by name at runtime. When using dynamic field components, you can access the TAutoIncField instance using the dataset’s Fields property or FieldByName method.
← →
Nikolay M. © (2004-04-01 15:50) [40]Вот только не надо генерить ID на клиенте!
Ты в [34] ссылку читал?
← →
31512 © (2004-04-01 16:06) [41]Ага. Спасибо большое. Работает вроде :)
← →
31512 © (2004-04-01 16:07) [42]procedure TForm1.ClientDataSet1NewRecord(DataSet: TDataSet);
begin
if DataSet["ID_ORGANISATION"] = NULL then
begin
Beep;
DataSet["ID_ORGANISATION"] := Random(32768);
end;
end;
← →
Nikolay M. © (2004-04-01 16:36) [43]
> DataSet["ID_ORGANISATION"] := Random(32768);
Надеюсь, это только для теста?
← →
31512 © (2004-04-01 16:53) [44]Разумеется для теста. Вот только плох этот способ. В клиентском приложении появляется логика базы данных. Приходится в ClientDataSetе добавлять поля в редакторе + для ID_ORGANISATION устанавливать ReadOnly := False. А это не есть гуд.
← →
31512 © (2004-04-01 17:29) [45]Ищу у способ обойтись без внесения полей в ClientDataSet в клиентском приложении и без установления ReadOnly := False для ключевого поля.
← →
31512 © (2004-04-07 10:27) [46]Вот ещё одно из возможных решений. При этом от identity отказываемся и ключевые поля имеют просто тип int...
.
.
.
var
.....
ID : integer = 0;
.....
.
.
.
procedure TForm1.ClientDataSet1NewRecord(DataSet: TDataSet);
begin
DataSet.Fields[0].AsInteger := ID;
end;
procedure TForm1.ClientDataSet1BeforeInsert(DataSet: TDataSet);
begin
ID := DataSet.Fields[0].AsInteger;
Inc(ID);
end;
Страницы: 1 2 вся ветка
Форум: "Базы";
Текущий архив: 2004.05.02;
Скачать: [xml.tar.bz2];
Память: 0.58 MB
Время: 0.043 c