Форум: "Базы";
Текущий архив: 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 вся ветка
Форум: "Базы";
Текущий архив: 2002.01.14;
Скачать: [xml.tar.bz2];
Память: 0.57 MB
Время: 0.005 c