ADO компоненты CommandTimeout

У меня проблема с настройками тайм-аута выполнения запроса с помощью TADOQuery, TADOCommand или TADODataSet (я пробовал с каждым). У меня есть крошечное приложение, которое подключается к базе данных и периодически выполняет хранимые процедуры, которые в результате возвращают набор данных. Моя цель - постоянно поддерживать это приложение в сети, но моя проблема в том, что при потере соединения тайм-аут только что выполненной команды (через один из упомянутых компонентов) по умолчанию составляет 30 секунд. Я искал решение, но ничего не работает.Не могли бы вы дать мне совет, как установить CommandTimeout, например, на 5 секунд или лучше сказать, как изменить ADODB.pas для соблюдения моего собственного времени ожидания, пожалуйста?

Для этого было много "решений", например set DataComponent.Connection.CommandTimeout:= 1; но на самом деле ничего не работает. Я использую D2009, MSSQL2005 и соединение с компонентом данных создается в потоке динамически.

Последнее, что я пробовал это

// protected variable owned and created in the thread with its own connection
var Query_Object: TADODataSet; 

// connection timeout is set to 3 seconds
Query_Object.Connection.ConnectionTimeout := 3;
...

// this piece of code I'm calling periodically in the only one existing thread
...
SQL_Query := 'EXEC my_procedure_which_returns_dataset'

with Query_Object do
  begin
    Close;    
    CommandType := cmdText;
    CommandText := SQL_Query;
    CommandTimeout := 5;             // doesn't affect the timeout
    CursorLocation := clUseServer;   // let the dataset retreives prepared data
    Open;
  end;

// and here I need to get faster than in the default 15 seconds to let the user
// know that the reading takes more than mentioned 5 seconds
...

Большое спасибо:)

3 ответа

Решение

CommandTimeout включается, когда у вас есть длинные запущенные запросы. E сть CommandTimeout собственностью TADOConnection но это не работает. Вы должны использовать CommandTimeout из TADODataSet вместо.

Если сервер недоступен, в вашем вопросе говорится "соединение потеряно", вам необходимо указать ConnectionTimeout из TADOConnection составная часть. По умолчанию это 15 секунд, прежде чем контроль возвращается в ваше приложение.

Редактировать 1 Я думаю, что обнаружил ситуацию, когда CommandTimeout не работает. Я проверил это на действительно большом столе. Требуется несколько минут, чтобы вернуть все строки. Если моя хранимая процедура делает select * from BigTable Тайм-аут запроса никогда не происходит. По крайней мере, я не был достаточно терпелив, чтобы ждать. Но если запрос выглядит так select * from BigTable order by Col1 и нет индекса на Col1, CommandTimout работает как положено.

Разница между двумя запросами очевидна при запуске их в SSMS. Первый начинает немедленно возвращать строки, а второй должен "подумать" об этом, прежде чем он вернет строки. Когда SQL Server найдет нужные ему строки и начнет их возвращать, CommandTimeout не работает.

Если вы установите CursorLocation в clUseServer CommandTimeout будет работать как ожидалось для обоих запросов.

Следующее - то, что мы используем, чтобы установить тайм-аут на 300 для наших долгосрочных отчетов.

  //***** Fix setting CommandTimeOut. 
  //      CommandTimeOut "should" get the timeout value from its connection. 
  //      This is not supported in ADODB (using Delphi5)
  TADODataSet(qryReport).CommandTimeout := ADOConnection.CommandTimeout;

редактировать

Время выполнения следующей части кода на моей машине разработки истекло через 1 секунду.

  • Запрос имеет строку подключения к нашей производственной базе данных SQLServer.
  • Сценарий (пытается) работает в течение 10 секунд
  • Через одну секунду я получаю исключение TimeOut

Тестовое задание

procedure TForm1.btn1Click(Sender: TObject);
const
  SSQL: string =
    'DECLARE    @intLoop int '#13#10
    + 'SET @intLoop = 10 '#13#10
    + 'WHILE @intLoop > 1 '#13#10
    + 'BEGIN '#13#10
    + ' SELECT  @intLoop, GetDate() '#13#10
    + ' WAITFOR DELAY ''00:00:01'' '#13#10
    + ' SELECT  @intLoop = @intLoop -1 '#13#10
    + 'END ';
begin
  qry1.SQL.Text := SSQL;
  TADODataSet(qry1).CommandTimeout := 1;
  qry1.ExecSQL;
end;

Я всегда использовал следующий код для установки значения CommandTimeout в TADOQuery. Если вы измените имя класса, оно также должно работать и с другими.

type 
TADOQueryHack = class(TADOQuery);

...

TADOQueryHack(Qry).CommandTimeout := COMM_TIMEOUT;
Другие вопросы по тегам