Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > Delphi: Базы данных и репортинг > блокировка записи в таблицу MS SQL


Автор: Гость_BasilL 22.6.2005, 20:12
Требуется на некоторое время запретить запись в таблицу MS SQL. При этом чтение разрешается.
Доступ через SDAC v3.

Подробнее: есть некая таблица "CurrentDate". В ней хранится рабочая дата.
На рабочих местах клиентов в глобальной переменной хранится "своя" дата - у разных клиентов она может быть разной.
При определенных условиях у конкретного клиента возникает необходимость в таких действиях:
1) сначала записать в таблицу на серваке "свою" дату (так, чтобы транзакция завершилась и сервер принял запись и увидел изменение)
2) после этого начинается новая транзакция (где и производится все, что клиенту надо), по окончании этой транзакции, дата в таблице на сервере снова возвращается та, которая была.

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

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

p.s. Объединять транзакции и вставлять что-то вроде "UPDLOCK" не подходит.
В BDE был предусмотрен монопольный доступ и Table.Lock. Нет ли чего-нибудь похожего в связке SDAC - MS SQL?

Вроде бы, более-менее понятно описал суть проблемы.

Заранее, спасибо.

Автор: Vit 22.6.2005, 23:31
SET TRAN ISOLATION LEVEL SERIALIZABLE

Автор: BasilL 23.6.2005, 19:18
To Vit
Цитата(Vit @ 22.6.2005, 23:31)
SET TRAN ISOLATION LEVEL SERIALIZABLE


Так не прокатит. Нелься создавать дополнительную транзакцию - в это время проходит другая транзакция, а пока она не завершена, новую я стартовать не могу, а применить "SET TRAN ISOLATION LEVEL SERIALIZABLE" к запущенной на шаге 2) транзакции тоже нельзя - это ничего не даст - таблица, запись в которую надо блокировать в ней не задействована. К тому же транзакция на шаге 2) не использует ХП, а прописать "SET TRAN ISOLATION LEVEL SERIALIZABLE" в компоненты MSTable и MSQuery от SDAC вроде-бы некуда.

Вот кусок кода, когда отрабатывает транз. на шаге 2):



Код

    try
        ConnectionData.ConnectionDataModule.ServerConnection.StartTransaction;

        if (VirtualTable1.State = dsInsert) or (VirtualTable1.State = dsEdit) then
                VirtualTable1.Post;
        if (VirtualTable2.State = dsInsert) or (VirtualTable2.State = dsEdit) then
                VirtualTable2.Post;

        VirtualTable1.First;

        if Check_mpz_kol() then
                begin
                if ConnectionData.ConnectionDataModule.ServerConnection.InTransaction then
                ConnectionData.ConnectionDataModule.ServerConnection.Rollback;
                Status.Caption := 'Документ не проведен.';
                exit;
                end;
        
        MsTable1.Append;

        MsTable1.FieldValues['RecID'] := RecID;
        MsTable1.FieldValues['НомерДокумента'] := 
              VirtualTable1.FieldValues['НомерДокумента'];

       ...

        btnPrint.Enabled := true;

        ConnectionData.ConnectionDataModule.ServerConnection.Commit;
        Status.Caption := 'Документ проведен.';

        exept
        ...
        end;
       



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

Автор: Vit 23.6.2005, 19:47
Хм... тогда только hint на запросах ставить... больше по-моему никак... И вообще такие действия должны ИМХО быть в пределах одной транзакции...

Цитата
В BDE был предусмотрен монопольный доступ и Table.Lock. Нет ли чего-нибудь похожего в связке SDAC - MS SQL?


Открыть таблицу запросом:

Код

Select * From Table with (tablockx)



Автор: BasilL 23.6.2005, 20:01
To Vit
Цитата(Vit @ 23.6.2005, 19:47)
тогда только hint на запросах ставить


Так вот, нету у меня в этой транзакции запросов, не нужны они.
Если только специально для хинт-а какой-нибудь в начале транзакции запускать... типа Select * from <таблица_с_датой> with (tablockx). А? Как-то не очень расиво получается.

Тут в одной статейке только что прочитал, что есть такая хп - sp_getapplock ("...Смысл ее в следующем – блокировка произвольного ресурса...").
Нельзя ли это как-то применить?

Автор: Vit 23.6.2005, 20:21
Цитата(BasilL @ 23.6.2005, 11:01)
Так вот, нету у меня в этой транзакции запросов, не нужны они.



Не бывает транзакций без запросов... в отношении SQL Server... Он просто не позволит делать ничего кроме запросов. Даже если ты открываешь таблицу каким-то компонентом типа Table в отличие от парадокса реально выполняется запрос типа Select * From Table, поэтому замени свои компоненты Table на запросы и используй блокировки.

Автор: BasilL 23.6.2005, 20:44
Я понимаю, что по-сути, нет транзакции без запросов, но дело не в этом.
Нельзя заменить Table на Query, я не смогу написать SQL.Text типа "insert into ... from... , поскольку from-то как раз и нету. В транзакции я переписываю некий набор записей из компонента TVirtualTable в TTable. TVirtualTable - это виртуальная таблица из набора SDAC (поэтому его и выбрал), на сервере этих данных нет и быть не может, они появятся там только после Table.post.
Вот и выходит, что нельзя все это описать запросом.

p.s. Insert ... values тоже не подойдет.

Автор: Vit 23.6.2005, 21:30
Ничего не понял... Извини я не знаком с этим пакетом... Не понимаю что за виртуальные таблицы... Для их осуществления можно использовать View, temp таблицы и т.п. И любой компонент Table абсолютно эквивалентен live запросу Select * From Table
Добавлено @ 21:35
Кажись понял... Это решаемо при помощи запросов, псевдокод такой:

Код

query.sql.text:='Select top 1 * From Table with (tablockx)'; //это таблица в которую будешь писать
query.open;
while not VirtualTable.eof do
  begin
    query.append; //записываем в Select запрос как в таблицу
    query.fieldbyname('чего-то там').value := VirtualTable..fieldbyname('чего-то там').value; 
    query.fieldbyname('чего-то там2').value := VirtualTable..fieldbyname('чего-то там2').value; 
    query.fieldbyname('чего-то там3').value := VirtualTable..fieldbyname('чего-то там3').value; 
    query.fieldbyname('чего-то там4').assign(VirtualTable..fieldbyname('чего-то там4')); 
    query.post;
    VirtualTable.next;
  end;

Автор: BasilL 24.6.2005, 12:36
2Vit

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

Объясню смысл: так, как ты написал (или как у меня сейчас, похоже) в таблицу я добавляю запись (записи). Перед этим запускается функция (if Check_mpz_kol() then ... - см. код), в которой сначала в таблицу <CurrentDate> пишется определенная дата, затем делается запрос к серверу, который возвращает определенный набор данных (они зависят от даты), эти данные проверяются, и если они удовлетворяют определенным условиям, функция возвращает true, и выполнение транзакции продолжается (тут и начинается перенос данных из виртуальной таблицы, который ты описал), если нет - откат. Так вот, таблицу с датой и надо залочить на время выполнения функцией проверки, точнее даже достаточно на время получения данных, которые проверяем - чтобы быть уверенными, что эти данные соответсвуют установленной дате. После проверки таблицу желательно разблокировать, чтобы не держать других клиентов - проверка идет быстро, а вот до конца транзакции еще долго.
А если я в начало основной транзакции (где переносятся записи из вирт. табл.) добавлю выполнение запроса типа "select * from CurrentDate with (tablelockx)", то, конечно, нужного результата добьюсь, но буду долго держать таблицу блокированной - это нежелательно.
А вставить такой запрос в процедуру проверки не получиться - сервер не даст запустить вторую транзакцию, в это время уже есть активная транзакция; на всякий случай пробовал - сервак, ессно, ругается: user transaction already in progress.

з.ы. логика формирования проверяемых данных организована на сервере, перенести ее на клиента нельзя, поэтому сделать что-то вроде TQuery с параметром, в который и ложить дату не выйдет.

Странная, наверное, у меня ситуация. А что делать?

Автор: Vit 24.6.2005, 15:13
Мдя... даже не знаю... Может над архитектурой подумать?

Автор: BasilL 25.6.2005, 12:33
Вот и я не знаю.
А может быть лучше создать некий пользовательский ресурс и блокировать его sp_getapplock?

Автор: Vit 26.6.2005, 03:45
Похоже на то

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)