Главная страница
    Top.Mail.Ru    Яндекс.Метрика
Форум: "Базы";
Текущий архив: 2009.05.17;
Скачать: [xml.tar.bz2];

Вниз

ADO и MS SQL - посоветуйте хорошую ссылку   Найти похожие ветки 

 
kaif ©   (2008-09-08 21:03) [0]

Пишу приложение для базы MSSQL. Решил в приложении использовать ADO. Не могу найти хорошей статьи с примерами.

Например, у меня есть справочники.
Конструкция типа:

INSERT INTO GOODS(GOODS_NAME) VALUES(:NAME)
SELECT :ID = @@IDENTITY

у меня в ADOQuery.ExecSQL не сработала. То есть сработала, но несмотря на то, что я  явно задал 2 параметра (NAME и ID), один как Dirction = pdInput, а другой - pdOutput, после ExecSQL у меня в выходном параметре присутствует NULL.

Я написал хранимую процедуру, возвращающую набор данных:

CREATE PROCEDURE SP_INSERT_GOOD(@NAME VARCHAR(60))
AS
BEGIN
 INSERT INTO GOODS(GOOD_NAME) VALUES(@NAME)
 SELECT :ID = @@IDENTITY
END


И, используя метод ADOQuery.Open, получил желаемое.
Однако мне это решение кажется каким-то кривым.
Хотелось бы увидеть что-нибудь традиционно принятое в этой связке Delphi6-ADO-MSSQL2000.

Буду благодарен за любую информацию.
С уважением.


 
stas ©   (2008-09-08 21:08) [1]

INSERT INTO GOODS(GOODS_NAME) VALUES(:NAME)
SET :ID = @@IDENTITY

ADOQuery.ExecSQL


 
sniknik ©   (2008-09-08 22:04) [2]

> после ExecSQL у меня в выходном параметре присутствует NULL.
либо врешь либо ошибка в 17й строке.

stas ©   (08.09.08 21:08) [1]
> SET :ID = @@IDENTITY
SELECT :ID = @@IDENTITY то же самое по сути, только стиль новее.


 
sniknik ©   (2008-09-08 22:06) [3]

> ADOQuery
кстати заменил бы ты его на ADOCommand


 
Медвежонок Пятачок ©   (2008-09-08 22:08) [4]

у меня в ADOQuery.ExecSQL не сработала.

ADODataSet и ADOCommand. Все остальное не использовать.


 
Медвежонок Пятачок ©   (2008-09-08 22:18) [5]

TAdoCommand + "insert into TABLENAME values(.........)"
+
TAdoDataSet + select IDENT_CURRENT("TABLENAME")


 
sniknik ©   (2008-09-08 22:27) [6]

Медвежонок Пятачок ©   (08.09.08 22:18) [5]
нет смысла разделять команды. опасно. а если возвращать значение через рекордсет, то тогда и инсерт тоже в TAdoDataSet  перенести.


 
Anatoly Podgoretsky ©   (2008-09-08 22:34) [7]

> sniknik  (08.09.2008 22:27:06)  [6]

О чем речь, о получение идентити?
Тогда просто insert + select в одной команде.


 
Медвежонок Пятачок ©   (2008-09-08 22:37) [8]

можно и в одном. еще лучше пересесть на 2005 и заюзать output для инсерта


 
kaif ©   (2008-09-08 22:52) [9]

2 sniknik ©   (08.09.08 22:06) [3]
Ух ты, спасибо, я вообще не разглядел ADOCommand на палитре...

Однако все равно не работает.

 with ADOCommand1 do
 begin
   CommandText := "INSERT INTO GOODS(GOOD_NAME) VALUES (:NAME)"#13+
                           "SET :ID = @@IDENTITY";
   Parameters.ParamByName("NAME").Value := AName;
   Execute;
   ShowMessage(IntToStr(Parameters.ParamByName("ID").Value));
 end;


Ошибка та же. Значение выходного параметра все равно имеет значение Null (в смысле variant).

Пробовал оба синтаксиса, и SELECT и SET. Разницы никакой.


 
stas ©   (2008-09-08 22:56) [10]

странно, поставь дирекшин InputOutput


 
kaif ©   (2008-09-08 23:17) [11]

stas ©   (08.09.08 22:56) [10]
странно, поставь дирекшин InputOutput

Давно стоит.
Если у всех это работает, буду биться с настройками, транзакциями и т.п.

я проверил, не стоит ли режим IMPLICIT_TRANSACTIONS на сервере.

select case when (@@options & 2) = 2 then "IMPLICIT" else "NOT IMPLICIT" end

Вроде бы не стоит.
Даже не знаю, с чем это все может быть связано.


 
kaif ©   (2008-09-08 23:35) [12]

В принципе мне нравится решение Медвежонок Пятачок ©   (08.09.08 22:18) [5], если перевести в TAdoDataSet. Мне не очень нравится запрашивать @@IDENTITY по честному. Но это похоже на мое решение с хранимой процедурой. Неприятно то, что для того чтобы сделать инсерт я в программе вынужден делать Open. Как-то неаккуратненько.
К тому же я не люблю не разобравшись в причине брать альтернативное решение. А вдруг мне еще где-нибудь понадобится вернуть параметр?
Что я буду делать?

Вот я сделал такой запрос для исследования:

 with ADOCommand1 do
 begin
   CommandText := "SELECT :ID = @@Error";
   Execute;
   ShowMessage(IntToStr(Parameters.ParamByName("ID").Value));
 end;


Все отлично. Выходной параметр установлен, значение равно 0.
Значит дело в том, что у меня пакет.
Вот такой текст уже не сработал:

 with ADOCommand1 do
 begin
   CommandText := "INSERT INTO GOODS(GOOD_NAME) VALUES (:NAME)"#13+
                  "SELECT :ID = @@Error";
   Parameters.ParamByName("NAME").Value := AName;
   Execute;
   ShowMessage(IntToStr(Parameters.ParamByName("ID").Value));
 end;


Причем запись вставилась, а вот второй запрос, похоже, то ли не отработал вообще, то ли не сумел донести выходной параметр до места назначения.

А вот так сработало:

 with ADOCommand1 do
 begin
   CommandText :=
                  "BEGIN TRAN"#13+
                  "INSERT INTO GOODS(GOOD_NAME) VALUES (:NAME)"#13+
                  "SELECT :ID = @@IDENTITY"#13+
                  "COMMIT TRAN";
   Parameters.ParamByName("NAME").Value := AName;
   Execute;
   ShowMessage(IntToStr(Parameters.ParamByName("ID").Value));
 end;


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


 
sniknik ©   (2008-09-08 23:42) [13]

> Однако все равно не работает.
в коде нет указания типа параметра, а говоришь что он pdOutput.
как знал, что врешь.


 
Медвежонок Пятачок ©   (2008-09-08 23:44) [14]

у меня аналогично.
set устанавливает значение, select не устанавливает

insert into test_table(name) values("test")
set :id = @@identity


 
sniknik ©   (2008-09-08 23:47) [15]

> О чем речь, о получение идентити?
да
> Тогда просто insert + select в одной команде.
это вариант от "пятачка", а изначально попытка вернуть через параметр. оба варианта работают. если бы не 17я строка...


 
sniknik ©   (2008-09-08 23:48) [16]

> set устанавливает значение, select не устанавливает
не может быть... это разные формы одного и того же.
счас проверю.


 
Медвежонок Пятачок ©   (2008-09-08 23:50) [17]

я уже раза четыре проверил.


 
Медвежонок Пятачок ©   (2008-09-08 23:52) [18]

ой нет. все работает


 
Медвежонок Пятачок ©   (2008-09-08 23:56) [19]

запрос был в дизайнтайме и после смены текста commandtext тип параметра сбрасывался в инпут


 
Германн ©   (2008-09-09 01:12) [20]

<offtop>
Я конечно полный лох в работе с серьёзными БД (типа круче Парадокса), но вот уже создалось стойкое впечатление, что не всё так гладко в датском королевстве ADO при работе с параметрами в runtime.
</offtop>


 
sniknik ©   (2008-09-09 01:46) [21]

> но вот уже создалось стойкое впечатление
и кто это тебя так "впечатлил"?

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


 
kaif ©   (2008-09-09 02:01) [22]

sniknik ©   (08.09.08 23:42) [13]
> Однако все равно не работает.
в коде нет указания типа параметра, а говоришь что он pdOutput.
как знал, что врешь.


Я не вру. Я с самого начала задал persistent-свойства коллекции Parameters, добавив в нее в дизайнере два параметра руками: с именем NAME (pdInput) и ID (pdOutput).
Ты прочитал мой последний пост (kaif ©   (08.09.08 23:35) [12] ) ?
Там ведь я прямо говорю, что после добавления в пакет явного старта транзакции все заработало.

Но объяснения я пока не знаю.
Хотелось бы понять логику этого самого ADOCommand при работе с пакетом команд.

И еще: что за транзакция стартовала? Вложенная?
Где почитать про грамотную работу с транзакциями MSSQL конкретно применяя компоненты ADO Delphi ? Мои поиски пока не увенчались успехом, к сожалению. Может Anatoly Podgoretsky © что-то подскажет?


 
Германн ©   (2008-09-09 02:05) [23]


> sniknik ©   (09.09.08 01:46) [21]

Коля. Спорить почти не буду. Ибо
> Я конечно полный лох в работе с серьёзными БД (типа круче
> Парадокса).

Но при работе с Парадоксом через BDE у меня никогда проблем не было.
Но и  
> вот ведь бардак! ну, что хотят то и творят эти борландовцы!

А что ADO компоненты писали борландовцы?


 
Anatoly Podgoretsky ©   (2008-09-09 07:46) [24]

> Германн  (09.09.2008 2:05:23)  [23]

А кто?


 
Медвежонок Пятачок ©   (2008-09-09 08:44) [25]

стойкое впечатление, что не всё так гладко в датском королевстве ADO при работе с параметрами в runtime.

В самом королевстве еще ничего, а вот в делфийской обертке вокруг него точно есть косяки. причем не только с параметрами


 
sniknik ©   (2008-09-09 08:59) [26]

> Я не вру.
от меня это выглядит по другому... ты даешь часть кода и про ту часть которую не даешь говоришь "там все нормально там я ставлю вот это", когда же после показываешь вторую часть, в которой типа проблем нет. первое что бросается в глаза ты НЕ ЗАДАЕШЬ тот параметр который говорил задаеш...
я не говорю что ты специально, но то что ты врешь очевидно. если бы не скрывал и дал обе половинки кода сразу то я бы сказал заблуждаешься, а так....

> Я с самого начала задал persistent-свойства коллекции Parameters, добавив в нее в дизайнере два параметра руками: с именем NAME (pdInput) и ID (pdOutput).
а запрос вносишь после... забавно, а если в запросе другие параметры используются что делать дельфе?
так получается что тебе все равно, толи сесть в автобус и поехать толи сначала поехать потом сесть в автобус... только первое это повседневность, а второе каскадерский трюк.  

> что после добавления в пакет явного старта транзакции все заработало.
есть 2 обьяснения, первое ну ты наверное понял уже - ты врешь.
второе, запрос вносимый совпал с тем что уже вбит в дизайне, при полном добуквенном совпадении очевидно и параметры теже, смысла переинициализировать их нет... вот они и остались с настроенными уже типами. (по аналогии с локальными переменными, тебе повезло и созданная переменная заняла туже память что при предыдушем вызове, и значит осталась инициализирована старым значением)
больше склоняюсь к первому объяснению... из-за вот этого - #13, с трудом представляю чтобы такой перенос строки мог быть внесен в десигне, т.е. побуквенного совпадения не предвидится... хотя есть вероятность конечно какихто преобразований до сравнений (ни проверять ни лесть в генофонд неохота, проше думать что ты вреш пока ты этого не опровергнеш(или нет, в первом случае ты подтвердил) показав и вторую часть кода...).  

Германн ©   (09.09.08 02:05) [23]
> Но при работе с Парадоксом через BDE у меня никогда проблем не было.
у меня и при работе с ADO проблем никогда не было, даже когда они реально были в компонентах (была пара известных и уже пофиксенных багов), а все почему? думаешь изза того, что я такой умный? хотелось бы, но нет. просто первым, что сделал это прочитал документацию. и главное не просто прочитал, а еще и следовал ей (просто чтение никому не помогает), ее рекомендациям (например использовать ADOQuery/... только для перехода с BDE, т.е. такого временного решения пока не изучишь основные (/быстрого перевода BDE программы на ADO, за счет ухудшения работы собственно программы). я не переходил, я изучал, поэтому сразу взялся за основные забыв про "BDE заглушки", а основные проблемы(те баги о которых выше) именно с ними и были).


 
Медвежонок Пятачок ©   (2008-09-09 09:11) [27]

(была пара известных и уже пофиксенных багов)

Там даже не баги, а урезанная реализация некоторых возможностей.
Например беседовал с разработчиками провайдера к ИБ (lcpi ib provider), когда возник трабл с блобами. ("произошла ошибка во время выполнения многофазной операции .....")
Сам провайдер поставляется в трех версиях. Мне отвечают: в адо экспресс (delphi) возможна работа только с первой версией, бла бла бла, и еще возьмите самый последний триал.
Беру, проверяю. А там все тоже самое. На ровном месте: insert into values + блоб параметры


 
Anatoly Podgoretsky ©   (2008-09-09 10:27) [28]


> Я конечно полный лох в работе с серьёзными БД (типа круче
> Парадокса), но вот уже создалось стойкое впечатление, что
> не всё так гладко в датском королевстве ADO при работе с
> параметрами в runtime.

Вообще то так и есть, реализация параметров в ADO.VCL грешит множеством ошибок, решается переходом на более старшие версии Дельфи, что мне в свое время и пришлось вынужденно сделать. И я не уверен, что Борланд устранил все ошибки, просто требуемые мне свойства заработали в 2006, а я последовательно проверял версии от Д5 до Д2006 и это ошибки именно Борланда, а не самого АДО, они почему то решили, что [N]VARCHAR это FixedChar, а Автоинкримент это Integer и так еще с несколькими типами. А модуль АДОDB построен так, что даже при желании правкой генофонда не проблему не решить, модуль не перекомпилируется, а только делает вид.
В то же время для не параметров эти типы реализованы правильно, и рантайм задача тоже решения не имеет, как ни указывай явно тип, он все равно меняется на FixedChar и прочее.
Судя по некоторым другим признакам, не все ошибки этого рода устранены и в 2006


 
b z   (2008-09-09 11:08) [29]


> kaif ©   (08.09.08 21:03)  
> SELECT :ID = @@IDENTITY
поясните. что это?

> CREATE PROCEDURE SP_INSERT_GOOD(@NAME VARCHAR(60))
CREATE PROCEDURE SP_INSERT_GOOD(@NAME VARCHAR(60), @ID INT OUTPUT) ?
+

> SELECT :ID = @@IDENTITY
SELECT @ID = SCOPE_IDENTITY()
если так?


 
Ega23 ©   (2008-09-09 11:22) [30]

Set NoCount On;
Insert into ... (...) Values (....);
Select Result=Scope_Identity();

Выполнять через TADODataSet.Open;
Брать результат либо через TADODataSet.Fields[0[ либо через обычный FieldByName


 
sniknik ©   (2008-09-09 11:42) [31]

> Там даже не баги, а урезанная реализация некоторых возможностей.
имел в виду именно баги, например был баг при внесении запроса частями через ADOQuery.SQL.Add(), чего по логике ADO делать было нельзя т.к. при внесении запроса определяются какието параметры, и определяются они на стороне сервера куда передается запрос, часть же всегда неправильна... кстати именно поэтому нужно подключение при разработке, можно конечно и без него и руками все устанавливать, толко основные претензии по этому поводу как раз в том что оно чегото не делает, а вот то что программист должен обеспечить какието условия для этого, это игнорируется.

этот баг миновал меня т.к. я им изначально не пользовался, а в CommandText вносить запрос частями затруднительно..., и т.д.

Anatoly Podgoretsky ©   (09.09.08 10:27) [28]
> Вообще то так и есть ...
вообщето это новая информация, не тронутая обсуждением до этого момента, а вывод "там чтото не то"  сделан смотря на когото у кого проблемы т.к. он инструкцию не прочитал, хотя на самом деле это все работает.
это все чем то похоже на выражение (гдето слышал) "если пристально на когото смотреть, любого можно принять за шпиона", т.е. выдать желаемое за действительное.

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

b z   (09.09.08 11:08) [29]
Ega23 ©   (09.09.08 11:22) [30]
блин люди! да вы читать то умеете? и читаете ли что до этого было? прежде чем выдавать образцы корявого кода.

> SELECT :ID = @@IDENTITY
поясните. что это?
это правильное присвоение значения Output параметру. то что у когото он не является Output хотя он говорит обратное. не показатель того что это не работает.


 
Ega23 ©   (2008-09-09 12:20) [32]


> блин люди! да вы читать то умеете? и читаете ли что до этого
> было? прежде чем выдавать образцы корявого кода.


А что я корявого выдал?


 
kaif ©   (2008-09-09 12:29) [33]

2 sniknik ©  

Странно.
Ты так настаиваешь на том, что я вру, что я уже стал рыться в глубинах подсознания, пытаясь отыскать мотив того, зачем мне тебе врать. Правда так и не нашел. Логичнее предположить, что это какие-то твои собственные проекции.

Ты видно не понял, что я ставлю некоторые эксперименты, прежде чем что-то юзать.

Мне проще было установить параметры для эксперимента руками в коллекцию, задать не только их direction, но даже и типы данных. Раз уж существует дизайнер. Я же не привожу ту часть кода, где у меня выполняется TADOCommand.Create, к примеру. И ты вроде бы не упрекаешь меня в том, что я сам компонент еще не создал, а чего-то от него уже хочу. Или ты не в курсе того, что чтобы пользоваться TADOCommand нужно сначала создать экземпляр этого класса?
Если ты по любому поводу готов упрекать человека во вранье, я впредь конкретно для тебя буду приводить код, специально переписанный для тебя  в форме типа:

 MyCommand = TADOCommans.Create(nil)
 try
   MyCommand.Connection := ...
   и так далее, включая добавление параметров и всего такого
   MyCommand.Execute;
 finally
   MyCommand.Free;
 end

Но я всего этого делать не буду.
Так как после таких вот перлов

имел в виду именно баги, например был баг при внесении запроса частями через ADOQuery.SQL.Add(),

я просто понял, что ты не очень понимаешь, в какой последовательности в VCL работают элементарные вещи, например, что такое тип TStrings, в какой момент осуществляется парсинг текста запроса, как создается сама коллекция Parameters и кем именно.

Если бы ты это все понимал, то заметил бы, что у меня нет проблемы с тем, что параметр не найден (метод ParamByName вернул nil). И нет проблемы с тем, что в него что-то не записывается из-за "неопределенности direction", так как при наличиии одной команды в пакете (SELECT :ID = @@Error) прекрасно записывается. И дело здесь именно в том, что я прошу выполниться пакету команд, а не отдельно взятой команде. Так как с пакетами MSSQL я работаю впервые я хотел услышать мнение опытных людей. Точнее даже не мнение, а хотя бы ссылку на литературу с примером использования ADO Delphi конкретно в этом случае.

Что же я услышал от тебя кроме упреков в том что я вру?
Да ничего дельного в данном случае не услышал.
Впрочем, я ничего и не требовал.
Странно, видно тебе кажется, что ты мне обязан отвечать на мои вопросы, будто я тебя к чему-то принуждаю или чем-то задеваю. Иначе ты был бы выжливее и не стал бы обсуждать мою персону вместо того чтобы осбуждать проблему, которая почему-то таинственным образом решается при помощи BEGIN TRAN.

Надеюсь не обидел.


 
kaif ©   (2008-09-09 13:03) [34]

Ega23 ©   (09.09.08 11:22) [30]
Set NoCount On;
Insert into ... (...) Values (....);
Select Result=Scope_Identity();

Выполнять через TADODataSet.Open;
Брать результат либо через TADODataSet.Fields[0[ либо через обычный FieldByName


Спасибо. Похоже, то решение, что я сразу применил, т.е. вызов хранимой процедуры и получение выходного рекордсета (одной строки и одной колонки) методом Open, не является таким уж нетрадиционным решением проблемы.

Собственно, сейчас меня волнуют только транзакции. Когда я открываю набор, надо полагать, что стартует некоторая транзакция. С ADO я мало работал. С MSSQL через ADO не работал никогда.
Дайте ссылку, если у кого есть, на статью, где описано явное управление транзакциями MSSQL при помощи ADO Delphi.


 
Ega23 ©   (2008-09-09 13:04) [35]


> Похоже, то решение, что я сразу применил, т.е. вызов хранимой
> процедуры и получение выходного рекордсета (одной строки
> и одной колонки) методом Open, не является таким уж нетрадиционным
> решением проблемы.


Ну я ОЧЕНЬ часто именно так и делаю.


 
kaif ©   (2008-09-09 13:27) [36]

2 Ega23 ©   (09.09.08 13:04) [35]
Спасибо. Так работает.
И с "Set NoCount On" и без него.
Кстати, по справке BOL я не могу понять какую область действия имеет эта команда. В пределах транзакции, сессии или как? И нужно ли после такого вызова делать Set NoCount Off? Или достаточно проделать один раз после открытия соединения и забыть об этом? Пользуются ли ADO информацией о количестве затронутых записей и не нарушит ли это все работу в каких-то других местах?


 
Anatoly Podgoretsky ©   (2008-09-09 13:41) [37]

> sniknik  (09.09.2008 11:42:31)  [31]

> можно кстати конкретнее, чем то что написано мешает(дает глюки) в работе?

FixedChar <> NVarChar(WideString) = Char
Дело не в фиксированой длине, а то что теряется Юникод, да и сам FixedChar очень неприятен при использование в dbAware - что бы добавить в конец приходится сначала стереть кучу пробелов.

AutoInc <> Integer, хотя бы потому что он ReadOnly и не только Integer

Это не единственные ошибки с типам в параметрах, про остальные просто не помню, но когда разбирался - то помнил, и это по разному в Д5-2006 - похоже хвост отрубали по кускам.


 
Ega23 ©   (2008-09-09 13:47) [38]


> Кстати, по справке BOL я не могу понять какую область действия
> имеет эта команда. В пределах транзакции, сессии или как?
>  И нужно ли после такого вызова делать Set NoCount Off?
> Или достаточно проделать один раз после открытия соединения
> и забыть об этом? Пользуются ли ADO информацией о количестве
> затронутых записей и не нарушит ли это все работу в каких-
> то других местах?


Set NoCount On;
create table #Tmp(UID int identity (1,1), aValue int)
insert into #Tmp(aValue) Values (1)
insert into #Tmp(aValue) Values (2)
insert into #Tmp(aValue) Values (3)
Select * from #Tmp
drop table #Tmp


Попробуй в QueryAnalyzer выполнить с Set NoCount On и без него.

Потом так попробуй:

Set NoCount On
create table #Tmp(UID int identity (1,1), aValue int)
insert into #Tmp(aValue) Values (1)
insert into #Tmp(aValue) Values (2)
Set NoCount OFF
insert into #Tmp(aValue) Values (3)
Select * from #Tmp
drop table #Tmp


 
Anatoly Podgoretsky ©   (2008-09-09 13:50) [39]

> kaif  (09.09.2008 13:03:34)  [34]

Транзакции!
Их две

1. средствами Дельфи на стороне клиента, это когда транзакция запускается через TAdoConnection

2. транзакция запускается средствами сервера, через управление транзакцией в запросе BEGIN TRAN

Считается, что надежнее и правильнее вариант 2, но я всегда использую 1

По поводу литературы по ADO.VCL особо читать нечего, поскольку это всего лишь обертка над ADO (ActiveX) и вот это и надо читать, только тогда можно понять кухню и даже напрямую работать с АДО с помощью компонент. У Борланда много проблем в реализации обертки и конечно эти проблемы не документированы, можно конечно поискать проблемы в QC но стоит ли, это еще та помойка.

Кстати если нет особых условий, то TAdoCommand не требуется, поскольку запросы можно писать в TAdoConnection, можно даже и SELECT запросы и потом работать с _recordSet или переводить его в Dataset - но это уже лишнии сложности, на которые стоит идти только в особых случаях.


 
Anatoly Podgoretsky ©   (2008-09-09 13:53) [40]

> kaif  (09.09.2008 13:27:36)  [36]

Не разбирался, но кажется в рамках транзакции, если задана в транзакции.
И в рамках сессии если задано на уровне AdoConnection.

Подумай также насчет прямой работе с АДО, но через компоненты, а не интерфейсы, можно получить интерсные результаты.


 
sniknik ©   (2008-09-09 13:56) [41]

> А что я корявого выдал?
имя поля более принято задавать через as,  а левая часть выражения логически соответствует то чему присваивается.
хотя это и не смертельно, т.к. синтаксически верны оба варианта.

kaif ©   (09.09.08 12:29) [33]
> зачем мне тебе врать
меня это всегда удивляло (это вообще а не только в тебе), вроде бы и не зачем, вроде и нужно только спрашивающему но тем не менее врут, скрывают исходную инфу, недоговаривают, не следуют советам, мало того даже не проверяют сказанное по их проблеме... т.е. ведут себя неадекватно (имхо), зачем тогда спрашивали?

> я впредь конкретно для тебя буду приводить код, специально переписанный для тебя  в форме типа:

> MyCommand = TADOCommans.Create(nil)
> try
>   MyCommand.Connection := ...
>   и так далее, включая добавление параметров и всего такого
>   MyCommand.Execute;
> finally
>   MyCommand.Free;
> end
если буквально так, то это будет вранье "в волшебных пузырьках". говоря что ты чтото сделал и показывая подобный код который ничего этого не подтверждает, а пропускает...

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


 
kaif ©   (2008-09-09 14:14) [42]

2 Ega23 ©   (09.09.08 13:47) [38]
Я попробовал.

Но я не знаю, не происходит ли каких-нибудь неявных автокоммитов после команд DDL.

Я открыл два отдельных окна запросов в Microsoft SQL Server Management Studio Express. Похоже, что это две разные сессии (хотя я могу ошибаться). В каждой я вижу, что set nocount Работает независимо.


 
Ega23 ©   (2008-09-09 14:17) [43]


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


Исключительно дело привычки. Мне, в принципе, пофиг как писать:
Select Result = @Result
Select @Result as Result
Select @Result Result


В MSSQL все три варианта равнозначны.
Просто так привык.


 
kaif ©   (2008-09-09 14:31) [44]

2 sniknik ©   (09.09.08 13:56) [41]

Блин. Вы меня меня задолбали.
Я сейчас специально для Вас, сударь, напишу код со всемия явными присвоениями, и который НЕ РАБОТАЕТ ТАК, КАК ВЫ НАСТАИВАЕТЕ ЧТО ТИПА ОБЯЗАН РАБОТАТЬ.

А Вы мне, милостивый государь, покажете, где в нем ошибка.

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

Вот код. Специально для тупых.

procedure TSimpleReferenceForm.Button1Click(Sender: TObject);
var
 P: TParameter;
begin
 with TADOCommand.Create(nil) do
 try
   Connection := MainData.Connection;

   P := Parameters.AddParameter;
   P.DataType := ftString;
   P.Direction := pdInput;
   P.Name := "NAME";

   P := Parameters.AddParameter;
   P.DataType := ftInteger;
   P.Direction := pdOutput;
   P.Name := "ID";

   CommandText :=
      "INSERT INTO GOODS(GOOD_NAME) VALUES (:NAME)"#13#10+
      "SELECT :ID = SCOPE_IDENTITY()";

   Parameters.ParamByName("NAME").Value := "Дядя Вася";

   Execute;
   if Parameters.FindParam("ID") = nil then
     ShowMessage("Параметр не найден")
   else if Parameters.ParamByName("ID").Value = Null then
     ShowMessage("Параметр вернул значение NULL. sniknik © зря упорствует в свеой неправоте")
   else
     ShowMessage("Происходит нечто третье");
 finally
   Free;
 end;
end;


Догадатесь с трех раз, какое сообщение он у меня вывел?


 
sniknik ©   (2008-09-09 14:42) [45]

Anatoly Podgoretsky ©   (09.09.08 13:41) [37]
> FixedChar <> NVarChar(WideString) = Char ...
> ...
не сталкивался, юникод не использую, только пробовал один раз, както. т.что ничего сказать не могу.

> AutoInc <> Integer, хотя бы потому что он ReadOnly и не только Integer
а вот тут непонятно, как типом AutoInc может быть параметр? насколько понимаю автоинкремент это исключительно свойство поля таблицы или в старших версиях mssql (> 2000, больше всего с ним работаю) уже появились переменные такого типа?
если же речь не о параметрах, а именно о поле, то инфа о его типе запрашивается от сервера через провайдера, и результаты действительно разные, тип от jet позволяет даже редактировать это поле в режиме ltBatchOptimistic, а вот от mssql нет, т.е. это не свойство обертки.


 
sniknik ©   (2008-09-09 14:44) [46]

> Вот код. Специально для тупых.
специально повторить что тип параметрам (да и всему другому) надо задавать после инициализации, а не до? ;)
внесение запроса переинициализирует все и вся... объяснял вроде почему.


 
sniknik ©   (2008-09-09 14:48) [47]

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

кстати приведенный код, это именно то к чему следует стремится при описании проблемы.


 
sniknik ©   (2008-09-09 14:51) [48]

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


 
Ega23 ©   (2008-09-09 14:55) [49]


> Вот код. Специально для тупых.


Ашот, ты реально неправ. В коде - косяк на косяке.

1. Если стоит DataSet.paramCheck = True то
 1.1. Если на момент задания коннекшена он (коннекшн) подключенный (Connected=True) то блок инициализация первого параметра нафиг не нужна.
 1.2. Если коннект не подключен, либо ParamCheck=False, то надо инициализировать.

2. В любом случае, инициализацию параметров надо делать ПОСЛЕ задавания CommandText.

З.Ы. Я бы на твоем месте извинился.


 
kaif ©   (2008-09-09 14:58) [50]

А вот решение проблемы. Если бы  sniknik © вместо того чтобы упрекать меня во вранье сам не врал о том, будто он знает, зачем устанавливать значение Direction рантайм, то вопрос был бы исчерпан еще вчера.

var
 DirectionNames: array [0..4] of string = ("pdUnknown", "pdInput", "pdOutput", "pdInputOutput", "pdReturnValue");

procedure TSimpleReferenceForm.Button1Click(Sender: TObject);
var
 P: TParameter;
begin
 with TADOCommand.Create(nil) do
 try
   Connection := MainData.Connection;

   P := Parameters.AddParameter;
   P.DataType := ftString;
   P.Direction := pdInput;
   P.Name := "NAME";

   P := Parameters.AddParameter;
   P.DataType := ftInteger;
   P.Direction := pdOutput;
   P.Name := "ID";

   CommandText :=
      "INSERT INTO GOODS(GOOD_NAME) VALUES (:NAME)"#13#10+
      "SELECT :ID = SCOPE_IDENTITY()";

   Parameters.ParamByName("NAME").Value := "Дядя Вася 7";

   ShowMessage(DirectionNames[ord(Parameters.ParamByName("ID").Direction)]);

   Parameters.ParamByName("ID").Direction := pdOutput; //принудительная установка еще раз

   Execute;

   ShowMessage(DirectionNames[ord(Parameters.ParamByName("ID").Direction)]);

   if Parameters.FindParam("ID") = nil then
     ShowMessage("Параметр не найден")
   else if Parameters.ParamByName("ID").Value = Null then
     ShowMessage("Параметр вернул значение NULL. sniknik © зря упорствует в своей неправоте")
   else
     ShowMessage(Format("Значение выходного параметра ID = %s"#13+
                        "sniknik © был бы прав, если бы сказал, что коллекция Parameters ADO кривая"#13+
                        "и работает не так, как разработчик от нее ожидает,"#13+
                        "то есть параметры меняют свое Direction после парсинга текста запроса"#13+
                        "и их Direction нужно ЕЩЕ РАЗ пересустанавливать ОБЯЗАТЕЛЬНО ПОСЛЕ присвоения"#13+
                        "текста свойству CommandText, а не так как это обычно принято у нормальных людей.",
                        [IntToStr(Parameters.ParamByName("ID").Value)]));
 finally
   Free;
 end;
end;


То есть свойства Direction параметров в коллекции Parameters, редактируемые дизайн-тайм в компонентах ADO Delphi являются никому не нужной бутафорией, призванной лишь сбить с толку разработчика.


 
sniknik ©   (2008-09-09 14:59) [51]

блин, еще одно, сравнение с нил > Value = Null не совсем верно (а в базах/запросах так совсем неверно), для проверок есть свойство isNull.

кстати а почему > Value := "Дядя Вася"; ты вносишь правильно, после? а не при создании. видать чегото подозревал? ;)


 
kaif ©   (2008-09-09 15:01) [52]

2 Ega23 ©   (09.09.08 14:55) [49]

Я готов принести свои извинения, если господин извинится передо мной за то, что многократно обвинил меня во вранье.
Я нигде ни разу не врал.
Мне это совершенно ни к чему.


 
sniknik ©   (2008-09-09 15:02) [53]

> А вот решение проблемы.
поверь ЭТО не решение.

> то вопрос был бы исчерпан еще вчера.
если бы ты вчера привел этот код "для тупых"...

> //принудительная установка еще раз
это не еще раз, это единственная которая нужна...


 
kaif ©   (2008-09-09 15:05) [54]

sniknik ©   (09.09.08 14:59) [51]
блин, еще одно, сравнение с нил > Value = Null не совсем верно (а в базах/запросах так совсем неверно), для проверок есть свойство isNull.

кстати а почему > Value := "Дядя Вася"; ты вносишь правильно, после? а не при создании. видать чегото подозревал? ;)


Подозрительность твоя не знает границ. Я вношу параметр NAME после просто потому что я всегда сначала присваиваю текст, а потом задаю значения параметров. У меня такая привычка. А вот привычки думать, что свойства, определенные в дизайнере меняются на ходу без того чтобы об этом было предупреждение в документации у меня нет.
Я же не предполагаю, например, что после выполнения метода Execute мне нужно еще раз, например, CommandText вписывать.

А откуда я знаю?

Может он очистился сдуру. Команда ведь "ушла на сервер".
От этих ADO я уже чего угодно могу ожидать...


 
kaif ©   (2008-09-09 15:06) [55]

sniknik ©   (09.09.08 15:02) [53]
> А вот решение проблемы.
поверь ЭТО не решение.


Что ты имеешь в виду?


 
sniknik ©   (2008-09-09 15:13) [56]

> Что ты имеешь в виду?
procedure TSimpleReferenceForm.Button1Click(Sender: TObject);
begin
with TADOCommand.Create(nil) do
try
  Connection := MainData.Connection;
  CommandText :=
     "INSERT INTO GOODS(GOOD_NAME) VALUES (:NAME)"#13#10+
     "SELECT :ID = SCOPE_IDENTITY()";

  Parameters.ParamByName("NAME").Value := "Дядя Вася 7";
  Parameters.ParamByName("ID").Direction := pdOutput;
  Execute;

  ShowMessage(Parameters.ParamByName("ID").Value);
finally
  Free;
end;
end;

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


 
kaif ©   (2008-09-09 15:13) [57]

sniknik ©   (09.09.08 14:59) [51]
блин, еще одно, сравнение с нил > Value = Null не совсем верно (а в базах/запросах так совсем неверно), для проверок есть свойство isNull.


Ты невнимательно смотришь. Сравнение с nil у меня при поиске параметра методом FindParam. Имеется в виду пустой указатель. А сравнение с Null у меня в другом месте. Там я просто сравниваю две переменные типа Variant. И такое сравнение корректно. Хотя IsNull красивее, разумеется.

А в базах данных NULL никакого отношения к этому вообще не имеет. Просто в Delphi тип данных NULL (неопределенное значение в базе данных) в выходной параметр помещается как типа Variant со значением Null. Больше ничего.


 
MsGuns ©   (2008-09-09 15:15) [58]

>kaif ©   (09.09.08 14:31) [44]

В глубоко научный спор не вмешиваюсь, а только спрошу - зачем, Ашот, ты упорно пытаешься закатить солнце вручную, т.е. пишешь код по созданию и инициализации параметров, ведь это требуется только в исключительных случаях и то, если ParamCheck := false, что, ИМХО, бывает нужным лишь в исключительных случаях ?
И еще вопрос - зачем эти спецсимволы в строке запроса ?

По поводу TADOQuery позволю себе не согласиться с авторитетами - вполне удобный компонент, в частности по простоте анализа RowsAffected. Что же касается глюка SQL.Add (есть такая кака), то вполне лечится SQL.Text := ..

У меня также не возникает проблем с использованием ADO и даже более того, - считаю эту оболочку самой удобной из всех, которыми пользовался при работе с БД

Что же касается транзакций в MSSQL скажу только, что внятного описания самого механизма так и не нашел, хотя искал во вмногих толстых книжках. Все, что вычитал - это приоритет внешних транзакций перед внутренними. И рекомендации использовать явное управление транзакциями в триггерах только в крайних случаях и будучи достаточно уверенным в их необходимости и корректности. По сравнению с ИБ транзакции в МССКЛ "несколько" запутаннее.

И еще, чисто из собственного опыта - старт транзакции и ее завершение на клиенте не рекомендуется вкладывать в текст запроса - лучше это делать, используя TADOConnection.BeginTrans/CommitTrans|RollbackTrans - во-первых "прозрачнее" с т.з. кода, во-вторых в МсСкл нет понятия "транзакция" как самостоятельная величина (как в ИБ) и все они интерпретируются сервером применительно к соединению в целом - поэтому чтобы не запутаться в "ручных" транзакциях (в смысле где внешняя, а где внутренняя), лучше разруливать их в контексте не запроса, а всего соединения в целом.
Что касается бизнес-логики, то там несколько другой принцип, но "база" ессно та же.


 
kaif ©   (2008-09-09 15:17) [59]

sniknik ©   (09.09.08 15:13) [56]

По-твоему я не вижу, в чем разница между тем текстом и этим текстом? Если уж я написал их оба, я наверно знал, что делаю.

Хотя ты сейчас опять скажешь, что я вру.
Похоже ты всерьез думаешь, что все вокруг одни сплошные идиоты, кроме тебя.


 
sniknik ©   (2008-09-09 15:20) [60]

> без того чтобы об этом было предупреждение в документации у меня нет.
вот именно поэтому я думал, что ты не читал документации, раз не знаеш об этом "предупреждении".

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


 
sniknik ©   (2008-09-09 15:22) [61]

> я наверно знал, что делаю.
да? и просвети зачем ты это делаешь? создание параметров до запроса имею ввиду.


 
MsGuns ©   (2008-09-09 15:25) [62]

>sniknik ©   (09.09.08 15:20) [60]
>в остальных случаях, когда этого не нужно, просто стиль.

хреновый стиль


 
sniknik ©   (2008-09-09 15:25) [63]

> Хотя ты сейчас опять скажешь, что я вру.
а то. в чем то обязательно, вот например сейчас когда ты "все" знаеш объясни причем тут транзакция которая якобы помогла тебе вернуть параметр в [12].


 
sniknik ©   (2008-09-09 15:27) [64]

> хреновый стиль
ну, в дельфи в отличие от с "\n" нету ;(.


 
Anatoly Podgoretsky ©   (2008-09-09 15:30) [65]

> sniknik  (09.09.2008 14:42:45)  [45]

Речь и полях и о параметрах. В данный момент я не могу сказать про что именно, я просто помню, что тип ранее был Integer (RW) а потом стал AutoInc (RO) как мне и надо.
TParameter.DataType = ftAutoInc
И это не ADO, а ADO.VCL, точнее DB.PAS
Использовать просто
WHERE ID =:id
Вот у :id тип ftAutoInc, ранее Integer
Пример в теме DataType, список типов в теме TFieldType type, он общий и для полей и для параметров.
Параметр должен быть того же типа, что и поле или совместимого.
Если же ftAutoInc меня не особо и волновал, то преобразование ftWideString в ftFixedChar поскольку это уже приводило к полной неработоспособности программы.
При это сначала они исправили только поля, а потом и параметры, но для этого им потребовалось более 4 лет.


 
kaif ©   (2008-09-09 15:31) [66]

MsGuns ©   (09.09.08 15:15) [58]
>kaif ©   (09.09.08 14:31) [44]

В глубоко научный спор не вмешиваюсь, а только спрошу - зачем, Ашот, ты упорно пытаешься закатить солнце вручную, т.е. пишешь код по созданию и инициализации параметров, ведь это требуется только в исключительных случаях и то, если ParamCheck := false, что, ИМХО, бывает нужным лишь в исключительных случаях ?
И еще вопрос - зачем эти спецсимволы в строке запроса ?


В том-то и проблема.
Вот взгляни на этот код.
Здесь я вообще ничего явно не задаю. Надеюсь на ParamCheck.
Получаю фигню.

procedure TSimpleReferenceForm.Button1Click(Sender: TObject);
begin
 with TADOCommand.Create(nil) do
 try
   Connection := MainData.Connection;
   ParamCheck:= TRue;

   CommandText :=
      "INSERT INTO GOODS(GOOD_NAME) VALUES (:NAME)"#13#10+
      "SELECT :ID = SCOPE_IDENTITY()";

   Parameters.ParamByName("NAME").Value := "Дядя Вася 8";
   Execute;
   if Parameters.ParamByName("ID").Value = Null then
     ShowMessage(Format("Значение выходного параметра Null]))
   else
     ShowMessage(Format("Значение выходного параметра ID = %s",
       [IntToStr(Parameters.ParamByName("ID").Value)]));
 finally
   Free;
 end;
end;


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

Почему обрамление транзакцией заставляет ADO работать так, как видится мне, а без этого обрамления я оказываюсь НЕПРАВ, а мой оппонент прав, говоря, что я зря в дизайн-тайме что-то указывал?

Вот пусть мой оппонент мне внятно объяснит этот момент.

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

Мне надоел этот стиль беседы.

У меня много работы и нет желания здесь опять с кем-то собачиться только потому что у того с культурой проблемы.
За обвинения во вранье человек так и не извинился. Ну и я извиняться не собираюсь за свои обвинения в том, что он дурно воспитан.

MsGuns ©   (09.09.08 15:15) [58]
>kaif ©   (09.09.08 14:31) [44]
И еще, чисто из собственного опыта - старт транзакции и ее завершение на клиенте не рекомендуется вкладывать в текст запроса - лучше это делать, используя TADOConnection.BeginTrans/CommitTrans|RollbackTrans - во-первых "прозрачнее" с т.з. кода, во-вторых в МсСкл нет понятия "транзакция" как самостоятельная величина (как в ИБ) и все они интерпретируются сервером применительно к соединению в целом - поэтому чтобы не запутаться в "ручных" транзакциях (в смысле где внешняя, а где внутренняя), лучше разруливать их в контексте не запроса, а всего соединения в целом.


За эту рекомендацию я очень тебе благодарен. Всегда хорошо услышать что-то дельное. Особенно человеку без опыта.


 
Ega23 ©   (2008-09-09 15:32) [67]

2 kaif:

Ашот, я тебе порекомендую не писать клиента с подключенным в дизайн-тайм коннекшеном. Многое тогда станет понятным.


 
kaif ©   (2008-09-09 15:34) [68]

sniknik ©   (09.09.08 15:20) [60]
> без того чтобы об этом было предупреждение в документации у меня нет.
вот именно поэтому я думал, что ты не читал документации, раз не знаеш об этом "предупреждении".


Приведи текст из Help по ADO Delphi, где об этом сказано. Если ты не врун, конечно.


 
app ©   (2008-09-09 15:40) [69]

Будете переругиваться - закрою тему.


 
kaif ©   (2008-09-09 15:40) [70]

sniknik ©   (09.09.08 15:25) [63]
> Хотя ты сейчас опять скажешь, что я вру.
а то. в чем то обязательно, вот например сейчас когда ты "все" знаеш объясни причем тут транзакция которая якобы помогла тебе вернуть параметр в [12].


Не якобы, а помогла.
Почему - не знаю.
И хочу как раз это обсудить.
Со вчерашнего дня, между прочим. Вместо этого вынужден доказывать тебе, что я не лжец. Надоело.

Ega23 ©   (09.09.08 15:32) [67]
2 kaif:

Ашот, я тебе порекомендую не писать клиента с подключенным в дизайн-тайм коннекшеном. Многое тогда станет понятным.


А я так и не делаю.

Я вообще не понимаю, о чем здесь спор идет.


 
MsGuns ©   (2008-09-09 15:47) [71]

[67]

+100
Я просто забыл отметить эту весьма существенную деталь

И еще вдовесок:
а) Тексты запросов лучше располагать в тексте константами. Если проект является очередным в серии приложений с одной и той же БД, стОит задуматься об унификации кода, вынеся все запросы в отдельный библиотечный модуль.
б) не следует "класть" много компонент - на форме (ДМ) должны быть только те, которые используется при дизайне решеток и не меняются в ран-тайме. Все "одноразовые" запросы на выборки-вставки-изменения-удаления строятся на динамически создаваемых и затем прибиваемых объектах
в) старайся максимально использовать возможности ADO, предоставляемые на клиенте (TCustomADODataSet) - это может существенно упростить логику и разгрузить сервер при поисках, сортировках, фильтрациях и т.д.
г) в особо критичных ситуациях "гридного" редактирования не ленись использовать TClientDataSet со всеми его вкусностями (в т.ч. "тонкой" технологией управления кэшированием-отсылкой изменений) - ADO в сеточной режиме ведет себя иногда весьма капризно (особенно в конкурентных соединениях)


 
sniknik ©   (2008-09-09 15:49) [72]

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

> Не якобы, а помогла.
> Почему - не знаю.
а ведь я объяснял... но видимо у тебя на меня игнор.


 
Ega23 ©   (2008-09-09 15:49) [73]


> ADO в сеточной режиме ведет себя иногда весьма капризно
> (особенно в конкурентных соединениях)


Нормально оно себя ведёт, LockType надо правильно выставлять...


 
MsGuns ©   (2008-09-09 15:49) [74]

По поводу кода в [66]
Ты внимательно почитал разницу между @@identity vs Scope_identity ?


 
MsGuns ©   (2008-09-09 15:52) [75]

>Нормально оно себя ведёт, LockType надо правильно выставлять...

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


 
sniknik ©   (2008-09-09 15:52) [76]

> Вот пусть мой оппонент мне внятно объяснит этот момент.
обьяснить как подозреваю ничего, никому нельзя... только если сам поймет.

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


 
kaif ©   (2008-09-09 15:54) [77]

Вместо таких фраз:

sniknik ©   (08.09.08 23:42) [13]
> Однако все равно не работает.
в коде нет указания типа параметра, а говоришь что он pdOutput.
как знал, что врешь.


нужно было сказать так:

Ашот, учти, что после рантайм присвоения свойству CommandText нового значения, несмотря на свойство ParamCheck=True, ADO (в случае с MSSQL как минимум) теряет Direction параметров (присвоенные до того) и их следует после такого присвоения еще раз устанавливать ручками принудительно. Это баг ADO. Тебе не повезло, что ты сразу на него наткнулся, пытаясь сделать первое в своей жизни редактирование справочника сервера MSSQL при помощи компонентов ADO.

Это был бы правильный ответ. И я никто бы не мучился и не обижался.

Кстати, я тебе признателен за идею использовать TADOCommand.


 
sniknik ©   (2008-09-09 15:54) [78]

> Причем тут тип блокировки - речь идет о невозможности явного управления порциями изменений (репликами) -
> когда надо писать не по одной записи, а несколькими логически связанными.
ltBatchOptimistic?


 
sniknik ©   (2008-09-09 15:58) [79]

> Это баг ADO.
это НЕ баг ADO, это стандартное поведение, имхо, для многих компонент (ParamCheck в BDE по крайней мере видел, чего зря оно там чтоли? зачем переключатель если нечего переключать?)


 
Ega23 ©   (2008-09-09 16:00) [80]


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


Locktype:=ltBatchOptimistic, вроде так пишется.


 
kaif ©   (2008-09-09 16:03) [81]

2 sniknik ©

Вот это работает. Это тот же самый текст, что "и для тупых", что не работал. Отличие я выделил жирно.

Теперь объясни.

procedure TSimpleReferenceForm.Button1Click(Sender: TObject);
var
P: TParameter;
begin
with TADOCommand.Create(nil) do
try
  Connection := MainData.Connection;

  P := Parameters.AddParameter;
  P.DataType := ftString;
  P.Direction := pdInput;
  P.Name := "NAME";

  P := Parameters.AddParameter;
  P.DataType := ftInteger;
  P.Direction := pdOutput;
  P.Name := "ID";

  CommandText :=
     "BEGIN TRAN"#13+
     "INSERT INTO GOODS(GOOD_NAME) VALUES (:NAME)"#13#10+
     "SELECT :ID = SCOPE_IDENTITY()"#13+
     "COMMIT TRAN";

  Parameters.ParamByName("NAME").Value := "Дядя Вася 12";

  Execute;
  ShowMessage(IntToStr(Parameters.ParamByName("ID").Value));
finally
  Free;
end;
end;


 
Anatoly Podgoretsky ©   (2008-09-09 16:09) [82]

> kaif  (09.09.2008 16:03:21)  [81]

Маленький совет, лучше не использовать #13#10 при объединение литералов, а добавлять пробел или в начале каждого литерала или в конце

" BEGIN TRAN"+
"   INSERT INTO GOODS(GOOD_NAME) VALUES (:NAME)"+
"   SELECT :ID = SCOPE_IDENTITY()"+
" COMMIT TRAN";


Меньше будет неожиданных проблем.


 
kaif ©   (2008-09-09 16:20) [83]

Между прочим такой вот код не работает.

procedure TSimpleReferenceForm.AddRecord(const AName: string);
var
 P: TParameter;
begin
 with TADOCommand.Create(nil) do
 try
   ParamCheck := True;
   Connection := MainData.Connection;
   CommandText :=
                  "SET NOCOUNT ON"#13+
                  "INSERT INTO GOODS(GOOD_NAME) VALUES (:NAME)"#13+
                  "SELECT :ID = SCOPE_IDENTITY()";

   Parameters.ParamByName("ID").Direction := pdOutput;
   Parameters.ParamByName("NAME").Value := AName;
   Execute;
   self.LastID := Parameters.ParamByName("ID").Value;
 finally
   Free;
 end;
end;


Причем ошибка возникает совершенно иного сорта. "Параметр задан неверно". Причем только если в тексте запроса две команды. Для одиночной команды проблем нет.

Для того чтобы он заработал нужно либо до присвоения текста CommandText создать рантайм все параметры, а после присвоения подправить Direction параметра с именем ID, либо же после присвоения CommandText сначала все параметры (их там 2 шт к этому моменту - я проверил) очистить методом Clear и заново создать рантайм, присвоив нужные Direction.

Я считаю это совершеннейшим багом.
У остальных может на этот счет быть свое собственное мнение.


 
kaif ©   (2008-09-09 16:24) [84]

Всем спасибо.


 
sniknik ©   (2008-09-09 16:29) [85]

> Вот это работает.
упс... точно работает. не ожидал.

> Теперь объясни.
не могу. буду смотреть.


 
Anatoly Podgoretsky ©   (2008-09-09 16:29) [86]

> kaif  (09.09.2008 16:24:24)  [84]

Это значит, что прощаешься? :-)


 
sniknik ©   (2008-09-09 16:31) [87]

> Причем ошибка возникает совершенно иного сорта. "Параметр задан неверно".
тип интеджер добавь, возможно проблема в этом.


 
kaif ©   (2008-09-09 16:41) [88]

Вот результаты моих исследований.

1. Парсер параметров ADO в определенных ситуациях портит значения Direction, но не портит значения типов (DataType) параметров, которые были установлены до присвоения CommandText.
2. Типы он определять в этих ситуациях сам не может, но и не портит, если они присвоены заранее. А вот Direction - портит, даже если заранее установлено верно.
3. Этот парсер ошибается не всегда, и лишь в каких-то случаях.

Рекомендации:
Можно положиться на то, что коллекцию параметров (количество и имена) после присовения CommandText парсер создаст правильно. Очищать их необязательно. Но при любых проблемах (если таковые возникли), присваивать как Direction, так и DataType принудительно после присвоения CommandText еще раз.

То есть вот минимальный текст:

 with TADOCommand.Create(nil) do
 try
   Connection := MainData.Connection;
   CommandText :=
                  "SET NOCOUNT ON "#13+
                  "INSERT INTO GOODS(GOOD_NAME) VALUES (:NAME)"#13+
                  "SELECT :ID = SCOPE_IDENTITY()";

   Parameters.ParamByName("NAME").DataType := ftString;
   Parameters.ParamByName("NAME").Direction := pdInput;
   Parameters.ParamByName("ID").DataType := ftInteger;
   Parameters.ParamByName("ID").Direction := pdOutput;

   Parameters.ParamByName("NAME").Value := AName;
   Execute;
   Result := Parameters.ParamByName("ID").Value;
 finally
   Free;
 end;


 
kaif ©   (2008-09-09 16:42) [89]

Anatoly Podgoretsky ©   (09.09.08 16:29) [86]
> kaif  (09.09.2008 16:24:24)  [84]

Это значит, что прощаешься? :-)


Ну я еще появлюсь. Вопросов еще много возникнет.
:)


 
Медвежонок Пятачок ©   (2008-09-09 16:47) [90]

по моим наблюдениям портит и то и другое.
я ввел командтекст с set :id = @@identity, пошел в редактор параметров
там автоматом появился инпут параметр типа стринг.
поменял его на аутпут интегер, выполнил запрос.

поменял командтекст на select :id = @@identity, пошел в редактор.
снова инпут стринг параметр.

ps метаданные сервера были доступны редактору в обоих случаях.

или после первой правки типов у меня не тот случай, когда параметр создан заранее?

надо бы глянуть в дфм что там


 
MsGuns ©   (2008-09-09 16:48) [91]

>> не по одной записи, а несколькими логически связанными.
>Locktype:=ltBatchOptimistic, вроде так пишется.

Эге ж, а теперь объясните мне пжлст каким раком мне указывать каждый раз размеры ентого самого "пакета", причем для разных таблиц - свой (при редактировании по технологии "мастер-детал") ?


 
Ega23 ©   (2008-09-09 17:05) [92]


> Эге ж, а теперь объясните мне пжлст каким раком мне указывать
> каждый раз размеры ентого самого "пакета", причем для разных
> таблиц - свой (при редактировании по технологии "мастер-
> детал") ?


Блин. Как-то можно было. Дома попытаюсь старые исходники поднять.
Но что-то такое делал.


 
kaif ©   (2008-09-09 17:07) [93]

2 sniknik ©

В любом случае я благодарен тебе за две вещи:

1. За то, что именно ты подсказал мне идею использовать SELECT :ID = или SET :ID = для того чтобы попытаться вернуть IDENTITY в качестве выходного параметра при вызове методами ExecSQL или Execute,  а не в виде результирующего набора методом Open. Так как использование метода Open для вставки данных делает текст программы некрасивым.

2. За то, что ты подсказал мне использовать ADOCommand.Execute вместо ADOQuery.ExecSQL.

Что касается парсинга параметров, то очевидно, что парсер ADO вряд ли в состоянии определять их типы, смотря в текст команды. Неприятно поразило то, что он может запортить их Direction. Я не привык к такому поведению компонентов, например, IBX так себя не ведут. Если Direction параметра в дизайн тайме выставлен, IBX не пытается его менять. Если что не так, он просто даст ошибку времени выполнения, а не будет вводить в заблуждение разработчика. Так что такое поведение ADO не назовешь стандартным. Что и вызвало массу затруднений, и похоже не только у меня. То, что помещение вызова BEGIN TRANS что-то изменило, возможно, просто случайность. Если разборщик пытается изменить Direction параметров, но при этом глючит, то тут можно ожидать все что угодно.

Еще я благодарен всем остальным участникам за полезную информацию разного рода, которую я по ходу почерпнул.

Что касается вопроса транзакций в ADO с сMSSQL, то я думаю открыть для этого отдельную ветку.


 
sniknik ©   (2008-09-09 17:14) [94]

> Теперь объясни.
с транзакцией сервер почемуто не возвращает коллекцию параметров, почему не знаю...

код в ADODB
procedure TADOCommand.AssignCommandText(const Value: WideString; Loading: Boolean);

....
           { Retrieve additional parameter info from the server if supported }
           Parameters.InternalRefresh;
           { Use additional parameter info from server to initialize our list }
          if Parameters.Count = List.Count then
             for I := 0 to List.Count - 1 do
             begin
               List[I].DataType := Parameters[I].DataType;
               List[I].Size := Parameters[I].Size;
               List[I].NumericScale := Parameters[I].NumericScale;
               List[I].Precision := Parameters[I].Precision;
               List[I].Direction := Parameters[I].Direction;
               List[I].Attributes := Parameters[I].Attributes;
             end
....


в Parameters.Count при запросе с транзакцией 0 (это список полученный с sql сервера), ясно что выделенное условие не выполнится т.к. в List.Count 2
а до этого в List распарсивается запрос (создаются параметры) и (в выделенном) переносятся/сохраняются значения, если они есть (сравнение по имени).
     NativeCommand := List.ParseSQL(Value, True);
     { Preserve existing values }
     List.AssignValues(Parameters);


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

и это в общем-то ничего не меняет в правиле, назначать свои значения только после инициализации. во избежание сюрпризов.


 
sniknik ©   (2008-09-09 17:18) [95]

> Эге ж, а теперь объясните мне пжлст каким раком мне указывать каждый раз размеры ентого самого "пакета", причем для разных таблиц -
> свой (при редактировании по технологии "мастер-детал") ?
"детайл" задавай запросом. а не по привычке как у таблиц в BDE.


 
sniknik ©   (2008-09-09 17:21) [96]

> 3. Этот парсер ошибается не всегда, и лишь в каких-то случаях.
судя по всему изза двойственности в определении, удалось получить с сервера или нет, лучше бы было меньше "авто" зато больше ясности (не удалось - значит не определено. и никаких гвоздей!).


 
kaif ©   (2008-09-09 18:48) [97]

2 sniknik ©

Вот теперь все стало ясно!
Спасибо за труды.

Я подозревал, что парсер в какой-то момент спрашивает параметры у сервера, но в код VCL за подробностями не лез.

Значит ошибка все же не в парсере ADO Delphi.
Просто сервер что-то не то возвращает в ситуации "SELECT :ID = " или провайдер не то доставляет.


 
sniknik ©   (2008-09-09 19:51) [98]

> Я подозревал, что парсер в какой-то момент спрашивает параметры у сервера, но в код VCL за подробностями не лез.
зачем подозревать? открытым текстом было сказано
sniknik ©   (09.09.08 11:42) [31]
> Там даже не баги, а урезанная реализация некоторых возможностей.
имел в виду именно баги, например был баг при внесении запроса частями через ADOQuery.SQL.Add(), чего по логике ADO делать было нельзя т.к. при внесении запроса определяются какието параметры, и определяются они на стороне сервера куда передается запрос, часть же всегда неправильна... кстати именно поэтому нужно подключение при разработке, можно конечно и без него и руками все устанавливать, толко основные претензии по этому поводу как раз в том что оно чегото не делает, а вот то что программист должен обеспечить какието условия для этого, это игнорируется.

думаешь я это просто так ляпнул, не подумавши? хотя, видимо так ты и думаешь, судя по ответу на это в [33].

> Просто сервер что-то не то возвращает в ситуации "SELECT :ID = " или провайдер не то доставляет.
наоборот, в этом случае он возвращает все, а вот в варианте с закрытием транзакцией почемуто не определяет не то что типов, а вообще параметров не распознает.


 
kaif ©   (2008-09-09 20:14) [99]

У тебя не иссякает желание говорить в таком тоне.
Хорошо.
Значит я тебя неправильно понял.

Но ты неверно говоришь здесь:

наоборот, в этом случае он возвращает все

Все-то он, может, и возвращает (в смысле правильное количество параметров), вот только Direction возвращает кривое для конкретно параметра ID. Если было бы не так, не пришлось бы столько мучиться из-за простой вещи.



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

Форум: "Базы";
Текущий архив: 2009.05.17;
Скачать: [xml.tar.bz2];

Наверх





Память: 0.83 MB
Время: 0.007 c
15-1237111075
Немо2
2009-03-15 12:57
2009.05.17
Что бы придумать, что бы не ездить на работу?


2-1238483163
AlexDan
2009-03-31 11:06
2009.05.17
Клик для кнопки


15-1236699497
Кое кто
2009-03-10 18:38
2009.05.17
Включить "Администратор"


2-1238565285
DmitriyR
2009-04-01 09:54
2009.05.17
массивы char и longword


3-1220965928
kaif
2008-09-09 17:12
2009.05.17
ADO Delphi и транзакции MSSQL





Afrikaans Albanian Arabic Armenian Azerbaijani Basque Belarusian Bulgarian Catalan Chinese (Simplified) Chinese (Traditional) Croatian Czech Danish Dutch English Estonian Filipino Finnish French
Galician Georgian German Greek Haitian Creole Hebrew Hindi Hungarian Icelandic Indonesian Irish Italian Japanese Korean Latvian Lithuanian Macedonian Malay Maltese Norwegian
Persian Polish Portuguese Romanian Russian Serbian Slovak Slovenian Spanish Swahili Swedish Thai Turkish Ukrainian Urdu Vietnamese Welsh Yiddish Bengali Bosnian
Cebuano Esperanto Gujarati Hausa Hmong Igbo Javanese Kannada Khmer Lao Latin Maori Marathi Mongolian Nepali Punjabi Somali Tamil Telugu Yoruba
Zulu
Английский Французский Немецкий Итальянский Португальский Русский Испанский