Главная страница
Top.Mail.Ru    Яндекс.Метрика
Текущий архив: 2004.02.25;
Скачать: CL | DM;

Вниз

Insert, Update, Delete своими руками   Найти похожие ветки 

 
mvg_first ©   (2004-01-19 16:40) [0]

Добрый день мастера и остальные.
Заранее прошу прощения за возможно-банальный вопрос, но я настолько давно не общался с сообществом Delphi-программистов что правильно впорос поставить сейчас и не смогу. Поэтому прошу отнестись с пониманием и "больно не пинать"
Суть задачи.
Я пробую разрабатывать приложение для работы с БД MSSQL2000/ Для этого использую компоненты TADQQuery. В качестве контролов использую стандартный DBGrid.
Но сложность заключается в том что я хочу все запросы на вставку, добавление и удаление записей из отображаемых таблиц - делать самостоятельно. Т.е. когда пользователь добавляет в Гриде запись и пробует ее записать - что бы каким-то образом получить доступ к изменившимся полям выполнить операцию по вставке и если не случилось ошибки дать пользователю возможность работать с программой дальше, а в случае ошибки отменить вставку строки (вомзожно с выводом сообщения).
Компонет реализующий очень похожую функциональность в Делфи есть это TUpdateSQL - но он работает только с BDE версиями компонентов доступа к данным. Я же хочу использовать технологию ADO.
Пожалуйста порекомендуйте литературу (интеренет ресурсы), компоненты, образцы исходного кода - проработав которые я смогу самостоятельно разобраться и решить поставленную задачу.

Если к кому нибудь из Вас можно обратится посредством ICQ буду очень признателен (мой ICQ 84705448)


 
stone ©   (2004-01-19 16:43) [1]

Стучись. Моя аська в анкете.


 
Academic ©   (2004-01-19 16:48) [2]

Если хочешь все запросы на обновление делать самостоятельно,
то не используй на клиенте DBGrig, или создавай виртуальный DataSet, например TClientDataSet.


 
KSergey ©   (2004-01-19 16:53) [3]

> [1] stone © (19.01.04 16:43)
> Стучись. Моя аська в анкете.

Может выложите здесь?
А то не сподручно будет, если еще и я, а может и еще кто в аську попремся ;)


 
mvg_first ©   (2004-01-19 17:13) [4]

Пока нечего выкладывать поступило предложение обрабатывать событие BeforeInsert на ДатаСете который привязан к используюемому пользователем гриду.
Но я не уверен что это правильная идея.
Может быть что бы добавить ясности стоит описать порядок действий пользовтеля и тот порядок который я хотел бы выполнить (но незнаю как):
1. Программа выполняет запрос вида
SELECT * From TESTTABLE
и выводит результат запроса в грид. (тут никаких вопросов)
2. Далее пользователь нажинмает кнопку Инсерт, вводит данные в какие то поля и уходит с записи (т.е. должен по идее сработать метод пост)
3. В этом месте программа должна отловить попытку вставить запись, проанализировать какие поля пользователь изменил, выполнить Инсерт согласно установленных мной правил. При этом стандартный инсерт датасета выполнится недолжен
4. Программа возвращает в грид (или в датасет - тут мне непонятно) информацию которую нужно вернуть после выполнения инсерта.
Ну и т.д. и т.п.


 
Academic ©   (2004-01-19 17:21) [5]

На BeforePost(Delete), отлавливаешь изменения, вручную делаешь Update/Insert/Delete, Далле Abort.


 
mvg_first ©   (2004-01-19 17:30) [6]

А вызов Abort не отменит ли саму операцию инсерта как таковую? Почему то мне кажется как только я сделаю вызов Abort - на гриде добавляемая запись просто исчезнет потому как датасет ему скажет что операция отменена?


 
Academic ©   (2004-01-19 17:34) [7]


> mvg_first © (19.01.04 17:30)

Согласен, немного не подумал.
Но объясни, зачем это все делать вручную???


 
Academic ©   (2004-01-19 17:37) [8]


> А вызов Abort не отменит ли саму операцию инсерта как таковую?
> Почему то мне кажется как только я сделаю вызов Abort -
> на гриде добавляемая запись просто исчезнет потому как датасет
> ему скажет что операция отменена?


Блин, ну я торможу...
Тебе не все равно что она отменится?
Ты же делаешь Refresh?


 
Petr V. Abramov ©   (2004-01-19 17:49) [9]

В BeforePost выполняете Update/Insert-запросы ( в зависимости от Dataset.State), в BeforeDelete - Delete-запрос. Если Insert/Update-запрос возвращает какие-то параметры, их "еще не поздно" просто подставить в соотвествушие поля DataSet`а. Если возникает exception - у пользователя не получится уйти с записи, не исправив свои ошибки ввода или не отменив ввод.
Технология 100% рабочая, испробованная, но если ее использовать "в лоб", т.е. со стандартным Дельфовым набором компонентов, требует много ручного труда, причем именно ручного, а не умственного


 
mvg_first ©   (2004-01-19 18:01) [10]

А какие могут применяться нестандартные компоненты облегчающие задачу? Ибо мне предстоит работать с большим количеством разнообразных таблиц. И не хочется для каждой из них много механического труда применять если есть возможность это обойти


 
mvg_first ©   (2004-01-19 18:24) [11]

А на вопрос зачем мне это все - изначально я хотел работать на хранимых процедурах и селекты и инсерты и апдейты делать на них - но потом просто захотел решить этот вопрос для себя кардинально.
Т.е. я хочу сам управлять изменением записей на сервере. Тем более не всегда возможно отдать на откуп обновление данных датасету. Ведь могут быть сложные конструкции селектов с разветвленными Join-ами и сложными условиями.
Как тут быть? Как контроллировать логику добавления записей?


 
mvg_first ©   (2004-01-20 09:30) [12]

Больше предложений не поступит? Только через BeforePost?
Попоробовал BeforePost - но так и не понял как сформировав и выполнив свой запрос не дать выполнится инсерту по дефолту проходящему в Дата сете


 
Academic ©   (2004-01-20 10:00) [13]

Повторяю...
Даже если на BeforePost, после Abort, данные
в DBGrid пропадут, изменения уже прошли в базу.
Достаточно сделать Refresh данных, и они отобразятся.


 
DenK_vrtz ©   (2004-01-20 10:15) [14]

>Как тут быть? Как контроллировать логику добавления записей?

а все на сервере нельзя контролировать?
ключи, индексы, обработчики ошибок, триггера и т.д.


 
DDP1   (2004-01-20 10:51) [15]

Есть предложение. Вместо
SELECT * From TESTTABLE1 T1
JOIN TESTTABLE2 T2 ON T1.KOD=T2.KOD

пишем
SELECT * into #t From TESTTABLE1 T1
JOIN TESTTABLE2 T2 ON T1.KOD=T2.KOD
select * from #t

отлавливаем все измения во временной табличке, пишем свои изменения в реальные таблички.


 
Плохиш_   (2004-01-20 11:04) [16]

>mvg_first

1. grid.readonly := true
2. на форму кнопки insert, edit, delete
3. форму для ввода данньх
4. Думалка.Active := true


 
KSergey ©   (2004-01-20 11:14) [17]

> [14] DenK_vrtz © (20.01.04 10:15)
> >Как тут быть? Как контроллировать логику добавления записей?
>
> а все на сервере нельзя контролировать?
> ключи, индексы, обработчики ошибок, триггера и т.д.

Высказывание не по теме
Речь идет не о контроле целостности, а о контроле над отправляемыми запросами по модификации данных, которые (запросы) ADO тщательно пытается от нас сокрыть.

> [9] Petr V. Abramov © (19.01.04 17:49)
> В BeforePost выполняете Update/Insert-запросы ( в зависимости
> от Dataset.State), в BeforeDelete - Delete-запрос. Если
> Insert/Update-запрос возвращает какие-то параметры,

Не совсем понял о каких параметрах речь. Уточните, пожалуйста.
их "еще

> не поздно" просто подставить в соотвествушие поля DataSet`а.
> Если возникает exception - у пользователя не получится уйти
> с записи, не исправив свои ошибки ввода или не отменив ввод.
> Технология 100% рабочая, испробованная, но если ее использовать
> "в лоб", т.е. со стандартным Дельфовым набором компонентов,
> требует много ручного труда, причем именно ручного, а не
> умственного

Вслед за mvg_first хочется спросить: "как сформировав и выполнив свой запрос не дать выполнится инсерту по дефолту проходящему в Дата сете"??

[13] Academic © (20.01.04 10:00)
Повторяю...
Даже если на BeforePost, после Abort, данные
в DBGrid пропадут, изменения уже прошли в базу.
Достаточно сделать Refresh данных, и они отобразятся.

Refresh - это если запись у меня уже етьс (если о рефреше одной записи речь). А если она добавилась - как клиентская часть может этот рефреш сделать? Или имеется в виду переоткрыть запрос? На каждую запись? Не слишком ли накладно? Или я что-то недопонял?

Давно мне еще помнится как-то кто-то советовал такой метод: включить BatchUpdate, тогда ADO не посылает запросы, пока не вызвать какой-то там метод (не помню, ну да не важно). Предлагалось сыграть на этом (т.е. вызывать CancelBatch). Вот только на вопрос "дак а как данные-то в DataSet запихнуть, да чтобы еще на сервер новый назрос не ушел" - был дан атвет "а это ноу-хау"...
Так я и не знаю до сих пор как это сделать.


 
DenK_vrtz ©   (2004-01-20 11:52) [18]

>KSergey ©

>Высказывание не по теме
>Речь идет не о контроле целостности, а о контроле над
>отправляемыми запросами по модификации данных, которые
>(запросы) ADO тщательно пытается от нас сокрыть.

не согласен!
Смотрим [11].
Логику ввода данных реально проверять на сервере, а не заниматься эти на клиете. Я не прав?

Сергей, или я сути вопроса не понимаю?


 
KSergey ©   (2004-01-20 12:40) [19]

> [16] Плохиш_ (20.01.04 11:04)
> 1. grid.readonly := true

А если надо именно как раз grid.readonly := false?

> 4. Думалка.Active := true

Этой темой интересуюсь давно, однако постоянно вижу одно и тоже: рассказывают очевидные в принципе вещи про то, что сесть надо на BeforeUpdate/Insert и отсылать свой запрос.
Но как только касается как раз самого узкого места "как же заставить не отправляться запрос движком ADO, однако обновить DataSet" - тут же либо молчок, либо что-то невразумительное, либо думай сам, "ноу-хау". Так остальное - оно и так очевидно!!! Дак уж разжуйте хоть раз до конца, хоть кто-нибудь, "ну что вам, трудно чтоли?"!

Вот и Petr V. Abramov утверждает, что у него "Технология 100% рабочая", однако тоже недосказывает... Хотя пока надежды не теряю услышать его комментарии.

> [18] DenK_vrtz © (20.01.04 11:52)
> Сергей, или я сути вопроса не понимаю?

Этого я не знаю ;)
Впрочем, как писал уже не один раз - самое узкое место для меня и до сих пор не понятое - "как заставить не отправляться запрос движком ADO, однако обновить DataSet". А то, что логику можно проверить на сервере - это не суть важно, на мой взляд. Т.е. и так понятно, что раз я сам формирую запрос на изменение данных - то это может быть что угодно. И под словом "контролировать" понимается, как я думаю, не контроль за целостностью данных, а контроль за отправляемыми запросами на сервер.

Я пока вижу лишь один способ обновлять DataSet: рефреш записи, однако если для модифицируемой записи это еще выполнимо, то для вновь вставленной - переоткрывать весь DataSet? Как-то не очень это здорово, по-моему... А как иначе - не знаю я...

> [15] DDP1 (20.01.04 10:51)
> пишем
> SELECT * into #t From TESTTABLE1 T1
> JOIN TESTTABLE2 T2 ON T1.KOD=T2.KOD
> select * from #t

Да, предложение так же не ново. Можно, факт. Можно даже создавая временнубю таблицу сразу создавать и тригера, которые будут прямо на сервере править реальные таблички, вот только как-то через чур заумно получается, на мой взгляд. Хотя многие проблемы действительно можно решить. С другой стороны - тогда уж проще вообще табличку в памяти организовать - получится точно менее ресурсоемко при той же функциональности.
Причем, что досадно, ну была же у борланда шикарная технология с UpdateSQL! Ну ведь действительно прекрасно придумано. А в ADO - ну нету такого и все... Хотя я и понимаю, что UpdateSQL - чисто борландовская примочка. К слову сказать, в библиотеке VGLibrary есть ADO-компоненты и именно с UpdateSQL. Но давно она что-то не развивается уже.


 
DDP1   (2004-01-20 13:27) [20]

KSergey © (20.01.04 12:40) [19]
А вто статья есть
http://www.delphikingdom.com/treasury/adocomp.htm
Я правда не разбирался


 
KSergey ©   (2004-01-20 15:12) [21]

> [20] DDP1 (20.01.04 13:27)

А это да, это есть такое.
Правда, в подобных случаях меня всегда смущает фраза "Это может способствовать быстрому переводу программ с BDE на ADO". Как я понимаю, автор хочет сказать, что это решение "для ленивых", вроде как и без этого можно обойтись...
Ну либо я напрасно так это все понимаю.


 
mvg_first ©   (2004-01-20 16:13) [22]

Плохиш_
Такой банальный вариант - давно эксплуатируется - меня не устравивает - мне нужно пользователю предоставить изменение менно в гриде данных - ибо данные однотипного образца и скорость ввода данных в гриде будет выше.
DenK_vrtz
Мне не нужно контроллировать логику работы БД (вернее конкретно в этом вопросе) мне нужно самостоятельно составить запрос и не дать этот запрос сделать ADO-шному датасету.
И поэтому никаких временных таблиц дополнительных транзакций и прочего не нужно. В бд уже есть своя логика завязанная на многих факторах и только из-за того что клиентское приложение не может правильный запрос послать - я ее переделывать не собираюсь.
KSergey
Присоединяюсь к Вам в поисках решения по этому вопросу. Если вдруг поиски увенчаются успехом и результат не будет ноухау - не соблаговолите ли Вы известить о результатах меня?

To ALL
Вопрос не закрываю и буду рад дальнейшим предложениям и советам.


 
Arm79 ©   (2004-01-20 16:51) [23]

Собственно говоря, я немного не понял. Зачем столько мути? Вместо UpgateSQL предлагаю использовать ADOCommand в режиме Command
Text.


 
mvg_first ©   (2004-01-20 17:42) [24]

Arm79
Краткий алгоритм в студию!!!
Основная проблема при выполнении изменений в гриде - недать датасету выполнить внутренний запрос к БД - как это решается с помощью ADOCommand?


 
KSergey ©   (2004-01-21 08:48) [25]

Off
Вот, я же говорил: идет множество очевидных предложений не по сути, как только в лоб спрашиваешь "так как же в конце концов" (см., например, ответ mvg_first для Arm79) - так все...

To > [9] Petr V. Abramov © (19.01.04 17:49)
> Технология 100% рабочая, испробованная, но если ее использовать
> "в лоб", т.е. со стандартным Дельфовым набором компонентов,
> требует много ручного труда, причем именно ручного, а не
> умственного

Может вы все же сможете поделиться 100% рабочей технологией с общественностью? Любой. Хоть в лоб. хоть по лбу - уже не важно ;)
(не хотелось бы думать, что уверения про 100% были просто для красного словца...)

> [22] mvg_first © (20.01.04 16:13)
> KSergey
> не соблаговолите ли Вы известить о результатах меня?

Забуду, стопудово.
Максимум что могу обещать - опубликовать на этом форуме.


 
mvg_first ©   (2004-01-21 11:06) [26]

В процессе длительних ковыряний (вчера до часу ночи сидел) пришел к выводу что один из "человеческих" вариантов - это написать свой потомок TADOCustomDataSet и переопределить в нем метод InternalPost. Вызывая Refresh - у объекта RecordSet (тот который чиста адошный)


 
mvg_first ©   (2004-01-27 13:31) [27]

Тут порекомендовали использовать View на сложные таблици и INSTEAD OF тригерры - но попробовал и получается чепуха какая то. Если я из Query Analizer выполняю инсерт то триггер отрабатыавет как и задумаывалось. А если я загоняю селект из этой вьюхи в обыкновенную ADOQuery то при добавлении записи выполняется инсернт непосредственно в таблицу на которую смотрит View??? Никто с таким не сталкивался?


 
sniknik ©   (2004-01-27 13:39) [28]

> Основная проблема при выполнении изменений в гриде - недать датасету выполнить внутренний запрос к БД
и это проблема?
ADODataSet.Connection:= nil;
все ни один не прорвется. ;о) а дальше все через ADOCommand руками.


 
mvg_first ©   (2004-01-27 15:19) [29]

Дык а как потом обновить данные в гриде (даже если и принять Ваш метод) мало того добавить запись нужно еще что бы пользователь увидел ее и после добавления как раз оказался спозиционированным на ней.

Я пробовал вариант - переопределить метод InternalPost у TCustomADODataset - но не могу разобраться с позиционированием записи если сделать IтternalRequery - запись прыгает всегда на первую строчку. Независимо от того куда добавляли запись.


 
sniknik ©   (2004-01-27 15:57) [30]

> Дык а как потом обновить данные в гриде (даже если и принять Ваш метод)
заявление к чему относится? к ADODataSet.Connection:= nil;?
ну вы же хотели руками, вот и в датасете тоже руками для грида изменения делай.
непойму я вас, то нужно все руками то когда этого добились это же и не нравится.

> ... мало того добавить запись нужно еще что бы пользователь увидел
> ее и после добавления как раз оказался спозиционированным на ней.
ага, понял. допустим я пользователь, задумчиво редактирую строку в гриде не не переходя между записями чтобы случайно post не сделался, т.к. не уверен в том что мне окончательно нужно (может esc нажать для сброса и возврата исходного), и вдруг бабах новая запись и я на ней, и то что я не дописал благополучно записалось на середине неоконченое (конец сомнениям), и старую я не помню и даже где она находится не смотрел. ??? хорошая система. возьму на вооружение (напишу брошурку как делать не надо).
оно конечно хозяин барин, и возможно в конкретном случае именно это и нужно, но всеже.


 
SergSuper   (2004-01-27 16:24) [31]


> А если я загоняю селект из этой вьюхи в обыкновенную ADOQuery
> то при добавлении записи выполняется инсернт непосредственно
> в таблицу на которую смотрит View??? Никто с таким не сталкивался?


Надо делать вью с опцией WITH VIEW_METADATA

VIEW_METADATA

Specifies that SQL Server will return to the DBLIB, ODBC, and OLE DB APIs the metadata information about the view, instead of the base table or tables, when browse-mode metadata is being requested for a query that references the view. Browse-mode metadata is additional metadata returned by SQL Server to the client-side DB-LIB, ODBC, and OLE DB APIs, which allow the client-side APIs to implement updatable client-side cursors. Browse-mode meta data includes information about the base table that the columns in the result set belong to.

For views created with VIEW_METADATA option, the browse-mode meta data returns the view name as opposed to the base table names when describing columns from the view in the result set.

When a view is created WITH VIEW_METADATA, all its columns (except for timestamp) are updatable if the view has INSERT or UPDATE INSTEAD OF triggers. See Updatable Views later in this topic.


 
denisov   (2004-01-27 17:11) [32]


> mvg_first © (19.01.04 18:01) [10]


Не читая дальше, скажу, что в аналогичной ситуации использую TVirtualStringTree - очень мощная вещь, "этакая помесь грида и тривью", в добавок хорошая ХР 3D-графика..). Один раз сделал запрос, поместил данные в поля структуры ( type record .. end), на которую есть указатель в созданной строке(ветке - VirtualNode), и в путь. На операции изменения-инсерта в компоненте обрабатываешь все ручками, полный контроль. При необходимости обновления вырираещь селектом одну строку данных, делаешь изменения в данных компонента - дешево и сердито. Для несложного запроса ещё и быстро. Есть ещё плюсы, сам найдешь.


 
Polevi ©   (2004-01-27 17:28) [33]

используй TClientDataset-TDatasetProvider-TADODataset
провайдер позволяет реализовать механизм, подобный TUpdateSQL
а представление данных в XML позволяет сделать все что угодно с помощью парсинга и модификации этого самого XML перед заливкой его в TClientDataset или перед передачей провайдеру


 
mvg_first ©   (2004-01-27 17:53) [34]

2>denisov
А как насчет тормозов на количестве записей > 5000? (только не надого говорить что у меня неправильная логика клиента - я про это и сам много знаю - но я пишу систему обработки прайсов и клиент хочет видеть все товары в одном списки и помечая их группами изменять цены)


 
mvg_first ©   (2004-01-27 17:58) [35]

sniknik
Ты сам понял что сказал???? (не вобиду) я не понял. В полне резонная ситуация когда я добавляю запись, вношу изменения во все поля и перехожу на следующую строку - то очень бы хотелось видеть куда эта запись "упала". Т.к. это и делается в стандартном датасете (если например есть индекс первичный) при добавлении записи - строка перемещается и вместе со строкой и фокус пользователя.
А то как ведут себя пользователи - это вобще отдельный разговор иногда они вытворяет такое что и в кошмарном сне не приснится :) И недай бог твоя программа не снабжена защитами от дурака - все пипец :) Ты же еще и виновать


 
sniknik ©   (2004-01-27 18:15) [36]

mvg_first © (27.01.04 17:58) [35]
какие обиды?
так понимаю возможностей ADO и в частности TADODataset ты не знаеш, по пунктам.
TADODataset.Open; //получили рекордсет (важно, должен локальный курсор стоять)
ADODataSet.Connection:= nil; //отключили от конекта (данные остались!)
ADODataSet.Sort:= "ID"; //считаем что ID это ключь (первичный индекс)
все, изменяй ID в гриде посмотри что будет, все изменения на сервер посылать нужно руками (не этого добивались?).

кстати если ID действительно ключь (а не просто считаем так), еще операция
ADODataSet.FieldByName("ID").ReadOnly:= false;
а то редактировать не даст.


 
mvg_first ©   (2004-01-27 18:16) [37]

denisov
Ссылочку плиз дай? Не нашел :(


 
denisov   (2004-01-27 18:22) [38]


> mvg_first © (27.01.04 17:53) [34]

Разбей их на группы сам..)
А какая проблема в том что записей 5000?


 
jocko ©   (2004-01-28 00:11) [39]

Сколько всего понаписано, а зачем так сложно?
Человек хочет сделать все правильно, а вы ему советуете сделать как всегда...
Посмотри ltBatchOptimistic - помоему решает все вопросы.
Добавь в модуль данных ActionList в нем действия, например Save, Insert и Delete (только не стандартные, а простые) в Execute напиши вызовы нужных хранимых процедур через ADOCommand, а в качестве параметров передай нужные поля ADODataset и ВСЕ!!!
И не слушай про тригеры, вьюхи и тд.
Из литературы BOL и Delphi Help.


 
mvg_first ©   (2004-01-28 09:01) [40]

denisov
Так таки и не подскажешь где поискать этот твой VirtualStringTree?


 
mvg_first ©   (2004-01-28 11:09) [41]

jocko
Я хочу без Экшин-листов :) Хочу просто в гриде добавить запись а датасет должен вызвать при этом мой запрос. И не дать выполнится запрос встроенному в Адошный рекодсет. И после выполнения запроса обновить грид и стать на ту же запись которую добавил или на которую должен встать в результате перемещения после добавления.
Если использовать БатчОптимистик - то как сказать что бы он все таки не делал запрос?


 
sniknik ©   (2004-01-28 11:27) [42]

mvg_first © (28.01.04 11:09) [41]
странный человек, ты скажи ты пробуеш чегонибудь ([36] к примеру), или продолжаеш теоретизировать?
чего до сих пор непонятно? единственное чего не обсуждали пока в этом варианте
> а датасет должен вызвать при этом мой запрос.
чем AfterPost не подходит? (в отсоедененном как раз сработает
> не дать выполнится запрос встроенному в Адошный рекодсет)

или, придумай другую причину по которой этот вариант не проходит. (пока все выдвигаемые "требования" один в один сходятся с действительностью)

> Так таки и не подскажешь где поискать этот твой VirtualStringTree?
почему думаеш что поможет левый компонент без документации если не можеш разабратся в стандартном с кучей хелпов и описаний?


 
denisov   (2004-01-28 19:43) [43]


> mvg_first © (28.01.04 09:01) [40]

В интернете, разумеется.) www.lischke-online.de де то там
http
> sniknik © (28.01.04 11:27) [42]
Левый он чуть более борландовских, к тому же документация в наличии, до безобразия в наличии


 
mvg_first ©   (2004-01-29 09:17) [44]

sniknik
Попробовал. Да действительно если отключать Connection := nil то данные не отсылаются, но я против этого и не спорил. Но по пунктам:
В AfterPost вставка записи не канает :) Потому как в этот момент запись в гирде уже фактически запостилась.
Поэтому написал в методе BeforePost. Следующий код
if ADOQuery1.State = dsInsert then
begin
ADOCommand1.CommandText := "INSERT TABLE1 (TestStr) VALUES(:TestStr) SELECT :ID = SCOPE_IDENTITY()";
ADOCommand1.Parameters.ParamByName("TestStr").Value := DataSet.FieldByName("TestStr").Value;
ADOCommand1.Parameters.ParamByName("id").Direction := pdOutput;
ADOCommand1.Parameters.ParamByName("Id").DataType := ftInteger;
ADOCommand1.Execute;
ADOQuery1.FieldByName("Id").ReadOnly :=false;
ADOQuery1.FieldByName("Id").Value := ADOCommand1.Parameters.ParamByName("Id").Value;
end;

При этом записи добавляются конечно, но в гриде поле ID не обновляется.
Так что даже в этом случае - слегка...
И тут же следующий вопрос а если у меня при добавлении 1-го поля в таблицу происходит заполнение других полей (например триггер срабатывает) как мне эти поля тоже в ручную обрабатывать? А не слишком ли это накладно - эфеект использования DBGrida как таковго теряет смысл :(


 
mvg_first ©   (2004-01-29 09:38) [45]

В догонку (предыдущий пост написал вчера но почему то сайт лежал что ли вообще отправить немог) дома попробовал больше поиграться с вышеописанным алгоритмом. Понял почему не обновляется поле ID - оно автоинкрементное и по определению обновлятся не может. Но все равно проблемы это не решает. Если у меня после инсерта сделанного самостоятельно - кроме измененных пользователем полей меняются еще и другие поля (например заполняется поле PostDateTime в триггере) - то при таком подходе они не обновляются в гриде (вернее мне придется заново, получать это все из грида и полностью переприсваивать все поля отображаемые в гриде руками - объясните мне полезность такого метода?


 
mvg_first ©   (2004-01-29 09:43) [46]

/denisov (28.01.04 19:43) [43]/
Ну а насчет VirtualStringTree - то я к этому времени уже сам нашел, вчера дома поковырял (мощнейшая штука ничего не скажешь) особенно если вкурить во все ее тонкости. Но непонравилось две вещи. Когда к ней подключаются эдиты - они выглядят наложенными сверху а не встроенными в компонент :( Даже не попадают в рамки редактируемого нода. (я это вкорне не приветствую) И второе так и не разобрался как же сделать там возможность начинать редактирование без клика мышью по ноду или без нажатия кнопки F2/ Так как это делается в ДБГриде. Нажимаешь Энтер и начинаешь редактировать еще раз Энтер - заканчиваешь редактировать поле???


 
mvg_first ©   (2004-01-29 09:50) [47]

Насчет использования VIEW c INSTEAD OF триггерами.
Вчера же опять попробовал создать вьюшку но уже с опцией WITH VIEW_METADATA, теперь действительно начали срабатывать триггеры на добавление. Но тут же наткнулся на одну интересную вещь. Использовал стандартные компоненты. В тригере вместо вставки добавляемых значений сделал вставку простой текствой строки в текстовое поле (строка точно не соответствовала добавляемым из грида значениям)
ПОсле этого при попытка добавить в гриде запись добавление происходило но в гриде оставались значения занесенные туда пользователем, а те данные которые реально попадают в таблицу с использованием INSTEAD OF триггера - неотображались - пока не переоткроешь таблицу :( Именно этот факт и вызывает неудовольствие с моей стороны.


 
DenK_vrtz ©   (2004-01-29 10:03) [48]

>ПОсле этого при попытка добавить в гриде запись добавление происходило но в гриде оставались значения занесенные туда пользователем, а те данные которые реально попадают в таблицу с использованием INSTEAD OF триггера - неотображались - пока не переоткроешь таблицу :(

а ты чуда ждал? :)
а refresh, если сделать?


 
Polevi ©   (2004-01-29 10:07) [49]

>mvg_first
ну и каша у тебя в голове :)


 
sniknik ©   (2004-01-29 10:20) [50]

mvg_first © (29.01.04 09:17) [44]

> При этом записи добавляются конечно, но в гриде поле ID не обновляется.
как это? должно.
вот так сделай
ADOQuery1.FieldByName("Id").ReadOnly :=false;
ADOQuery1.FieldByName("Id").Value := 30;
обновилось?
даже если оно автоинкрементное, оно такое в базе/таблице а тут ты уже с рекордсетом работаеш. и т.д. ...
извини, на остальное реагировать не буду, больно много у тебя "нестыковок" в связках желания-знания. опровергать/доказывать все лень.
но вопрос похоже решон? все что изначально описано выполнилось. другое дело хотел ты оказывается не того.


 
mvg_first ©   (2004-01-29 11:48) [51]

sniknik © (29.01.04 10:20) [50]
Именно так неработает - поле в таблице (MSSQL2000) описано как IDENTITY, и именно снимал флаг только для чтения и именно проставлял значение - нихрена, хотя другие поля по такому же принципу в гриде обновляются.

Вопрос решил
Все таки написал свой компонент потомок TCustomADODataset и в нем вызывал Requery после чего позиционировался на записи по ключевому полю (ID) используюя LOCATE


 
sniknik ©   (2004-01-29 12:03) [52]

> Именно так неработает - поле в таблице (MSSQL2000) описано как IDENTITY, и именно снимал флаг только для чтения и именно проставлял
> значение - нихрена, хотя другие поля по такому же принципу в гриде обновляются.

надеюсь говорим про тот ADOQuery1 у которого коннекшн отключен, и про изменения в рекордсете а не в базе->таблице.
счас проверю, я так делал для аксесной базы у которой тоже ID IDENTITY(1,1), может MSSQL чтото еще в настройку передает.

> Вопрос решил
поздравляю. ;о)) (но проверю все одно, просто для себя)


 
Polevi ©   (2004-01-29 12:12) [53]

>sniknik © (29.01.04 12:03) [52]
у меня не получалось в отсоединенном рекордсете присвоить значение AutoInc полю, ReadOnly:=false не помогало, а подменить метаданные я не нашел способа, кроме парсинга XML


 
sniknik ©   (2004-01-29 12:27) [54]

Polevi © (29.01.04 12:12) [53]
странно. у меня и для MSSQL также работает. пример могу прислать. (???) хотя чего там, 3 строчки всего, но все одно чтото значит не так.
курсор локальный?
данные остались посде отсоеденения?
в гриде меняются/добавляются?


 
mvg_first ©   (2004-01-29 12:32) [55]

Может у нас весрии ADO разные?
Все то что ты писал - я делал на стандартном ADQQuery, Delphi7


 
sniknik ©   (2004-01-29 12:57) [56]

в общем высылаю обоим, если у вас не работает то значит адо версия виновата. (хз. у меня вроде не последняя версия, что там в SP4 для w2k. стоит, после ного обновлений не качал)

там переделано, надо открыть и кнопку нажать, увидите какую ;о)), после можно и в гриде автонкремент править и так (первая запись сразу должна последней стать, меняется и индекс по нему).

зы... а ящика то я не нашол, ну чтож после.


 
Polevi ©   (2004-01-29 12:59) [57]

>sniknik © (29.01.04 12:27) [54]
у меня 3-звенка
на среднем звене делается выборка, курсор локальный, рекордсет отсоединятеся и возращается клиенту как результат вызова фасадной ф-ии
на клиенте это выглядит как
MyAdoDataset.RecordSet:=Facade.SelectSomeData;

и после этого
MyAdoDataset.Insert;
MyAdoDataset.FieldByName("AutoIncField").ReadOnly:=false;
MyAdoDataset.FieldByName("AutoIncField").AsInteger:=666;
MyAdoDataset.Post;
не работало, может тупил конечно, но вопрос я изучал конкретно, так что ???
в результате перешел на CLientDataset и не жалею


 
sniknik ©   (2004-01-29 13:12) [58]

вот мое проверочное, без трехзвенки

ADODataSet1.Open;
ADODataSet1.Connection:= nil;
ADODataSet1.Sort:= "AUTOID";
ADODataSet1.FieldByName("AUTOID").ReadOnly:= false;
ADODataSet1.Edit;
ADODataSet1.FieldByName("AUTOID").AsInteger:= 201; //первую запись на последнее место
ADODataSet1.Post;

AUTOID создан с AUTOID INT IDENTITY (1,1)
и после прям в базе назначен ключом.
не знаю, все вроде похоже может только
MyAdoDataset.FieldByName("AutoIncField").ReadOnly:=false;
MyAdoDataset.Insert;
???

треззвенка вроде бы влиять не должна.


 
sniknik ©   (2004-01-29 13:16) [59]

отперестановки мест слагеемых сумма не меняется... ;о)

Polevi © (29.01.04 12:59) [57]
а если явно
MyAdoDataset.Connection:= nil; прописать?
или уже ломает менять чегонибудь? CLientDataset тоже неплохо :о), жаль только *.cds сохраненный из ADODataSet не открывает. :(


 
Polevi ©   (2004-01-29 14:06) [60]

>sniknik © (29.01.04 13:16) [59]
>а если явно
>MyAdoDataset.Connection:= nil; прописать?
на клиенте Connection не определен вообще за ненадобностью, как на форму бросили его так там нил с тех пор и лежит :)


 
sniknik ©   (2004-01-29 14:19) [61]

> на клиенте Connection не определен вообще за ненадобностью, как на форму бросили его так там нил с тех пор и лежит :)
это понятно, но разница то есть, может она в методе отсоеденения от базы (которая у тебя впрочем не выполнится
там код

procedure TCustomADODataSet.SetConnection(const Value: TADOConnection);
begin
if Connection <> Value then
begin
.....

)

даже скорее всего именно изза этого, вот переделал немного, перестало работать!!! (в смысле редактироватся ID)

ADODataSet2.Clone(ADODataSet1);
ADODataSet2.Sort:= "AUTOID";
ADODataSet2.FieldByName("AUTOID").ReadOnly:= false;
ADODataSet2.Edit;
ADODataSet2.FieldByName("AUTOID").AsInteger:= 201; //первую запись на последнее место
ADODataSet2.Post;
DataSource1.DataSet:= ADODataSet2;


ADODataSet2 также просто бросил на форму, без подключений и т.д.


 
Polevi ©   (2004-01-29 14:25) [62]

ну вот, я же говорил !
:-)


 
lmatveev   (2004-01-29 14:36) [63]

Вот так это работает:

TAEMADOQuery = class(TADOQuery)
private
FKeyField: string;
FInsertSQL: TStrings;
FUpdateSQL: TStrings;
FDeleteSQL: TStrings;
FKeyValue: variant;
function GetKeyFieldValue: variant;
function DoExecSQL(aSQL: string): variant;
procedure SetInsertSQL(Value: TStrings);
procedure SetUpdateSQL(Value: TStrings);
procedure SetDeleteSQL(Value: TStrings);
protected
procedure DoAfterOpen; override;
procedure DoBeforeClose; override;
procedure InternalPost; override;
procedure InternalDelete; override;
public
constructor Create(AOwner: TComponent); override;
destructor Destroy; override;
procedure RefreshDataSet; // Делает DisableControls, затем переоткрывается и делает EnableControls
property KeyFieldValue: variant read GetKeyFieldValue;
published
property SQLInsert: TStrings read FInsertSQL write SetInsertSQL; // Пользовательская команда на Insert. Если не указана - выполняется стандартная
property SQLUpdate: TStrings read FUpdateSQL write SetUpdateSQL; // Пользовательская команда на Insert. Если не указана - выполняется стандартная
property SQLDelete: TStrings read FDeleteSQL write SetDeleteSQL; // Пользовательская команда на Insert. Если не указана - выполняется стандартная
property KeyField: string read FKeyField write FKeyField; // Название ключевого поля. Если указано, то после открытия позиционируется на значение, которое было до закрытия
end;

implementation

constructor TAEMADOQuery.Create;
begin
inherited Create(AOwner);
FInsertSQL := TStringList.Create;
FUpdateSQL := TStringList.Create;
FDeleteSQL := TStringList.Create;
end;

destructor TAEMADOQuery.Destroy;
begin
FInsertSQL.Free;
FUpdateSQL.Free;
FDeleteSQL.Free;
inherited Destroy;
end;

function TAEMADOQuery.GetKeyFieldValue: variant;
begin
if Active and (FindField(KeyField) <> nil) then Result := FieldByName(KeyField).Value else Result := null;
end;

procedure TAEMADOQuery.InternalPost;
var
id: variant;
i: integer;
begin
id := null;
if ((State = dsEdit) and (Trim(FUpdateSQL.Text) = "")) or
((State = dsInsert) and (Trim(FInsertSQL.Text) = "")) then
inherited InternalPost
else begin
for I := 0 to Fields.Count - 1 do
with Fields[I] do
if Required and not ReadOnly and (FieldKind = fkData) and IsNull then
begin
FocusControl;
DatabaseErrorFmt(SFieldRequired, [DisplayName]);
end;
if State = dsEdit then
id := DoExecSQL(FUpdateSQL.Text)
else
id := DoExecSQL(FInsertSQL.Text);
if (KeyField <> "") and not(VarIsEmpty(id) or VarIsNull(id)) then
Locate(KeyField, id, []);
end;
end;

procedure TAEMADOQuery.InternalDelete;
var
id: variant;
begin
if (Trim(FDeleteSQL.Text) = "") then
inherited InternalDelete
else begin
id := DoExecSQL(FDeleteSQL.Text);
if (KeyField <> "") and not(VarIsEmpty(id) or VarIsNull(id)) then
Locate(KeyField, id, []);
end;
end;

function TAEMADOQuery.DoExecSQL;
var
FCommand: TADOCommand;
i: integer;
begin
Result := null;
FCommand := TADOCommand.Create(Self);
try
if ConnectionString <> "" then FCommand.ConnectionString := ConnectionString
else FCommand.Connection := Connection;
FCommand.CommandText := aSQL;
for i := 0 to FCommand.Parameters.Count-1 do
if FindField(FCommand.Parameters[i].Name) <> nil then begin
FCommand.Parameters[i].Assign(FieldByName(FCommand.Parameters[i].Name));
if FieldByName(FCommand.Parameters[i].Name).IsNull then
FCommand.Parameters[i].Value := null;
if FCommand.Parameters[i].Name = KeyField then FCommand.Parameters[i].Direction := pdInputOutput;
end
FCommand.Execute;
if FCommand.Parameters.FindParam(KeyField) <> nil then
Result := FCommand.Parameters.ParamByName(KeyField).Value;
finally
FCommand.Free;
end;
RefreshDataSet;
end;

procedure TAEMADOQuery.RefreshDataSet;
begin
if not Active then exit;
DisableControls;
try
Close;
Open;
finally
EnableControls;
end;
end;

procedure TAEMADOQuery.SetInsertSQL;
begin
FInsertSQL.Assign(Value);
end;

procedure TAEMADOQuery.SetUpdateSQL;
begin
FUpdateSQL.Assign(Value);
end;

procedure TAEMADOQuery.SetdeleteSQL;
begin
FDeleteSQL.Assign(Value);
end;

procedure TAEMADOQuery.DoBeforeClose;
var
Field: TField;
begin
inherited DoBeforeClose;
Field := FindField(KeyField);
if Field <> nil then FKeyValue := Field.Value
else FKeyValue := Unassigned;
end;

procedure TAEMADOQuery.DoAfterOpen;
var
Field: TField;
begin
Field := FindField(KeyField);
if (Field <> nil) and not(VarIsEmpty(FKeyValue)) then begin
if not(VarIsNull(FKeyValue)) then
try
Locate(Field.FieldName, FKeyValue, [])
except
end
else
if Field.ReadOnly then Last;
end;
inherited DoAfterOpen;
end;


... и дальше забываем, что это не обычный DataSet. При вставке новый ID возвращаем output параметром. Заодно этот компоент после переоткрытия позиционируется на запись, которая была активна перед закрытием :))
Можно также расставить флагов и чуть-чуть модифицировать, чтобы снизить накладные расходы при переоткрывании


 
mvg_first ©   (2004-01-29 14:39) [64]

Извините за возможно глупый вопрос - но что такое CClientDataSet - все классы в дельфине насколько я знаю обычно начинаются с "T". А с "C" начинаются классы писанные на Visual С++. И по ходу вопрос где можно посмотреть примерчик работы трехзвенки с таким вот классом


 
sniknik ©   (2004-01-29 14:39) [65]

Polevi © (29.01.04 14:25) [62]
> ну вот, я же говорил !
> :-)

а я что против? я изначально ничего про методы трехзвенки не упоминал. (можеш пересмотреть мои посты, там не присвоение рекодсета или клонирование а именно отсоединение)

если чегото нужно то делать надо так как работает (чего и расписывал), а если понимать все по своему и под себя переделывать то и удивлятся не надо почему не работает.


 
sniknik ©   (2004-01-29 14:41) [66]

mvg_first © (29.01.04 14:39) [64]
описка скорее всего
ClientDataSet1: TClientDataSet;

> И по ходу вопрос где можно посмотреть примерчик работы трехзвенки с таким вот классом
в дельфях пример есть вроде


 
mvg_first ©   (2004-01-29 14:45) [67]

lmatveev (29.01.04 14:36) [63]
Блин - у меня такой же код (ну почти такой же) единственное что не так я при инсерте пробую получить IDENTITY используюя функцию MSSQL SCOPE_IDENTITY() и позиционирую запись именно на этом идентификаторе.
Сходу получается ограничение на использование такого компонента в том что таблица должна иметь обязательный превичный ключи (автоинкремент). Хотя это и не сильно уж трудно и тяжело (почит все таблицы в стандартных прикладных задачах его имеют)



Страницы: 1 2 вся ветка

Текущий архив: 2004.02.25;
Скачать: CL | DM;

Наверх




Память: 0.71 MB
Время: 0.03 c
1-79862
WG
2004-02-10 17:11
2004.02.25
Автоматическая отправка письма через Outlook


14-80107
Anatoly Podgoretsky
2004-02-02 17:37
2004.02.25
Разъемы DVI


6-80068
Сетевик
2003-12-18 12:17
2004.02.25
Никто низнает как сделать авторизацию на подключение к idTcpserve


14-80256
REA
2004-02-06 10:40
2004.02.25
Imagebase


3-79639
ivan01234
2004-01-31 19:26
2004.02.25
Как завершить приложение (при его содании), если IBDatabase т....