2015-05-19 2 views
0

Я разрабатываю приложение, использующее C#, часть которого будет загружать данные из моей базы данных SQL и отправлять на принтер. Когда печать будет завершена, она пометит данные как уже напечатанные, поэтому я не буду печатать одни и те же данные более одного раза.Как предотвратить дублирование загрузки из базы данных SQL несколькими клиентами?

Он отлично работает с одним компьютером/принтером, но что делать, если это приложение запускается на нескольких клиентских компьютерах? Кажется, что после того, как один клиент загрузит данные, но до того, как данные будут отмечены как завершенные, другой клиент будет считать, что он не загружен и не загружает одни и те же данные.

Есть ли способ предотвратить это? Я думаю, может быть, пометить данные при загрузке или убедиться, что только один запрос может быть выполнен одновременно?

Любые мысли или рекомендации в отношении возможного направления будут полезны.

Спасибо,

+3

отметьте его как «in progress», а не только логическое значение. –

+0

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

ответ

0

Я думаю, что вы на правильном пути. Либо измените ваше поле boolean/bit IsComplete на статус (с такими значениями, как «Новый», «Выполняется» и «Завершить») или добавьте новое поле бит IsInProgress. Затем загружайте только те, которые еще не являются InProgress или Complete.

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

+0

Как насчет двух одновременных соединений, участвующих в гонке, чтобы получить одну и ту же строку? Это должно быть усилено с учетом параллелизма. –

+0

Hi Pittsburgh DBA, я думаю, что вы говорите, это моя самая большая проблема сейчас, что бы вы сделали, чтобы предотвратить гоночные подключения от того же ряда? – user2612061

+0

Я бы рекомендовал сделать что-то вроде этого: обновить строки, которые вы хотите обработать, с помощью нового поля (возможно, BatchGUID) с guid/uniqueid и с помощью WITH (ROWLOCK), чтобы ни один другой процесс не мог его прочитать, пока вы его обновляете , Затем прочитайте эти строки с использованием одного и того же GUID и обработайте их. – jtower

0

Добавить колонку "PrintStatus".

  • Когда данные добавляются, он установлен (скажем) 1, «Не печатается».
  • Когда клиент запрашивает для данных для печати, только возвращать данные, где PrintStatus = 1
  • Когда клиент берет данные для печати, установите его (скажем) 2, «Быть ​​Printed»
  • Когда клиент заканчивает печать, установить статус на 3, «Печатный»
  • Если по какой-либо причине клиент не печатает данные, установите статус обратно в 1 (и PrintedAt обратно в нуль)

Это оставляет дворника вопрос, что произойдет, если печать не удалась, и этот факт не сообщается (потому что дворник вытащил вилку на сервер. Не смейтесь, это произошло здесь, хотя это был технический ремонт парень, который на самом деле должен был знать лучше) Если вы беспокоитесь о таких вещах:.

  • Добавить некоторую форму времени регистрации, например, столбец «PrintedAt» , который устанавливается равным null, когда для параметра PrintStatus установлено значение 1, и установите значение getdate(), когда установлено значение PrintStatus 2. Боковое преимущество: теперь вы знаете, когда (или, возможно, «какая печать») были напечатаны ваши данные!
  • PrintStatus устанавливается в 3 раза при печати. Таким образом, если PrintStatus = 2, печать имеет (до сих пор) значение getdate() - PrintedAt для запуска.
  • Регулярно выполняйте регулярные расписания (рабочие задания SQL-агента). Он проверяет таблицу и обнаруживает ли какие-либо строки с PrintStatus = 2, где столбец PrintedAt больше вашего допуска (1 день? 2 часа?). Любые такие строки «откатываются», устанавливая PrintStatus в 1 и PrintedAt равными null.

Это может показаться излишним, но вы расскажете мне, насколько важны для вас согласованные данные.


Добавлено:

Да, параллелизм и последовательность является проблемой. Хотя вы можете общаться с BEGIN TRANSACTION ... COMMIT/ROLLBACK, я предпочитаю использовать силу неявных транзакций. Вот один из способов:

Это создает таблицу тестирования с некоторыми данными:

-- Set up test data 
CREATE TABLE Test 
(
    Data   int  not null identity(1,1) 
    ,PrintStatus tinyint not null 
    ,PrintedAt datetime null 
) 

INSERT Test (PrintStatus) values 
(1),(1),(1),(1),(1),(1),(1),(1),(1),(1),(1) 

SELECT * 
from Test 
GO 

Этот бит содержит оператор UPDATE, который обновляет все строки, которые нужно распечатать, и используется предложение OUTPUT для хранения этих элементов в временная таблица. Содержимое этой таблицы затем возвращается в процедуру печати.

-- First pass: Get everything to pring 
DECLARE @ToBePrinted table 
(Data int not null) 


UPDATE Test 
set 
    PrintStatus = 2 
    ,PrintedAt = getdate() 
output inserted.Data into @ToBePrinted (Data) 
where PrintStatus = 1 


SELECT Data PrintThese 
from @ToBePrinted 


SELECT * from Test 

GO 

Здесь мы добавляем еще три строки данных, а затем снова запускаем процедуру. Старые предметы не выбраны, новые.

-- Second pass: Add three new items, they (and only they) get selected 
INSERT Test (PrintStatus) values 
(1),(1),(1) 

SELECT * 
from Test 


DECLARE @ToBePrinted table 
(Data int not null) 


UPDATE Test 
set 
    PrintStatus = 2 
    ,PrintedAt = getdate() 
output inserted.Data into @ToBePrinted (Data) 
where PrintStatus = 1 


SELECT Data PrintThese 
from @ToBePrinted 


SELECT * from Test 

Это работает из-за свойств ACID SQL Server - в частности, I). После того, как одно из выражений существа изменяет содержимое таблицы, ни один другой оператор не может получить доступ к изменяемым данным до тех пор, пока выполнение инструкции не будет завершено. (Если вы не настолько глупы, чтобы использовать NOLOCK. Не используйте NOLOCK.)

+0

Вы должны добавить защиту от одновременных запросов. В этом случае в решении не указывается, как обеспечить, чтобы один и только один процесс мог «собирать данные для печати». Как написано, в этом есть слабость параллелизма. –

+0

Спасибо за ваш ответ! Теперь я беспокоюсь, что, скажем, клиент A возьмет строку «Не печатал», а перед клиентом A отметьте эту строку как «Напечатанный», клиент B запускает и захватывает одну и ту же строку? Является ли способ в SQL отмечать эту строку при выборе? Я читал некоторую документацию по «select ... for update». Но, похоже, это не мешает «выбрать» – user2612061

+0

. Я добавил один способ сделать это с кратким объяснением. Для полного понимания, google «кислота базы данных» и прочитайте несколько статей bazillion по этой теме. (Выполнение этого стоит вашего времени!) –