|
|
su.dbms.sql- SU.DBMS.SQL ------------------------------------------------------------------ From : Andrey 2:5083/13.5 01 Apr 2001 13:03:40 To : All Subject : Дремина хитрость. --------------------------------------------------------------------------------
Hi! Drema*
> D> UPDATE ID_ SET @ID=ID=ID+1
> Дрема вот это и есть конкретное ламерство, тут тебя не сможет развести
>ни один,даже самый мудрый сервер. Потому как даже строчная блокировка не
>спасает от совершенно ненужной задержки пользователей работающих с вообще
>никак не пересекающимися таблицами.Дрема,ты сам не понятно зачем организовал
>узкое место в своей системе. Или же ты таким вот образом вылечил MS-SQL от
>необходимости при его спотыканиях запускать checkident :))))). Крут ты Дрема
>однако,кру-у-ут.:)))
D> :) Короче, умник ты наш, зайди вот сюда:
D> http://www.drema.c-net.ru/c_bd_ident.asp,
D> там специально для таких как ты все разжевано...
D> ЗЫ Подробнее объяснять тебе нет совершенно никакого желания.
Так как не у всех фидошников есть интернет то я все же слазил за Дреминой
хитростью и просто офигел от того как можно простое сделать сложным и
окончательно все запутать.
Дальше и до конца пошел Дрема.
В MSSQL есть возможность добавления в таблицу поля автоикремента - это всем
известно. о иногда возникают проблемы с таким подходом. апример, если на клиенте
нужно вставить строчку, а в этой таблице есть триггер, а в этом триггере есть
операторы, которое тоже вставляют в таблицу с identity. В этом случае задача
возврата значения identity клиенту становится уже не тривиальной.
После некоторых раздумий, я решил вообще не пользоваться свойством identity. Для
этого просто создал таблицу следующего содержания:
create table NSI_ID15dev5
(id_ int not null
)
insert iden values (0)
То есть создал свой счетчик, причем принял за основу, что во всех таблицах моей
БД первичный ключ будет генерироваться из этой таблицы. Ясно дело, что нужно это
делать с помощью процедуры или триггера. От триггера я сразу отказался, потому
что что-то лень писать на каждую таблицу по своему триггеру. Исходя из всего
вышеперечисленного решил при вставке всегда использовать процедуру.
---
Разберем это на примере таблицы клиентов:
create table client
(key_nsi int not null,
name varchar(100),
inn varchar(12),
adress varchar(100)
)
Чтобы не заморачиваться с написанием различных процедур вставки для каждой
таблицы, я постулирую следующее правило: Процедура вставки должна быть одна на
все таблицы, то есть универсальной. Исходя из этого теперь можно сформулировать
задачи этой процедуры и данные которые ей нужны для выполнения.
Входные данные:
[Имя таблицы] в которую нужно вставлять данные.
Выходные данные:
Первичный ключ вставленной записи
Здесь, как можно видеть, я опускаю необходимость иметь прочие данные для вставки
(например поля name, inn и т.п.). Дело в том, что если их тоже передавать в
процедуру, то будет невозможно написание одной универсальной процедуры, так как
количество полей в каждой таблице БД различно, а такой поддержки (переменного
числа параметров) еще в MSSQL v.7 нету (может и появится в 2000-ой версии).
Следовательно все поля кроме первичного ключа (key_nsi), должны иметь свойства
NULL, то есть поддерживать вставку пустого значения. екоторые типы данных не
поддерживают вставку значений NULL, например Boolean, тогда нужно применять
вставку по умолчанию (defaults, для boolean это должно быть равно "1" например).
Есть еще одна проблема с этим - если у вас будет стоять уникальный индекс по
полю, то нельзя будет вставить два раза NULL. В этом случае нужно в defaults
поля вставить случайное значение по умолчанию, например зависящее от даты.
Еще одно условие на структуру таблицы данных - название первичного ключа
(key_nsi) должно быть одинаково во всех таблицах.
[Имя таблицы] передается клиентом, то есть он должен знать в какую таблицу он
вставляет данные. Как лучше передавать имя таблицы? Я решил что просто текстовое
название передавать не стоит (из последующего ясно, что я захочу поменять имя
таблицы, не изменяя клиентской программы). В таком случае нужно передавать
какой-либо код таблицы, для этого создаем еще одну вспомогательную таблицу:
create table nsi_spravochnik
(key_nsi_spravochnik int not null IDENTITY(1,1),
table_name varchar(100) not null,
table_name_rus varchar(100) not null
)
То есть в этой таблице находится информация о всех таблицах в БД с русскими
именами. Имея такую таблицу мы можем на клиенте вывести диалог, который
предлагает выбрать таблицу с которой необходимо работать. о пока самым главным
для нас является то, что в поле key_nsi_spravochnik есть [номер] таблицы, а по
этому номеру таблицы мы можем получить и физическое название таблицы. Таким
образом мы определились с одним (и единственным) параметром у процедуры вставки
- это будет целое число однозначно определяющее таблицу.
Опишем примерную структуру процедуры вставки:
create procedure sp_InsertNewRecord
@KEY_NSI int=0 OUTPUT
-- При входе @KEY_NSI равен номеру справочника,
-- При выходе - новому первичному ключу
as begin
-- Получаем новый ID
UPDATE NSI_ID SET @KEY_NSI = ID_ = ID_ + 1
-- Определяем название таблицы в которую происходит вставка
declare @name_table varchar(100)
SELECT @name_table = table_name
FROM nsi_spravochnik
WHERE key_nsi_spravochnik = @KEY_NSI
-- Составляем оператор, для вставки в таблицу данных
declare @SQL varchar(1000)
SET @SQL = 'INSERT '+@name_table+' (KEY_NSI) VALUES ('+
convert(varchar(20),@KEY_NSI)+')'
-- Выполняем вставку
EXEC(@SQL)
end
Таким образом эта процедура будет работать на всех таблицах нашей БД.
Что требуется от клиента?
Клиентская программа (в моем случае написанная на Delphi) перед вставкой данных,
должна иметь код таблицы в которую нужно вставить данные. В связи с
особенностями моей программы, этот код мне всегда известен, причем он не "зашит"
в программу, а получается при выборе из таблицы nsi_spravochnik.
В Delphi я использую TDBGrid, который берет данные из TUpdateSQL. Я выбрал
объект TUpdateSQL потому, что в нем легко вставлять свои строчки данных, которых
нет на сервере. апример в TQuery такой возможности нет.
Еще существует одна проблема с обновлением после вставки/изменения/удаления.
Если я использую объект TUpdateSQL, то мне не надо обновлять весь курсор после
того произошло обновление таблицы, мне достаточно обновить только те строчки,
которые обновились.
Опишем действия на клиенте по вставке стоки в таблицу. Для этого договоримся,
что на данный момент объект TUpdateSQL отображает некий запрос по таблице,
key_nsi_spravochnik которой известен (допустим, что он равен "133"):
1.Вызов следующего оператора:
declare @KEY_NSI int
SET @KEY_NSI = 133
exec sp_InsertNewRecord @KEY_NSI = @KEY_NSI OUTPUT
SELECT @KEY_NSI
Результатом будет курсор, содержащий первичный ключ в таблице (допустим он равен
9876).
2.Вставка в TUpdateSQL пустой строки
3.Вызов на экран формы редактирования полей БД
4.Если пользователь подтвердил процесс вставки, то выполняем следующий оператор:
-- Определяем название таблицы в которую происходит вставка
declare @name_table varchar(100)
SELECT @name_table = table_name
FROM nsi_spravochnik
WHERE key_nsi_spravochnik = 133
declare @SQL varchar(1000)
SET @SQL='UPDATE'+@name_table+
' SET name=''Клиент'', inn=''123456789'',adress=''ул. Ленина д.1''
WHERE KEY_NSI = 9876'
EXEC (@SQL)
То есть на самом деле происходит редактирование, а не вставка. Если же
пользователь отказался от вставки, то пустая строчка должна быть удалена. Этот
процесс у меня тоже описан в процедуре, которую я здесь приводить не буду.
5.Данные из формы редактирования передаются в строку TUpdateSQL. (Заметьте, что
курсор как таковой не обновляется).
Такой подход решает еще одну проблему. Допустим у вас есть какой-то документ, а
у этого документа есть строчки. Другими словами есть две таблицы, связанные
отношением один-ко-многим. Для того, чтобы вставлять строки документа, нужно
знать значение внешнего ключа, который ссылается на таблицу документа, а так как
у нас уже есть пустая строчка в БД под этим ключом в главной таблице, то проблем
не возникает.
...PS...
а самом деле у меня сделано немного по другому и намного удобнее, но сама
стратегия точно такая-же.
---
* Origin: (2:5083/13.5)
Вернуться к списку тем, сортированных по: возрастание даты уменьшение даты тема автор
Архивное /su.dbms.sql/2764ac7494ad.html, оценка из 5, голосов 10
|