2013-06-25 2 views
2

Я пытаюсь прочитать только первую строку из расширенной хранимой процедуры xp_readerrorlog в сценарии PowerShell.Прочитайте первую строку из набора результатов хранимой процедуры

К сожалению, метод ExecuteReader() считывает все записи, возвращаемые хранимой процедурой, а иногда это несколько миллионов строк или несколько минут.

Я попытался найти его и выяснил, что с помощью DAO можно считывать данные по мере их возврата SQL Server, но DAO устарел, и я не хочу идти по этому маршруту.

BeginExecuteReader(), похоже, не помогает, так как нет никаких указаний, если какие-либо данные были возвращены еще до завершения исполнения, которое равно ExecuteReader(). Есть ли другой способ получить только несколько первых строк/страниц при работе с большим набором записей?

Я понимаю, что я могу создать временную таблицу, сбросить данные в нее и SELECT TOP 1, но это введет некоторые дополнительные операции ввода-вывода, и это не решение, которое я ищу.

+0

Как насчет [архивация] (http://blogs.msdn.com/b/chrissk/archive/2011/01/05/how-to-archive-your-sql-errorlogs-into-a- table.aspx) ваш журнал ошибок и периодически его циклически? Тогда 'sp/xp_readerrorlog' должен быть более управляемым. –

+0

Это один из способов обхода этой конкретной хранимой процедуры. Я мог бы использовать этот подход, если нет другого решения. Но было бы неплохо узнать, возможно ли то, что я попросил. – Sheva

ответ

0

Начиная с .Net 4.5 SqlCommand класс имеет ExecuteReaderAsync() метод и SqlDataReader класс имеет ReadAsync() метод. Можно начать обработку данных, как только SQL Server начнет отправлять его клиенту с использованием этих новых методов.

К сожалению, в PowerShell нет оператора ожидания, поэтому код выглядит не так красиво, как на C#.

$sqlInstance = 'SqlServerName'; 

$con = New-Object System.Data.SqlClient.SQLConnection; 
$con.ConnectionString = "Data Source=$($sqlInstance);Integrated Security=SSPI;Asynchronous Processing=true;"; 
$con.Open(); 

$cmd = New-Object System.Data.SqlClient.SQLCommand; 
$cmd.Connection = $con; 

$cmd.CommandType = [System.Data.CommandType]::StoredProcedure; 
$cmd.CommandText = 'xp_readerrorlog'; 

# add parameters 
$cmd.Parameters.Add('p1', [System.Data.SqlDbType]::Int, 1) | out-null; 
$cmd.Parameters['p1'].Value = 1; 
$cmd.Parameters.Add('p2', [System.Data.SqlDbType]::Int, 1) | out-null; 
$cmd.Parameters['p2'].Value = 1; 

$cmd.Prepare(); 

$task = $cmd.ExecuteReaderAsync(); 

# wait for first rows 
While ($task.IsCompleted -ne $true) { 
    Start-Sleep -m 10; 
} 

# PowerShell does not have await operator so a SqlDataReader object can be retrieved this way 
$reader = $task.GetAwaiter().GetResult(); 

# read first row 
$reader.ReadAsync().GetAwaiter().GetResult() | out-null; 

$reader['LogDate']; 

$cmd.Cancel(); # cancel execution, there is no need to wait for the whole recordset to be transfered to the client 

$con.Close(); 
0

Я думаю, что у вас есть небольшое заблуждение относительно Execute methods work on a ServerConnection. Они не влияют на работу хранимых процедур. Даже если вы использовали ExecuteScalar (выполняет оператор Transact-SQL и возвращает первый столбец первой строки как тип значения.), Тогда вся целая хранимая процедура будет выполняться на сервере, после чего будет возвращено только первое значение.

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

Нет встроенных методов в ServerConnection, чтобы делать то, что вы хотите в .Net Framework.

+0

Я не пытался использовать ** SET ROWCOUNT ** для 'INSERT INTO EXEC xp_readerrorlog ...' , но он действительно работает. Это не работает, когда вы выполняете хранимую процедуру напрямую. Это код, который я собираюсь использовать. Благодаря! CREATE TABLE #errorlog ( \t [LogDate] [DateTime] NOT NULL, \t [ProcessInfo] [VARCHAR] (20) NOT NULL, \t [Текст] [Текст] NOT NULL ) SET ROWCOUNT 1 INSERT INTO #errorlog EXEC xp_readerrorlog 0, 1, NULL, NULL, NULL, NULL, N'desc» SELECT * FROM #errorlog DROP TABLE #errorlog – Sheva

+0

Создайте свой собственный ответ свой комментарий, и принять его как ответ на ваш вопрос. –

+0

Я только что проверил код выше на большом журнале ошибок, и время выполнения было ~ 2 минуты. Это означает, что он фактически обрабатывает полный набор записей и, следовательно, добавляет дополнительную нагрузку на сервер. – Sheva