Страницы: 1 2 вся ветка
Форум: "Базы";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.01.14;
Скачать: [xml.tar.bz2];




Вниз

Вставка записей в 2 таблицы 


Shoo   (2001-12-13 13:20) [0]

Мастера, интересует такая вещь: имею БД на MS SQL 7.0, использую ADO, имеется 2 таблицы, KatNakl и SpNakl, связанные таким образом: SpNakl.cNakl -> KatNakl.NRec. По нажатию кнопки хочу добавить данные в обе таблицы, у TADOQuery использую свойство SQL для формирования запроса на добавление и метод ExecSQL для выполнения запроса, примерно так:

procedure TForm1.Button1Click(Sender: TObject);
var mycNakl: longint;
begin
mycNakl := 0;
ADOQuery1.SQL.Clear;
with ADOQuery1.SQL do
begin
Add("insert into katnakl (datenakl, smena, priznak)");
Add("values (""10.12.2001"", 1, ""1"")");
end;
ADOQuery1.ExecSQL;

//как здесь получить новое значение поля NRec в KatNakl,
//записав его, скажем в mycNakl,
//чтобы подставить его в cNakl таблицы SpNakl?

ADOQuery1.SQL.Clear;
with ADOQuery1.SQL do
begin
Add("insert into spnakl (cnakl, name)");
Add("values (" + IntToStr(mycNakl) + ", ""QQQQQQQ""");
end;
ADOQuery1.ExecSQL;
end;
end;

И вообще, правильным ли путем я иду? Как лучше вставлять данные в моем случае? Заранее благодарен.



neXt   (2001-12-13 13:31) [1]

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



Delirium   (2001-12-13 13:43) [2]

Существует специальная функция для генерации уникальных идентификаторов
NEWID тип uniqueidentifier, это значение каждый раз новое и абсолютно уникальное. На мой взгляд исользование таких идентификаторов - самый грамотный подход.



Shoo   (2001-12-13 14:06) [3]

2neXt:
Увы, для меня это все еще не так просто. Если несложно, на основе моего примера, приведите, плз, очень сжато, примерный текст хранимой процедуры, которая будет этим заниматься и как ее потом корректно вызывать? Спасибо.



Shoo   (2001-12-13 14:15) [4]

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



neXt   (2001-12-13 14:17) [5]

Дай полный список полей обоих таблиц с типами



Shoo   (2001-12-13 14:29) [6]

2neXt:
Да, пожалуйста:

Table KatNakl:
Fields
NRec - numeric (primary key)
DateNakl - DateTime
Smena - int
Priznak - char

Table SpNakl:
Fields
NRec - numeric (primary key)
cNakl - numeric (связана с KatNakl.NRec)
BarCod - numeric
Name - char
Kol - numeric

Если есть необходимость, типы полей можно изменить, БД пока пуста.



neXt   (2001-12-13 14:51) [7]

технические подробности:
Ключ NRec в таблицах стоит как IDENTITY ? или его значение нужно наигрывать перед каждым добавлением



Shoo   (2001-12-13 14:59) [8]

Да, как IDENTITY.



neXt   (2001-12-13 15:05) [9]

-- Ну позно, я написал уже для простого поля, но поправить не сложно
-- не проверял так что это только пример
----------------------------------------------------------------
if object_id("KatNakl_SpNakl_Insert") is not null
drop proc KatNakl_SpNakl_Insert

GO
create proc KatNakl_SpNakl_Insert
@KatNakl_NRec numeric = null output, --Выходной параметр идентификатор KatNakl
@SpNakl_NRec numeric = null output, --Выходной параметр идентификатор SpNakl
-- Значения полей таблицы KatNakl
@DateNakl DateTime ,
@Smena int ,
@Priznak char ,
-- Значения полей таблицы SpNakl
@BarCod numeric ,
@Name char ,
@Kol numeric
as
declare @RetVal int

select @RetVal = 0 -- Если всё выполнилось удачно , то вернёт 0

-- Либо всё - либо ничего !!!
begin tran save tran TRAN_INSERT_KatNakl_SpNakl

-- Найдём новое не занятое значение первичного ключа
select @KatNakl_NRec = isnull( max(NRec) + 1 , 1 )
from KatNakl

-- Добавим запись в KatNakl
insert into KatNakl
(
NRec ,
DateNakl,
Smena ,
Priznak
)
select
@KatNakl_NRec,
@DateNakl ,
@Smena ,
@Priznak

-- Проверим добавилось ли?
if @@rowcount <> 1
begin
-- Код оибки добавления записи в
-- первую таблицу (можно выработать
-- свою систему нумерации ошибок)
@RetVal = 1000
-- Отменяем неудачное действие
rollback TRAN_INSERT_KatNakl_SpNakl
-- И на выход
goto EXIT_OFF
end

-- Найдём новое не занятое значение первичного ключа
select @SpNakl_NRec = isnull( max(NRec) + 1 , 1 )
from SpNakl

-- Если все хорошо то добавляем запись во вторую таблицу
insert into SpNakl
(
NRec ,
cNakl ,
BarCod ,
Name ,
Kol
)
select
@SpNakl_NRec ,
@KatNakl_NRec,
@BarCod ,
@Name ,
@Kol

if @@rowcount <> 1
begin
-- Код оибки
@RetVal = 1001
-- Отменяем неудачное действие
rollback TRAN_INSERT_KatNakl_SpNakl
-- И на выход
goto EXIT_OFF
end

EXIT_OFF:
commit tran
return @RetVal
GO



neXt   (2001-12-13 15:10) [10]

я по ADO не спец, могу сказать как из стандартной TQuery вызывать, уверен что один фиг



Shoo   (2001-12-13 15:14) [11]

2neXt:
Спасибо, постараюсь проверить в течение ближайшего времени...



Shoo   (2001-12-13 15:15) [12]

Да, кстати, как вызвать-то? А в ADO, действительно, все будет аналогично.



NewComer   (2001-12-13 15:21) [13]

вот я подумал.....
использование identity влечет за собой проблеммы подобного рода.
по-моему все ж лучше использовать в качестве идентификаторов GUID и получать их функцией NewID, будешь знать значение идентификатора перед вставкой. Тут NexT написал целую процедуру (и не лень было ? :-) ), я предлагаю в ней поставить уровень изоляции для транзакции SERIALIZABLE, чтобы небыло накладок при повторном селекте из первой таблицы.



Delirium   (2001-12-13 15:25) [14]

Согласен с NewComer, то, что предлагает neXt - в общем-то устаревший подход, а GUID - решает кучу проблем.



neXt   (2001-12-13 15:35) [15]

В Query.SQL пишем
------------------------------------------------
declare @KatNakl_NRec numeric,
@SpNakl_NRec numeric,
@RetVal int

exec @RetVal = KatNakl_SpNakl_Insert
@KatNakl_NRec = @KatNakl_NRec ,
@SpNakl_NRec = @SpNakl_NRec ,
@DateNakl = :DateNakl ,
@Smena = :Smena ,
@Priznak = :Priznak ,
@BarCod = :BarCod ,
@Name = :Name ,
@Kol = :Kol

select @KatNakl_NRec As KatNakl_NRec_NewID,
@SpNakl_NRec As SpNakl_NRec_NewID ,
@RetVal As ErrorCode
-----------------------------------------------
Нужно ещё добавить в Query.Fields три поля поля:
KatNakl_NRec_NewID
SpNakl_NRec_NewID
ErrorCode
,а в окошечке свойства Query.Params появятся все параметры указанные с двоеточием в первом символе (см Query.SQL)
а именно
:DateNakl
:Smena
:Priznak
:BarCod
:Name
:Kol
у каждого из них нужно выстовить (обязательно) свойство DataType
--------------------------------------------------------
это почти всё, НО
нужно ещё передать из кода Delphi значения параметров чтобы произошло добавление записей
это делается так
когда нужно добавить запись выполняется следующий код
///////////////
Query.Params[0].AsDataTime := (*DateNakl - значение*);
Query.Params[1].AsInteger := (*Smena - значение*);
Query.Params[2].AsString := (*Priznak - значение*);
Query.Params[3].AsFloat := (*BarCod - значение*);
Query.Params[4].AsString := (*Name- значение*);
Query.Params[5].AsFloat := (*Kol - значение*);
Query.Open;
/////////////////////
далее можно проверить добавилась ли запись взглянув в поле
Query.FieldByName("ErrorCode").AsInteger
если там 0 - всё окей если нет то надо думать
идентификаторы добавленных записей доступны соответственно из полей
Query.FieldByName("KatNakl_NRec_NewID").AsInteger
Query.FieldByName("SpNakl_NRec_NewID").AsInteger
если надо



neXt   (2001-12-13 15:36) [16]

устаревший , но работающий



neXt   (2001-12-13 15:41) [17]

кстати GUID многого не позволяет
например (это не относится к данному случаю) при использовании IDENTITY или его заменителя как в том решении что выше, есть возможность использовать "маленькие ID-шники" закрытые диапазоны идентифокаторов , например для дистрибутивных данных



neXt   (2001-12-13 15:47) [18]

кроме того GUID не поддерживается некоторыми SQL - платформами , для меня это существенное ограничение



Delirium   (2001-12-13 15:50) [19]

"закрытые диапазоны идентифокаторов" А часто-ли это надо? Если и надо, то лучше создать дополнительное поле с псевдоидентификатором, а опираться всё равно на GUID. Если для каждой пары (тройки) таблиц присать свою процедуру - вероятность ошибки вырастает! Кроме того - как быть с удалёнными серверами? - придётся создавать глобальные хранилища идентификаторов, создавать систему рассылки и т.п. А GUID-метод, не мене "работающий" но гораздо более удобный, да и индексация по uniqueidentifier в MSSQL оптимизирована.



Delirium   (2001-12-13 15:58) [20]

"GUID не поддерживается некоторыми SQL - платформами" - Shoo - же работает с MSSQL :)



neXt   (2001-12-13 16:11) [21]

GUID нельзя сравнивать , даже произносить в слух не рекомендуется
что касается индексирования полей uniqueidentifier то это тоже самое что и bigint преимуществ никаких (ещё бы они не индексировались они же для ID придуманы!)



NewComer   (2001-12-13 16:19) [22]

А при чем тут сравнение идентификаторов?
первичные ключи не должны обладать такой функциональностью.
Я конечно сомневаю в эффективности индексов по ним .. но все же.
Делать то кластерный индекс по ним тебя никто не заставляет.



Delirium   (2001-12-13 16:21) [23]

"GUID нельзя сравнивать" ты имеешь в виду сортировку?
А с какой стати сортировка должна сториться по идентификаторам?
Операции с идентификаторами "=" и "<>" больше и не надо, это вредно!



neXt   (2001-12-13 16:21) [24]

ЧЕСНО ГОВОРЯ У меня не 100 процентных аргументов против uniqueidentifier только вот работать с ним не привычно и не удобно



neXt   (2001-12-13 16:23) [25]

не ну последовательность заполнения таблицы



Delirium   (2001-12-13 16:25) [26]

"не привычно" - возможно, "не удобно" - предрассудки



neXt   (2001-12-13 16:26) [27]

и ещё если уж есть поля с каким-то типом то значит в процедурах будут применятся переменные с этимже типом, а как GUID"ом заменить выражение
exec Procedure_
@ID = @ID out
if @ID = 0
begin
--- то-то и то-то
end



neXt   (2001-12-13 16:27) [28]

причем null , допустим уже занят каким-то смыслом



Delirium   (2001-12-13 16:36) [29]

Хм, а в какой ситуации идентификатор вообще может содежать значения типа Null или просто 0 ? Это уже не идентификатор а просто поле с цифрами получается :)



NewComer   (2001-12-13 16:40) [30]

to NeXt:
не должен null быть занят каким то смыслом, null он и в Африке null.
Ну а если уж занят, то {00000000-0000-0000-0000-000000000000} вот тебе и обнуленный идентификатор.



Delirium   (2001-12-13 16:47) [31]

Совсем упустил из виду "последовательность заполнения таблицы" это нонсенс, по идентификаторам определять прорядок занесения информации!



neXt   (2001-12-13 16:53) [32]

значение null принемет не поле а переменная, например
select @ID = ID from ...
если там нет не одной пожходящей записи



neXt   (2001-12-13 16:56) [33]

чесно говоря, рассматривая выражения вида
select @ID = isnull(ID ,{00000000-0000-0000-0000-000000000000})
и
select @ID = isnull(ID ,0)
моё предпочтение остаётся со старым добрым numeric или int



NewComer   (2001-12-13 17:00) [34]

NeXt! сдавайся! :-)))))
очень много аргументов в Пользу Guid. И вообще.. каждый пишет так, как ему удобней. Но сдаеться мне, что корни identity растут с аксесса, с однопользовательской системы.



Delirium   (2001-12-13 17:01) [35]

На самом деле можно просто:

Declare @I uniqueidentifier
select @I=null



neXt   (2001-12-13 17:03) [36]


Совсем упустил из виду "последовательность заполнения таблицы" это нонсенс, по идентификаторам определять прорядок занесения информации!

речь не о сортировке или явного определения последовательности, а вот о чем
при работе со временными таблицами (или с разбитыми по @@spid)
1.прошёл инсерт
2.в max(ID) мой только что внесённый ID-шник
удобно, особенно если инсерт - это процедура откуда ID ещё надо тянуть



NewComer   (2001-12-13 17:05) [37]

select @ID = isnull(ID ,{00000000-0000-0000-0000-000000000000})
кстати сие выражени попахивает потенциальной ошибкой. ГЫ.



neXt   (2001-12-13 17:07) [38]

не не сдамся, я не вынужден писать не на одном MSSQL и выгрузка скажем в text или чего доброго в Sybase для полей GUID оборачивается серьёзным геморроем



NewComer   (2001-12-13 17:07) [39]

"1.прошёл инсерт
2.в max(ID) мой только что внесённый ID-шник
удобно, особенно если инсерт - это процедура откуда ID ещё надо тянуть"
Необходимо отучаться от "однопользователького" метода мышления. ГЫ



neXt   (2001-12-13 17:08) [40]

выше: "вынужден"



neXt   (2001-12-13 17:09) [41]

не ну чем тогда GUID лучше timestamp тоже не с чем не совместим и индексируется на зашибись



neXt   (2001-12-13 17:10) [42]

или тоже устаревший подход



Delirium   (2001-12-13 17:10) [43]

Это изначально "кривой" метод и естесвенно, прямо реализовать его с uniqueidentifier не получится, однако есть же триггера :)



Delirium   (2001-12-13 17:12) [44]

"чем тогда GUID лучше timestamp тоже не с чем не совместим и индексируется на зашибись" - вернёмся к удалённым серверам :)



NewComer   (2001-12-13 17:13) [45]

timestamp - вообще специфичная штука, и не имеет никакого отношения к uniqueidentifier, тем более этот тип нельзя использовать для ключей.



neXt   (2001-12-13 17:15) [46]

в борьбе за отказ от "однопользовательского" мышления , так же, предлягаю отказаться от транзакций, и прочих прежитков "тяжёлого прошлого" вроде блокировак, а ещё ролей , пользователей и технологии "клиент-сервер"



NewComer   (2001-12-13 17:15) [47]

Ой.. насчет timestamp я погарячился..... :-)



neXt   (2001-12-13 17:21) [48]

timestamp нормально подходит на роль ключа уж во всяком случае ни чем не хуже guid"а я не защищаю его, только как альтернатива, а сточки зрения разработчика эти типы вообще отличаются только алгоритмом генерации и размером



Delirium   (2001-12-13 17:26) [49]

В timestamp гарантированая уникальность только в составе одной машины!
И это лишает его права быть глобальным идентификатором. И вообще это всё уже давно флейм - жизнь нас рассудит :)



NewComer   (2001-12-13 17:27) [50]

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




Страницы: 1 2 вся ветка
Форум: "Базы";
Поиск по всему сайту: delphimaster.net;
Текущий архив: 2002.01.14;
Скачать: [xml.tar.bz2];




Наверх





Память: 0.83 MB
Время: 0.055 c
1-42436           Yakudza               2001-12-24 15:02  2002.01.14  
Редактор SQL запросов.


14-42559          fliz                  2001-11-19 10:43  2002.01.14  
Монитор полетел.


1-42474           fliz                  2001-12-25 12:55  2002.01.14  
Запись в поток (SaveToStream) перестала работать после переноса проги на Вин2000.


1-42425           Kevin                 2001-12-26 15:38  2002.01.14  
Очень срочно надо


6-42522           Polevi                2001-10-17 11:45  2002.01.14  
С мольбой о помощи взываю!!!