Используя SQLCMD в пакетном файле, как я могу проанализировать ответ и проверить наличие ошибки?

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

Используемый мной sqlcmd выглядит так:

sqlcmd -S %server% -U %user% -P %pass% -b -Q "select count(*) from %table%"

Если это сработает, он вернет:

-----------
      10205

(1 rows affected)

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

Если я передаю несуществующую таблицу, я получаю следующий ответ:

Msg 208, Level 16, State 1, Server devServer, Line 1
Invalid object name 'dbo.no_table'.

Поскольку у меня есть флаг -b, я могу проверить значение ERRORLEVEL (в данном случае 1).

Чтобы сохранить переменную count, я использовал следующую строку:

for /F %%i in ('sqlcmd -S %server% -U %user% -P %pass% -b -Q "select count(*) from %table%" ^| findstr /r "[^(][0-9]"') do SET /a rec_count=%%i

После for %errorlevel% возвращает 0. Даже внутри do errorlevel равен 0.

Есть ли простой способ запустить sqlcmd, сохранить счетчик, если нет ошибки, и распечатать обе строки, если есть ошибка?


person Dan    schedule 09.04.2015    source источник
comment
Если бы это был новый проект, то я полностью согласен. К сожалению, я просто пытаюсь добавить счетчик в существующий пакетный файл.   -  person Dan    schedule 10.04.2015
comment
Я предполагаю, что вы говорите, что errorlevel не изменяется, даже если таблица недействительна?   -  person unclemeat    schedule 10.04.2015
comment
Да это верно. Уровень ошибки не меняется, если выполняется в цикле for, но изменяется, если выполняется вне цикла for.   -  person Dan    schedule 10.04.2015


Ответы (2)


Команды, которые выполняются FOR /F, выполняются неявно через новый сеанс CMD. Например, с for /f %a in ('echo hello') do ... выполняемая команда становится C:\Windows\system32\cmd.exe /c echo hello.

Ваша команда правильно устанавливает ERRORLEVEL, но затем значение теряется, как только дочерний сеанс CMD завершается, и управление возвращается вашему пакетному сценарию.

Таким образом, вариант /b на самом деле не приносит вам никакой пользы, и от него можно отказаться.

Вы можете скрыть информацию заголовка, добавив параметр -h -1.

Вы можете подавить сообщение (1 rows affected), поставив перед командой префикс set nocount on;.

Вы можете добавить параметр -r 1, чтобы сообщения об ошибках отображались на стандартном выводе вместо стандартного вывода. Это предотвратит обработку FOR /F какой-либо ошибки, и вместо этого на экране появится сообщение об ошибке.

Вы можете очистить переменную rec_count перед выполнением команды. Тогда он останется неопределенным, если была ошибка, иначе он будет содержать количество, если ошибки не было.

set "rec_count="
for /f %%A in (
  'sqlcmd -S %server% -U %user% -P %pass% -h -1 -r 1 -Q "set nocount on;select count(*) from %table%"'
) do set "rec_count=%%A"
if not defined rec_count echo There was an error!

Еще одна вещь, которую вы можете рассмотреть, — это использование переменных среды, распознаваемых SQLCMD, для вашего сервера, имени пользователя и пароля. Тогда вам не придется использовать параметры -S, -U или -P. Это особенно удобно, если ваш пакетный сценарий выполняет много команд SQLCMD.

set "sqlcmdServer=YourServer"
set "sqlcmdUser=YourUserName"
set "sqlcmdPassword=YourPassword"

set "rec_count="
for /f %%A in (
  'sqlcmd -h -1 -r 1 -Q "set nocount on;select count(*) from %table%"'
) do set "rec_count=%%A"
if not defined rec_count echo There was an error!
person dbenham    schedule 10.04.2015

Причина, по которой errorlevel, похоже, не устанавливается, заключается в том, что команда for выполняется успешно, независимо от того, как выполняется код, через который она проходит. Таким образом, вы можете взаимодействовать только с уровнем ошибки, установленным командой sqlcmd в той же строке (внутри скобок цикла for).

Вы должны иметь возможность использовать || (двойная вертикальная черта) после команды sqlcmd. Любой код после || будет выполняться только в случае сбоя предыдущей команды. Пример:

notACommand || echo test

Вернет "test". В то время как следующее выведет только «a command»:

echo a command || echo test

Я не могу проверить это, но что-то вроде следующего должно работать для вас:

for /F "EOL=(" %%i in ('sqlcmd -S %server% -U %user% -P %pass% -b -Q "select count(*) from %table%" ^|^| echo fail') do (
    SET rec_count=%%i
)
if "%rec_count%"=="fail" echo SQL command failed

Если вывод именно такой, как вы говорите, вам не нужна команда findstr - просто установите ( открывающую скобку в качестве символа EOL в цикле for, чтобы вы фактически удалили строку «(1 rows affected)». Возможно, вы захотите использовать переменные по-другому, но это всего лишь один из способов определить, не удалось выполнить команду sqlcmd или нет.

Что касается вывода ошибки - плохое решение - снова запустить ту же команду sqlcmd. Что-то вроде следующего:

set command=sqlcmd -S %server% -U %user% -P %pass% -b -Q "select count(*) from %table%"

for /F "EOL=(" %%i in ('%command% ^|^| echo fail') do SET rec_count=%%i

if "%rec_count%"=="fail" (%command%) else echo rec_count is %rec_count%

Обратите внимание, что я удалил переключатель /a при установке переменной rec_count, потому что теперь ее можно установить как слово.

person unclemeat    schedule 10.04.2015