Подтвердить что ты не робот

Как отменить длительную операцию базы данных?

В настоящее время работает с Oracle, но также потребуется решение для MS SQL.

У меня есть графический интерфейс, который позволяет пользователям генерировать SQL, который будет выполнен в базе данных. Это может занять очень много времени, в зависимости от поиска, который они создают. Я хочу, чтобы GUI/App реагировал во время этого поиска, и я хочу, чтобы пользователь мог отменить поиск.

Я использую тему рабочего рабочего.

Моя проблема в том, что когда пользователь отменяет поиск, я не могу прерывать вызов в базу данных. Он ждет, пока он не будет завершен, и затем он сможет опросить свойство "Отмена". Этот ресурс не только в базе данных, но и создает проблемы для моего кода.

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

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

Как я могу это сделать?

4b9b3361

Ответ 2

Если вы используете поставщик данных ADO.NET и SQL, посмотрите на метод SqlCommand.Cancel. Это делает то, что вы ищете. Тем не менее, он пытается отменить, и отмена может занять некоторое время. В основном, это до SQL Server, чтобы решить, когда предоставить запрос на отмену. Когда запрос отменяется, вы должны получить исключение SqlException, которое указывает, что операция была отменена пользователем. По-видимому, вы не хотите рассматривать это исключение как исключение и обрабатывать его специально, например, если SqlException вызван тем, что пользователь отменил операцию, просто проглотите его.

Ответ 3

Я также заметил, что command.Cancel() не отменяет команду. То, что сработало для меня, - это закрытие соединения (транзакция возврата, если вы используете ее), когда пользователь прерывается. Это приведет к возникновению исключения в фоновом потоке во время выполнения команды, поэтому вам придется поймать его и проверить свойство CancellationPending, а не реконструировать исключение в этом случае...

// When aborting
worker.CancelAsync();
command.Connection.Close();

// In your DoWork event handler
...
catch (Exception)
{
    if (worker.CancellationPending)
    {
        e.Cancel = true;
        return;
    }
    else
    {
        throw;
    }
}

// And in your RunWorkerCompleted event handler
if (e.Error == null && !e.Cancelled)
{
    ...
}

Ответ 4

Я уверен, что это возможно. Мы используем TOAD для Oracle и позволяем отменить длительные запросы как описанных здесь. Я не уверен, как они это делают, хотя.

Ответ 5

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

Ответ 6

Я думаю, что лучшее решение, похоже, убивает сеансы через таблицу мониторинга.

С Oracle вы можете сделать это, как говорит Burnsys

В Firebird 2.5 он будет выглядеть тот же

Я надеюсь, что что-то подобное существует в Ms SQL

Ответ 7

Если вы используете SQLCommand, вы можете попробовать называть его Cancel.

Ответ 8

Как насчет открытия нового подключения к базе данных, войдите в систему как sysdba и отправьте команду "ALTER SYSTEM KILL SESSION" sid, серийный номер "IMMEDIATE", определяющий SID процесса, который вы хотите завершить.

Чтобы получить sessionID: выберите sid из v $mystat, где rownum = 1

Чтобы получить Serial #: выберите sid, serial # из v $session, где sid =: SID

http://www.oracle-base.com/articles/misc/KillingOracleSessions.php

РЕДАКТИРОВАТЬ: идея WWW не для входа в систему как sysdba здесь: http://forums.oracle.com/forums/thread.jspa?threadID=620578

Ответ 9

Я пробовал как Отменить, так и закрыть с ADO 2.8 и SQLOLEDB или SQL Server родным клиентом. С помощью Отмена набор записей перестает извлекать данные, но на заднем плане чтение с сервера продолжается и потребляет память из приложения. В 32-битном приложении может случиться, что через несколько минут вы получите сообщение "из памяти". Когда я закрываю набор записей (или соединение, с или без отмены до), ADO 2.8 ждет, пока все записи не будут извлечены.

Я не знаю, улучшает ли ADO.NET, но я думаю, что неплохо следить за памятью и доступом к сети после Cancel/Close, чтобы убедиться, что ADO действительно перестает читать данные.

Ответ 10

KILL SESSION был для меня единственным способом отменить длительный запрос. Я использую управляемый оракул, и OracleCommand.Cancel() работает несколько раз, но обычно он не работает. Также OracleCommand.CommandTimeout не соблюдается в соответствии с моими тестами. Некоторое время назад, когда я использовал неуправляемый оракул, при условии, что мне удалось отменить команды, но не больше с управляемой. Любой способ убить сессию был единственным вариантом. Запрос выполняется не в потоке пользовательского интерфейса, а в отдельном потоке. Команда отмены отправляется из потока пользовательского интерфейса. Это немного сложнее, потому что приложение использует посредник, используя WCF, но в конце дня я убиваю сессию. Конечно, при выполнении запроса мне нужно найти и сохранить сеанс, чтобы при необходимости убить его. Есть много способов найти sid и serial #, чтобы убить сеанс оракула, и я хочу потратить ваше время на объяснение того, что вы уже знаете.

Ответ 11

Oracle представил ALTER SYSTEM CANCEL SQL в 18c. Вам нужно добавить какой-то комментарий с UID к вашему SQL, а затем искать его примерно так

SELECT S.SID||','||S.SERIAL#
                    FROM GV$SESSION S, V$SQL Q
                    WHERE S.USERNAME IS NOT NULL
                    AND S.STATUS = 'ACTIVE'
                    AND S.SQL_ID IS NOT NULL
                    AND Q.SQL_ID = S.SQL_ID
                    and sql_text like '%{queryId}%'

А затем запустите другую операцию из .NET ALTER SYSTEM CANCEL SQL 'SID, SERIAL'