При установлении соединения с SQL Server произошла ошибка, связанная с сетью или с определенным экземпляром
При подключении к экземпляру SQL Server может возникнуть одно или несколько из следующих сообщений об ошибке. В этой статье приводятся некоторые шаги по устранению этих ошибок, которые предоставляются в порядке от простых до сложных.
Сообщения об ошибках
Полные сообщения об ошибках зависят от клиентской библиотеки, используемой в приложении и серверной среде. Вы можете просмотреть приведенные ниже сведения, чтобы узнать, возникает ли одно из следующих сообщений об ошибке:
«При установлении соединения с SQL Server произошла ошибка, связанная с сетью или с определенным экземпляром. Убедитесь, что имя экземпляра указано правильно и что на SQL Server разрешены удаленные соединения».
Provider: Поставщик именованных каналов, ошибка: 40 — не удалось открыть подключение к SQL Server (Microsoft SQL Server, ошибка: 53) При установлении соединения с SQL Server произошла ошибка, связанная с сетью или с определенным экземпляром. Сервер не найден или недоступен. Убедитесь, что имя экземпляра указано правильно и что на SQL Server разрешены удаленные соединения.
поставщик: поставщик именованных каналов, ошибка: 40 — не удалось открыть подключение к SQL Server (Microsoft SQL Server, ошибка: 53)
поставщик: поставщик TCP, ошибка: 0 — данный узел неизвестен. (Microsoft SQL Server, ошибка: 11001)
поставщик: сетевые интерфейсы SQL, ошибка: 26 — ошибка при обнаружении указанного сервера или экземпляра При установлении соединения с SQL Server произошла ошибка, связанная с сетью или с определенным экземпляром. Сервер не найден или недоступен. Убедитесь, что имя экземпляра указано правильно и что на SQL Server разрешены удаленные соединения.
поставщик: сетевые интерфейсы SQL, ошибка: 26 — ошибка при обнаружении указанного сервера или экземпляра
Истекло время ожидания входа Ошибка канала передачи данных в клиенте Native Client SQL Server
[Microsoft SQL Server Native Client 10.0]: истекло время ожидания входа
[Microsoft SQL Server Native Client 10.0]: при установке подключения к SQL Server произошла ошибка, связанная с сетью или экземпляром. Сервер не найден или недоступен. Проверьте правильность имени экземпляра и убедитесь, что SQL Server разрешает удаленные подключения. Дополнительные сведения см. в электронной документации по SQL Server.
[Microsoft SQL Server Native Client 10.0]: SQL Server сетевые интерфейсы: ошибка при обнаружении указанного сервера или экземпляра [xFFFFFFFF].
Сбой попытки подключения из-за того, что подключенная сторона не ответила должным образом по истечении определенного периода времени, или сбой установленного подключения из-за того, что подключенный узел не смог ответить При установлении соединения с SQL Server произошла ошибка, связанная с сетью или с определенным экземпляром. Сервер не найден или недоступен. Убедитесь, что имя экземпляра указано правильно и что на SQL Server разрешены удаленные соединения.
поставщик: поставщик TCP, ошибка: 0
Произошел сбой при попытке подключения, поскольку истекло время ожидания ответа от подключаемой стороны, либо произошел сбой уже установленного подключения, поскольку подключенный узел не ответил.
Microsoft SQL Server, ошибка: 10060
Provider: поставщик именованных каналов, ошибка: 40 — не удалось открыть подключение к SQL Server При установлении соединения с SQL Server произошла ошибка, связанная с сетью или с определенным экземпляром. Сервер не найден или недоступен. Убедитесь, что имя экземпляра указано правильно и что на SQL Server разрешены удаленные соединения.
поставщик: поставщик именованных каналов, ошибка: 40 — не удалось открыть подключение к SQL Server
Microsoft SQL Server, ошибка: 53
Не найден сетевой путь
[Майкрософт] [SQL Server Native Client 11.0]Поставщик TCP: не удалось подключиться, так как целевой компьютер активно отказался от него Ошибка канала передачи данных в клиенте Native Client SQL Server
[Microsoft][Клиент Native Client SQL Server 11.0]Поставщик TCP: подключение не установлено, т.к. конечный компьютер отверг запрос на подключение.
[Microsoft][Клиент Native Client SQL Server 11.0]Истекло время ожидания входа.
[Microsoft][Клиент Native Client SQL Server 11.0]При установлении соединения с SQL Server произошла ошибка, связанная с сетью или с определенным экземпляром. Сервер не найден или недоступен. Проверьте правильность имени экземпляра и убедитесь, что SQL Server разрешает удаленные подключения. Дополнительные сведения см. в электронной документации по SQL Server.
«SQL Server не существует, либо в доступе к нему отказано»
Эта ошибка обычно означает, что клиенту не удается найти экземпляр SQL Server. Эта проблема возникает по одной из следующих причин:
- Неверное имя компьютера, на котором размещен SQL-сервер.
- Экземпляр не разрешает правильный IP-адрес.
- Номер TCP-порта указан неправильно.
Сведения об устранении неполадок с подключением в сценариях высокой доступности см. в следующих статьях:
- Подключение к прослушивателю группы доступности Always On
- Экземпляры отказоустойчивого кластера Always On (SQL Server)
Ошибка Windows 233: нет процесса на другом конце канала
Подключение к серверу успешно установлено, но затем произошла ошибка при входе. (Поставщик: поставщик общей памяти, ошибка: 0 — на другом конце канала нет процесса.) (Microsoft SQL Server, ошибка: 233)
Это сообщение означает, что SQL Server не прослушивает протокол общей памяти или именованных каналов.
Сбор сведений для устранения ошибки
Мы рекомендуем собрать сведения, перечисленные в этом разделе, с помощью одного из следующих вариантов, прежде чем переходить к фактическим действиям по устранению ошибки.
Вариант 1. Использование средства проверки SQL для сбора необходимых сведений
Если вы можете войти локально на компьютер SQL Server с правами администратора, используйте средство SQLCheck из репозитория GitHub «Microsoft SQL Networking». Это средство предоставляет большую часть сведений, необходимых для устранения неполадок в одном файле. Дополнительную информацию об использовании средства и собираемых сведениях см. на домашней странице средства. Вы также можете проверить рекомендуемые предварительные требования и страницу контрольного списка.
Вариант 2. Сбор данных по отдельности с помощью следующих процедур
Получение имени экземпляра из диспетчера конфигурации
На сервере, на котором размещается экземпляр SQL Server, используйте диспетчер конфигурации SQL Server для проверки имени экземпляра:
Диспетчер конфигурации устанавливается на компьютере автоматически при установке SQL Server. Инструкции по запуску диспетчера конфигурации немного различаются в зависимости от версий SQL Server и Windows. Дополнительные сведения для конкретной версии см. в разделе Диспетчер конфигурации SQL Server.
- Войдите на компьютер, на котором размещается экземпляр SQL Server.
- Запустите диспетчер конфигурации SQL Server.
- В области слева выберите Службы SQL Server.
- В области справа проверьте имя экземпляра ядра СУБД.
- SQL SERVER (MSSQLSERVER) указывает экземпляр по умолчанию SQL Server. Имя экземпляра по умолчанию — компьютера.
- SQL SERVER ( экземпляра) указывает именованный экземпляр SQL Server. Имя именованного экземпляра — компьютера\ экземпляра.
Получение IP-адреса сервера
Чтобы получить IP-адрес компьютера, на котором размещается экземпляр SQL Server, выполните следующие действия.
- В меню Пуск выберите команду Выполнить. В окне Выполнить введите cmd и нажмите кнопку ОК.
- В окне командной строки введите ipconfig/all и нажмите клавишу ВВОД. Запишите IPv4-адрес и IPv6-адрес.
Примечание. SQL Server можно подключиться с помощью протокола IP версии 4 или 6. Ваша сеть может использовать обе версии.
Получение TCP-порта экземпляра
В большинстве случаев подключение к ядру СУБД на другом компьютере осуществляется с помощью протокола TCP. Чтобы получить TCP-порт экземпляра, выполните следующие действия:
- Используйте SQL Server Management Studio на компьютере, на котором выполняется SQL Server, и подключитесь к экземпляру SQL Server. В обозревателе объектов последовательно разверните разделы Управление, Журналы SQL Server, а затем дважды щелкните текущий журнал.
- В средстве просмотра журнала выберите Фильтр на панели инструментов. В поле Сообщение содержит текст введите сервер прослушивает, выберите Применить фильтр и нажмите кнопку ОК.
- Должно появиться сообщение типа «Сервер прослушивает [любой» 1433]». Это сообщение указывает, что экземпляр SQL Server прослушивает все IP-адреса на этом компьютере (для протокола IP версии 4) и TCP-порт 1433. (TCP-порт 1433 обычно является портом, используемым ядром СУБД или экземпляром по умолчанию SQL Server. Только один экземпляр SQL Server может использовать этот порт. Если установлено несколько экземпляров SQL Server, некоторые экземпляры должны использовать другие номера портов.) Запишите номер порта, используемый экземпляром SQL Server, к которому вы пытаетесь подключиться.
- Вероятно, указан IP-адрес 127.0.0.1. Он называется адресом адаптера замыкания на себя. Только процессы на одном и том же компьютере могут использовать IP-адрес для подключения.
- Журнал ошибок SQL Server также можно просмотреть с помощью текстового редактора. По умолчанию журнал ошибок размещается в файлах Program Files\Microsoft SQL Server\MSSQL.n\MSSQL\LOG\ERRORLOG и ERRORLOG.n. Для получения дополнительной информации см. раздел Просмотр журнала ошибок SQL Server.
Шаг 1. Проверка выполнения экземпляра
Вариант 1. Использование выходного файла из средства SQLCheck
- В выходных данных файла SQLCheck найдите текст «Сведения об SQL Server».
- В разделе «Интересующие службы» найдите экземпляр SQL Server в столбцах Имя и Экземпляр (для именованных экземпляров) и проверьте его состояние с помощью столбца Запущено. Если значение равно True, службы запущены. В противном случае служба в настоящее время не запущена.
- Если служба не запущена, запустите службу с помощью SQL Server Management Studio, диспетчера конфигурации SQL Server, PowerShell или приложения «Службы».
Вариант 2. Использование диспетчера конфигурации SQL Server
Чтобы убедиться, что экземпляр выполняется, выберите пункт Службы SQL Server в диспетчере конфигурации SQL Server и проверьте символ для экземпляра SQL Server.
- Зеленая стрелка указывает, что экземпляр выполняется.
- Красный квадрат указывает, что экземпляр остановлен.
Если экземпляр остановлен, щелкните его правой кнопкой мыши и выберите Запустить. Затем запускается экземпляр сервера, и индикатор становится зеленой стрелкой.
Вариант 3. Использование команд PowerShell
Чтобы проверить состояние служб SQL Server в системе, можно использовать следующую команду в PowerShell:
Get-Service | Where
Вы можете использовать следующую команду для поиска в файле журнала ошибок определенной строки «SQL Server готов для клиентских подключений». Это сообщение является информационным; никаких действий пользователя не требуется».
Get-ChildItem -Path "c:\program files\microsoft sql server\mssql*" -Recurse -Include Errorlog | Select-String "SQL Server is now ready for client connections."
Шаг 2. Проверка запуска службы обозревателя SQL Server
Этот шаг требуется только для устранения неполадок с подключением к именованным экземплярам.
Вариант 1. Использование выходного файла из средства SQLCheck
- В выходных данных файла SQLCheck найдите текст «Сведения об SQL Server».
- В разделе «Интересующие службы» найдите службу SQLBrowser в столбце Имя и проверьте ее состояние с помощью столбца Запущено. Если значение равно «True», служба запущена. В противном случае служба в настоящее время не запущена, и ее необходимо запустить. Дополнительные сведения см. в разделе Запуск, остановка, приостановка, возобновление, перезапуск служб SQL Server.
Вариант 2. Использование диспетчера конфигурации SQL Server
Чтобы подключиться к именованному экземпляру, служба обозревателя SQL Server должна быть запущена. В диспетчере конфигурации SQL Server найдите службу обозревателя SQL Server и убедитесь, что она запущена. Если служба не запущена, запустите ее. Служба обозревателя SQL Server не требуется для экземпляров по умолчанию.
Дополнительные сведения об использовании службы обозревателя SQL Server в вашей среде см. в разделе Служба обозревателя SQL Server.
Дополнительные сведения об остановке и запуске служб SQL см. в разделе Запуск, остановка, приостановка, возобновление, перезапуск служб SQL Server.
Если в вашей среде служба обозревателя SQL Server не запущена, ознакомьтесь с разделом Подключение к именованному экземпляру сервера SQL без использования службы обозревателя SQL Server для получения дополнительной информации.
Шаг 3. Проверка имени сервера в строке подключения
При указании неправильного имени сервера в строке подключения часто возникают ошибки. Убедитесь, что имя сервера совпадает с именем сервера, извлеченным на предыдущих шагах.
Если вы используете средство SQLCheck, просмотрите значения NetBIOS-имя/полное доменное имя в разделе Сведения о компьютере выходного файла.
- Примеры строк подключения см. в разделе Строки подключения SQL Server.
- Подробные примеры см. в разделе Проверка концепции подключения к SQL с помощью ADO.NET в подразделе Главная страница для программирования клиента SQL.
Шаг 4. Проверка псевдонимов на клиентских компьютерах
Псевдонимы часто используются в клиентских средах при подключении к SQL Server с помощью альтернативного имени или при наличии проблем с разрешением имен в сети. Они создаются с помощью диспетчера конфигурации SQL Server или служебной программы клиентской сети. Неправильный псевдоним может привести к подключению приложений к неправильному серверу, что вызовет сбой. Используйте следующие способы, чтобы проверить наличие неправильных псевдонимов. Вы также можете использовать средство (например SQLCHECK) на клиентском компьютере для проверки псевдонимов и других параметров, связанных с подключением, на клиентском компьютере.
Следующие параметры применяются только к приложениям, которые используют клиент Native Client SQL Server для подключения к SQL Server.
Вариант 1. Использование выходного файла из средства SQLCheck
- В выходном файле SQLCheck найдите псевдонимы строк SQL. (Эта строка будет отображаться в разделе файла Безопасность клиента и сведения о драйвере)
- Просмотрите записи в таблице. Если их нет, на компьютере отсутствуют псевдонимы. Если имеется запись, просмотрите сведения, чтобы убедиться, что для имени сервера и номера порта заданы правильные значения.
Пример выходных данных:
Псевдонимы SQL:
Alias Name Protocol Server Name Port 32-bit ---------- -------- ------------ ---- ------ prodsql TCP prod_sqlserver 1430
Выходные данные указывают, что prodsql является псевдонимом для SQL Server prod_sqlserver , работающего на порту 1430.
Вариант 2. Проверка наличия псевдонимов в диспетчере конфигурации SQL Server
- В диспетчере конфигурации SQL Server разверните узел Конфигурация собственного клиента SQL Server и выберите Псевдонимы.
- Проверьте, определены ли псевдонимы для сервера, к которому вы пытаетесь подключиться. Если псевдонимы существуют, выполните следующие действия:
- Откройте панель Свойства псевдонима.
- Переименуйте значение в поле Имя псевдонима (например, если имя сервера — MySQL, переименуйте его в MySQL_test) и повторите попытку подключения. Если подключение работает, псевдоним является неправильным и может быть указан в старой конфигурации, которая больше не нужна. Если подключение не работает, переименуйте псевдоним, вернув ему первоначальное имя, и перейдите к следующему шагу.
- Проверьте параметры подключения для псевдонима и убедитесь, что они верны. Следующие распространенные сценарии могут вызвать проблемы с подключением:
- Неправильный IP-адрес для поля Сервер. Убедитесь, что IP-адрес соответствует записи в файле журнала ошибок SQL Server.
- Неправильное имя сервера в поле Сервер. Например, псевдоним сервера указывает на правильное имя сервера. Однако подключения будут утеряны, если значение параметра имени сервера неверно.
- Неправильный формат имени канала (при условии, что используются псевдонимы именованных каналов).
- При подключении к экземпляру по умолчанию с именем Mydefaultinstance имя канала должно быть \\Mydefaultinstance\pipe\sql\query.
- При подключении к именованным экземпляру MySQL\Named имя канала должно быть \\MySQL\pipe\MSSQL$Named\sql\query.
Вариант 3. Проверка наличия псевдонимов в служебной программе клиентской сети SQL Server
- Откройте служебную программу клиентской сети SQL Server, введя cliconfg.exe в окне команды «Выполнить».
- Выполните шаг 2 в разделе Вариант 2. Проверка наличия псевдонимов в диспетчере конфигурации SQL Server.
Шаг 5. Проверка конфигурации брандмауэра
Конфигурацию брандмауэра можно проверить в зависимости от экземпляра по умолчанию или именованного экземпляра.
Если вы используете брандмауэры сторонних производителей в сети, приведенные шаги по-прежнему применимы. Однако вам может потребоваться обратиться к администратору сети или изучить документацию по брандмауэру, чтобы получить дополнительные сведения о настройке брандмауэра для разрешения связи необходимых портов с SQL Server.
Экземпляр по умолчанию SQL Server
Экземпляр по умолчанию обычно выполняется через порт 1433. Некоторые установки также используют нестандартный порт (кроме 1433) для запуска экземпляров SQL. Брандмауэр может заблокировать любой порт. Для дальнейшей проверки номера порта выполните следующие действия:
- Определите порт, на котором выполняется экземпляр SQL, ознакомившись с информацией в разделе Получение TCP-порта экземпляра.
-
- Если SQL Server настроен на прослушивание порта 1433, убедитесь, что брандмауэры в сети между клиентом и сервером разрешают трафик через этот порт. Ознакомьтесь с разделом Настройка брандмауэра Windows для доступа к ядру СУБД и обратитесь к администратору сети для реализации необходимых решений.
- Если экземпляр по умолчанию SQL Server не использует порт 1433, попробуйте добавить номер порта SQL Server к имени сервера, используя формат , , и проверьте, работает ли это. Например, имя экземпляра SQL, который выполняется на порту 2000, — MySQLDefaultinstance. Укажите имя сервера MySQLServer, 2000 и проверьте, работает ли это.
- Если это не работает, брандмауэр блокирует порт. Вы можете выполнить инструкции в разделе Настройка брандмауэра Windows для доступа к ядру СУБД или обратитесь к администратору сети, чтобы добавить порт в список исключений брандмауэра.
- Если это работает, брандмауэр разрешает обмен данными через этот порт. Чтобы использовать номер порта и имя сервера в строке подключения приложения, необходимо изменить строку подключения.
Именованный экземпляр SQL Server
Если экземпляр SQL является именованным, его можно настроить для использования динамических или статических портов. В любом случае базовые сетевые библиотеки запрашивают службу обозревателя SQL Server, запущенную на компьютере SQL Server через UDP-порт 1434, чтобы перечислить номер порта для именованного экземпляра. Если брандмауэр между клиентом и сервером блокирует этот UDP-порт, клиентская библиотека не может определить порт (требование для подключения) и подключение завершается сбоем. Для проверки подключения воспользуйтесь одним из описанных ниже способов:
- Способ 1. Проверьте подключение, указав номер порта в строке подключения.
- Определите порт, на котором выполняется экземпляр SQL, ознакомившись с информацией в разделе Получение TCP-порта экземпляра.
- Попробуйте подключиться к именованному экземпляру, используя номер порта, добавленный к имени сервера в формате
, , и проверьте, работает ли это. Например, если имя экземпляра SQL — MySQL\Namedinstance и он выполняется на порте 3000, укажите имя сервера MySQL\Namedinstance,3000. - Если это работает, то брандмауэр блокирует UDP-порт 1434 или экземпляр скрыт от обозревателя SQL Server.
- Если это не работает, возникла одна из следующих причин:
- Заблокирован UDP-порт 1434 или статический порт, либо оба порта. Чтобы убедиться, что заблокирован UDP-порт или статический порт, используйте Portqry.
- Экземпляр скрыт от службы обозревателя SQL Server.
- Сценарий 1. Динамические порты. В этом случае убедитесь, что служба обозревателя SQL Server запущена и UDP-порт 1434 не блокируется в брандмауэре между клиентом и сервером. Если вы не можете выполнить ни одно из этих действий, следует переключить экземпляр SQL Server на статический порт и использовать процедуру, описанную в разделе Настройка сервера для прослушивания определенного TCP-порта.
- Сценарий 2. Настройка статического порта. Обозреватель SQL Server не запущен или UDP-порт 1434 не может быть открыт в брандмауэре. В этом случае обязательно укажите статический порт в строке подключения и убедитесь, что брандмауэр не блокирует порт. Дополнительные сведения см. в разделе Настройка брандмауэра Windows для доступа к ядру СУБД.
Шаг 6. Проверка включенных протоколов в SQL Server
В некоторых установках SQL Server подключения к ядру СУБД с другого компьютера не включены, если администратор не включит их вручную. Вы можете использовать один из следующих вариантов, чтобы проверить и включить необходимые протоколы с целью разрешения удаленных подключений к SQL Server Database Engine.
Вариант 1. Использование выходного файла из средства SQLCheck
- В выходном файле SQLCheck найдите раздел «Сведения об экземпляре SQL Server» и найдите раздел сведений об экземпляре SQL Server.
- В разделе найдите значения, перечисленные в следующей таблице, чтобы определить, включены ли протоколы SQL Server:
Value name Последствие Дополнительные сведения Включена общая память Может иметь значение «true» или «false» и влияет только на локальные подключения. Создание допустимой строки подключения с помощью протокола общей памяти Именованные каналы включены Если значение равно «false», локальные и удаленные подключения, использующие именованные каналы, будут утеряны. Выбор сетевого протокола TCP включен Если значение равно «false», локальные и удаленные подключения, использующие TCP/IP, будут утеряны.
Примечание. Большинство установок SQL Server используют TCP/IP в качестве протокола связи между сервером и клиентом.Выбор сетевого протокола - Включите необходимые протоколы с помощью диспетчера конфигурации SQL Server или SQL Server PowerShell. Дополнительные сведения см. в разделе Включение или отключение сетевого протокола сервера.
Примечание. После включения протокола ядро СУБД должно быть остановлено и перезапущено, чтобы изменения вступили в силу.
Вариант 2. Использование диспетчера конфигурации SQL Server
Чтобы включить подключения с другого компьютера с помощью диспетчера конфигурации SQL Server, выполните следующие действия:
- Откройте диспетчер конфигурации SQL Server.
- В области слева разверните узел Сетевая конфигурация SQL Server, а затем выберите экземпляр SQL Server, к которому нужно подключиться. В области справа перечислены доступные протоколы подключения. Общая память обычно включена. Может использоваться только с того же компьютера, поэтому в большинстве установок общая память остается включенной. Чтобы подключиться к SQL Server с другого компьютера, используйте TCP/IP. Если TCP/IP не включен, щелкните правой кнопкой мыши TCP/IP и выберите Включить.
- Если вы изменяете включенный параметр для любого протокола, перезапустите ядро СУБД. В области слева выберите Службы SQL Server. В области справа щелкните правой кнопкой экземпляр ядра СУБД, а затем щелкните Перезапустить.
Шаг 7. Проверка подключения по протоколу TCP/IP
Для подключения к SQL Server с помощью TCP/IP требуется, чтобы система Windows установила подключение. Чтобы проверить TCP-подключение с помощью средства проверки связи, выполните следующие действия.
- В меню Пуск выберите команду Выполнить. В окне Выполнить введите cmd и нажмите кнопку ОК.
- В окне командной строки введите ping и IP-адрес компьютера, на котором работает SQL Server. Например:
- IPv4: ping 192.168.1.101
- IPv6: ping fe80::d51d:5ab5:6f09:8f48%11
- Если сеть настроена правильно, ping возвращает Reply from с некоторой дополнительной информацией. Если ping возвращает Destination host unreachable или Request timed out , TCP/IP настроен неправильно. Ошибки на этом этапе указывают на проблему с клиентским компьютером, серверным компьютером или другим устройством в сети, таким как маршрутизатор. Сведения об устранении проблем с сетью см. в разделе Расширенное устранение неполадок с TCP/IP.
- Если тест ping с помощью IP-адреса завершается успешно, проверьте, можно ли разрешить имя компьютера в TCP/IP-адрес. На клиентском компьютере в окне командной строки введите введите ping и имя компьютера, на котором выполняется SQL Server. Например, ping newofficepc .
- Если проверка связи по IP-адресу завершается успешно, но проверка связи с именем компьютера возвращает Destination host unreachable или Request timed out , на клиентском компьютере кэшируются старые (устаревшие) сведения о разрешении имен. Введите ipconfig /flushdns , чтобы очистить кэш DNS. Затем снова проверьте связь с компьютером по имени. Если кэш DNS пуст, клиентский компьютер проверяет последние сведения об IP-адресе серверного компьютера.
- Если сеть настроена правильно, ping возвращает Reply from с некоторой дополнительной информацией. Если вы можете успешно проверить связь с серверным компьютером по IP-адресу, но получаете ошибку, например Destination host unreachable или Request timed out , при проверке связи по имени компьютера, разрешение имен настроено неправильно. Дополнительные сведения см. в разделе Устранение основных неполадок с TCP/IP. Для подключения к SQL Server не требуется успешное разрешение имен. Однако если имя компьютера не может быть разрешено в IP-адрес, необходимо установить подключения, чтобы указать IP-адрес. Проблему с разрешением имен можно устранить позже.
Можно также использовать командлет Test-NetConnection или Test-Connection для проверки TCP-подключения в соответствии с версией PowerShell, установленной на компьютере. Дополнительные сведения о командлете PowerShell см. в разделе Обзор командлета.
Шаг 8. Проверка локального подключения
Прежде чем устранять неполадки подключения с другого компьютера, проверьте возможность подключения из клиентского приложения, установленного локально на компьютере, на котором работает SQL Server. Локальное подключение позволяет избежать проблем с сетями и брандмауэрами.
Для выполнения этой процедуры требуется SQL Server Management Studio. Если у вас не установлено средство Management Studio, ознакомьтесь с разделом Загрузка SQL Server Management Studio (SSMS).
Если не удается установить Management Studio, можно проверить подключение с помощью служебной программы sqlcmd.exe. sqlcmd.exe устанавливается вместе с ядром СУБД. Дополнительные сведения об sqlcmd.exe см. в разделе Служебная программа sqlcmd.
- Войдите на компьютер, на котором установлен SQL Server, используя имя входа для доступа к SQL Server. Во время установки SQL Server необходимо указать по крайней мере одно имя входа в качестве администратора SQL Server. Если вы не знаете, как связаться с администратором, ознакомьтесь с разделом Подключение к SQL Server при блокировке системных администраторов.
- На начальной странице введите SQL Server Management Studio или в меню Пуск предыдущих версий Windows последовательно выберите Все программы, Microsoft SQL Server и SQL Server Management Studio.
- В раскрывающемся меню Подключение выберите Ядро СУБД. В поле Проверка подлинности выберите Проверка подлинности Windows. В поле Имя сервера введите один из следующих типов подключений:
Подключение к Тип Пример Экземпляр по умолчанию ACCNT27 Именованный экземпляр ACCNT27\PAYROLL
Примечание. При подключении SQL Server из клиентского приложения на одном и том же компьютере используется протокол общей памяти. Общая память — это тип локального именованного канала, поэтому иногда возникают ошибки, связанные с каналами.
Примечание. Вы не можете устранить проблему, не имея достаточной информации, так как некоторые сообщения об ошибках передаются клиенту намеренно. Это функция безопасности, которая не предоставляет злоумышленнику сведения об SQL Server. Дополнительные сведения об ошибке см. в журнале ошибок SQL Server.
Шаг 9. Тестирование удаленного подключения
После подключения с помощью протокола TCP на том же компьютере попробуйте подключиться с клиентского компьютера. Можно использовать любое клиентское приложение, но во избежание сложностей установите средства управления SQL Server на клиенте. После установки попробуйте использовать SQL Server Management Studio.
- Используйте SQL Server Management Studio на клиентском компьютере и попробуйте подключиться, используя IP-адрес и номер TCP-порта в формате номера порта с IP-адресом, разделенным запятыми. Например, 192.168.1.101,1433 . В случае сбоя подключения может возникнуть одна из следующих проблем:
- ping IP-адреса не работает. Это указывает на общую проблему конфигурации TCP. Вернитесь к разделу Шаг 7. Проверка подключения по протоколу TCP/IP.
- SQL Server не прослушивает протокол TCP. Вернитесь к разделу Шаг 6. Проверка включенных протоколов в SQL Server.
- SQL Server прослушивает порт, отличный от указанного. Вернитесь к разделу Получение TCP-порта.
- TCP-порт SQL Server блокируется брандмауэром. Вернитесь к разделу Шаг 5. Проверка конфигурации брандмауэра.
- После установления подключения по IP-адресу и номеру порта ознакомьтесь со следующими сценариями:
- При подключении к экземпляру по умолчанию, который прослушивает любой порт, отличный от 1433, необходимо использовать номер порта в строке подключения или создать псевдоним на клиентском компьютере для подключения к экземпляру по умолчанию. Служба обозревателя SQL Server не может перечислить порты экземпляра по умолчанию.
- При подключении к именованному экземпляру попробуйте подключиться к экземпляру в формате имени экземпляра и IP-адреса с обратной косой чертой. (Например, 192.168.1.101\ ). Если это действие не срабатывает, это означает, что номер порта не возвращается клиенту. Проблема связана со службой обозревателя SQL Server, которая предоставляет клиенту номер порта именованного экземпляра. Ниже приведены решения:
- Запустите службу обозревателя SQL Server. Ознакомьтесь с инструкциями по запуску обозревателя в диспетчере конфигурации SQL Server.
- Служба обозревателя SQL Server блокируется брандмауэром. Откройте UDP-порт 1434 в брандмауэре. Вернитесь к разделу Шаг 5. Проверка конфигурации брандмауэра. Убедитесь, что вы открываете UDP-порт, а не TCP-порт.
- Сведения о UDP-порте 1434 блокируются маршрутизатором. UDP-соединение не осуществляется через маршрутизаторы и предотвращает заполнение сети трафиком с низким приоритетом. Вы можете настроить маршрутизатор для переадресации трафика UDP или указать номер порта при каждом подключении.
- Если клиентский компьютер использует Windows 7, Windows Server 2008 или более позднюю версию операционной системы, клиентская операционная система может удалить трафик UDP, так как ответ с сервера возвращается с другого IP-адреса, который был запрошен. Это действие является функцией безопасности, блокирующей «слабое сопоставление источников». Дополнительные сведения см. в разделе Несколько IP-адресов сервера в электронной документации по устранению неполадок: истекло время ожидания. (Эта статья относится к SQL Server 2008 R2, но субъекты по-прежнему применяются. Вы можете настроить клиент на использование правильного IP-адреса или указать номер порта при каждом подключении.)
- После подключения по IP-адресу (или IP-адресу и имени экземпляра для именованного экземпляра) попробуйте подключиться с помощью имени компьютера (или имени компьютера и имени экземпляра для именованного экземпляра). Поместите tcp: перед именем компьютера для принудительного подключения по протоколу TCP/IP. Например, для экземпляра по умолчанию на компьютере с именем ACCNT27 используйте tcp:ACCNT27 . Для именованного экземпляра PAYROLL на этом компьютере используйте tcp:ACCNT27\PAYROLL . Если вы можете подключиться по IP-адресу, но не по имени компьютера, у вас возникла проблема с разрешением имен. Вернитесь к разделу Шаг 7. Проверка подключения по протоколу TCP/IP.
- После установления подключения с помощью имени компьютера с принудительным использованием TCP попробуйте подключиться с помощью имени компьютера без принудительного использования TCP. Например, для экземпляра по умолчанию просто используйте имя компьютера, например CCNT27. Для именованного экземпляра используйте имя компьютера и имя экземпляра, например ACCNT27\PAYROLL. Если вы можете подключиться только при принудительном использовании TCP, клиент, вероятно, использует другой протокол, например именованные каналы. Для устранения данной проблемы выполните следующие действия:
- На клиентском компьютере используйте диспетчер конфигурации SQL Server. В левой области разверните узел Sql Native Client Configuration и выберите Client Protocols (Протоколы клиента).
- В области слева убедитесь, что включен протокол TCP/IP. Если протокол TCP/IP отключен, щелкните правой кнопкой мыши TCP/IP и выберите Включить.
- Убедитесь, что порядок протокола для TCP/IP ниже, чем у протоколов именованных каналов (или VIA в более ранних версиях). Как правило, следует оставить общую память в качестве порядка 1, а TCP/IP — в качестве порядка 2. Общая память используется только в том случае, если клиент и SQL Server работают на одном компьютере. Все включенные протоколы реализуются по порядку, пока один из них не будет реализован успешно, но общая память пропускается, если подключение не установлено на одном компьютере.
См. также
- Сетевые протоколы и сетевые библиотеки
- Конфигурация клиентской сети
- Настройка клиентских протоколов
- Устранение проблем с подключением в SQL Server
- 0200 Проблема с согласованной сетью
- Подключение к прослушивателю группы доступности Always On
- Экземпляры отказоустойчивого кластера Always On (SQL Server)
- Устранение неполадок с подключением и других ошибок с базой данных SQL Azure и Управляемым экземпляром SQL Azure
Обратная связь
Были ли сведения на этой странице полезными?
Примеры выражений
В этой статье приведено множество примеров выражений Access. Выражение — это сочетание математических или логических операторов, констант, функций, полей таблицы, элементов управления и свойств, результатом вычисления которого является одно значение. Выражения применяются для вычисления значений, проверки данных и задания значений по умолчанию.
В этой статье
Формы и отчеты
Запросы и фильтрыФормы и отчеты
Таблицы, приведенные в этом разделе, содержат примеры выражений, вычисляющих значение в элементе управления в форме или отчете. Чтобы создать вычисляемый элемент управления, введите выражение в свойство Данные элемента управления, а не в поле таблицы или запроса.
Примечание Выражения также можно использовать в формах или отчетах при выделении данных с помощью условного форматирования.
Операции с текстом
В выражениях, приведенных в таблице ниже, используются операторы & (амперсанд) и + (плюс) для объединения текстовых строк, а также встроенные функции для обработки текстовых строк или выполнения с ними других действий с целью создания вычисляемого элемента управления.
Отображает значения, хранящиеся в полях таблицы FirstName и LastName. В данном примере оператор & используется для объединения значения в поле «Имя», пробела (заключенного в кавычки) и значения в поле «Фамилия».
Функция Left используется для отображения первого знака в значении поля или элемента управления «НазваниеТовара».
Функция Right используется для отображения последних 2 знаков в значении поля или элемента управления «КодТовара».
Функция Trim используется для отображения значения в элементе управления «Адрес» и удаления всех пробелов в начале или конце строки.
=IIf(IsNull([Region]), [City] & » » & [PostalCode], [City] & » » & [Region] & » » & [PostalCode])
Функция IIf используется для отображения значений элементов управления City и PostalCode, если элемент управления Region имеет пустое значение; в противном случае отображаются значения в элементах управления City, Region и PostalCode, разделенные пробелами.
=[City] & (» » + [Region]) & » » & [PostalCode]
Оператор + и распространение значений Null используются для отображения значений в элементах управления «Город» и «Индекс», если элемент управления или поле «Область» имеет значение Null; в противном случае отображаются значения в элементах управления или полях «Город», «Область» и «Индекс», разделенные пробелами.
Распространение значений Null означает, что если хотя бы один компонент выражения возвращает значение Null, то и все выражение также возвращает Null. Оператор + поддерживает распространение значений Null, а оператор & — нет.
Колонтитулы
Для отображения или печати номеров страниц в отчетах или формах используются свойства Page и Pages. Свойства Page и Pages доступны только во время печати или предварительного просмотра, поэтому они не отображаются на странице свойств формы или отчета. Обычно эти свойства используются путем перемещения текстового поля в область верхнего или нижнего колонтитула в форме или отчете и применения выражения, например из приведенной ниже таблицы.
Дополнительные сведения об использовании колонтитулов в формах и отчетах см. в статье Вставка номеров страниц в форму или отчет.
SQL. Занимательные задачки
Вот уже более 3-х лет я преподаю SQL в разных тренинг центрах, и одним из моих наблюдений является то, что студенты осваивают и понимают SQL лучше, если ставить перед ними задачу, а не просто рассказывать о возможностях и теоретических основах.
В этой статье я поделюсь с вами своим списком задач, которые я даю студентам в качестве домашнего задания и над которыми мы проводим разного рода брейнстормы, что приводит к глубокому и четкому пониманию SQL.
SQL (ˈɛsˈkjuˈɛl; англ. structured query language — «язык структурированных запросов») — декларативный язык программирования, применяемый для создания, модификации и управления данными в реляционной базе данных, управляемой соответствующей системой управления базами данных. Подробнее…
Почитать об SQL можно из разных источников.
Данная статья не преследует цели обучить вас SQL с нуля.Итак, поехали.
Будем использовть всем известную схему HR в Oracle с ее таблицами (Подробнее):
Отмечу что мы будем рассматривать только задачи на SELECT. Тут нет задач на DML и DDL.
Задачи
Restricting and Sorting Data
Таблица Employees. Получить список с информацией обо всех сотрудниках
Решение
SELECT * FROM employeesТаблица Employees. Получить список всех сотрудников с именем ‘David’
РешениеSELECT * FROM employees WHERE first_name = 'David';Таблица Employees. Получить список всех сотрудников с job_id равным ‘IT_PROG’
РешениеSELECT * FROM employees WHERE job_id = 'IT_PROG'Таблица Employees. Получить список всех сотрудников из 50го отдела (department_id) с зарплатой(salary), большей 4000
Решение
SELECT * FROM employees WHERE department_id = 50 AND salary > 4000;Таблица Employees. Получить список всех сотрудников из 20го и из 30го отдела (department_id)
РешениеSELECT * FROM employees WHERE department_id = 20 OR department_id = 30;Таблица Employees. Получить список всех сотрудников у которых последняя буква в имени равна ‘a’
РешениеSELECT * FROM employees WHERE first_name LIKE '%a';Таблица Employees. Получить список всех сотрудников из 50го и из 80го отдела (department_id) у которых есть бонус (значение в колонке commission_pct не пустое)
Решение
SELECT * FROM employees WHERE (department_id = 50 OR department_id = 80) AND commission_pct IS NOT NULL;Таблица Employees. Получить список всех сотрудников у которых в имени содержатся минимум 2 буквы ‘n’
РешениеSELECT * FROM employees WHERE first_name LIKE '%n%n%';Таблица Employees. Получить список всех сотрудников у которых длина имени больше 4 букв
РешениеSELECT * FROM employees WHERE first_name LIKE '%_____%';Таблица Employees. Получить список всех сотрудников у которых зарплата находится в промежутке от 8000 до 9000 (включительно)
Решение
SELECT * FROM employees WHERE salary BETWEEN 8000 AND 9000;Таблица Employees. Получить список всех сотрудников у которых в имени содержится символ ‘%’
РешениеSELECT * FROM employees WHERE first_name LIKE '%\%%' ESCAPE '\';Таблица Employees. Получить список всех ID менеджеров
РешениеSELECT DISTINCT manager_id FROM employees WHERE manager_id IS NOT NULL;Таблица Employees. Получить список работников с их позициями в формате: Donald(sh_clerk)
РешениеSELECT first_name || '(' || LOWER (job_id) || ')' employee FROM employees;Using Single-Row Functions to Customize Output
Таблица Employees. Получить список всех сотрудников у которых длина имени больше 10 букв
Решение
SELECT * FROM employees WHERE LENGTH (first_name) > 10;Таблица Employees. Получить список всех сотрудников у которых в имени есть буква ‘b’ (без учета регистра)
Решение
SELECT * FROM employees WHERE INSTR (LOWER (first_name), 'b') > 0;Таблица Employees. Получить список всех сотрудников у которых в имени содержатся минимум 2 буквы ‘a’
РешениеSELECT * FROM employees WHERE INSTR (LOWER (first_name),'a',1,2) > 0;Таблица Employees. Получить список всех сотрудников зарплата которых кратна 1000
РешениеSELECT * FROM employees WHERE MOD (salary, 1000) = 0;Таблица Employees. Получить первое 3х значное число телефонного номера сотрудника если его номер в формате ХХХ.ХХХ.ХХХХ
Решение
SELECT phone_number, SUBSTR (phone_number, 1, 3) new_phone_number FROM employees WHERE phone_number LIKE '___.___.____';Таблица Departments. Получить первое слово из имени департамента для тех у кого в названии больше одного слова
Решение
SELECT department_name, SUBSTR (department_name, 1, INSTR (department_name, ' ')-1) first_word FROM departments WHERE INSTR (department_name, ' ') > 0;Таблица Employees. Получить имена сотрудников без первой и последней буквы в имени
РешениеSELECT first_name, SUBSTR (first_name, 2, LENGTH (first_name) - 2) new_name FROM employees;Таблица Employees. Получить список всех сотрудников у которых последняя буква в имени равна ‘m’ и длинной имени большей 5ти
Решение
SELECT * FROM employees WHERE SUBSTR (first_name, -1) = 'm' AND LENGTH(first_name)>5;Таблица Dual. Получить дату следующей пятницы
РешениеSELECT NEXT_DAY (SYSDATE, 'FRIDAY') next_friday FROM DUAL;Таблица Employees. Получить список всех сотрудников которые работают в компании больше 17 лет
РешениеSELECT * FROM employees WHERE MONTHS_BETWEEN (SYSDATE, hire_date) / 12 > 17;Таблица Employees. Получить список всех сотрудников у которых последня цифра телефонного номера нечетная и состоит из 3ех чисел разделенных точкой
Решение
SELECT * FROM employees WHERE MOD (SUBSTR (phone_number, -1), 2) != 0 AND INSTR (phone_number,'.',1,3) = 0 AND INSTR (phone_number,'.',1,2) > 0;Таблица Employees. Получить список всех сотрудников у которых в значении job_id после знака ‘_’ как минимум 3 символа но при этом это значение после ‘_’ не равно ‘CLERK’
Решение
SELECT * FROM employees WHERE LENGTH (SUBSTR (job_id, INSTR (job_id, '_') + 1)) > 3 AND SUBSTR (job_id, INSTR (job_id, '_') + 1) != 'CLERK';Таблица Employees. Получить список всех сотрудников заменив в значении PHONE_NUMBER все ‘.’ на ‘-‘
РешениеSELECT phone_number, REPLACE (phone_number, '.', '-') new_phone_number FROM employees;Using Conversion Functions and Conditional Expressions
Таблица Employees. Получить список всех сотрудников которые пришли на работу в первый день месяца (любого)
Решение
SELECT * FROM employees WHERE TO_CHAR (hire_date, 'DD') = '01';Таблица Employees. Получить список всех сотрудников которые пришли на работу в 2008ом году
РешениеSELECT * FROM employees WHERE TO_CHAR (hire_date, 'YYYY') = '2008';Таблица DUAL. Показать завтрашнюю дату в формате: Tomorrow is Second day of January
РешениеSELECT TO_CHAR (SYSDATE, 'fm""Tomorrow is ""Ddspth ""day of"" Month') info FROM DUAL;Таблица Employees. Получить список всех сотрудников и дату прихода на работу каждого в формате: 21st of June, 2007
Решение
SELECT first_name, TO_CHAR (hire_date, 'fmddth ""of"" Month, YYYY') hire_date FROM employees;Таблица Employees. Получить список работников с увеличенными зарплатами на 20%. Зарплату показать со знаком доллара
Решение
SELECT first_name, TO_CHAR (salary + salary * 0.20, 'fm$999,999.00') new_salary FROM employees;Таблица Employees. Получить список всех сотрудников которые приши на работу в феврале 2007го года.
РешениеSELECT * FROM employees WHERE hire_date BETWEEN TO_DATE ('01.02.2007', 'DD.MM.YYYY') AND LAST_DAY (TO_DATE ('01.02.2007', 'DD.MM.YYYY')); SELECT * FROM employees WHERE to_char(hire_date,'MM.YYYY') = '02.2007';Таблица DUAL. Вывезти актуальную дату, + секунда, + минута, + час, + день, + месяц, + год
РешениеSELECT SYSDATE now, SYSDATE + 1 / (24 * 60 * 60) plus_second, SYSDATE + 1 / (24 * 60) plus_minute, SYSDATE + 1 / 24 plus_hour, SYSDATE + 1 plus_day, ADD_MONTHS (SYSDATE, 1) plus_month, ADD_MONTHS (SYSDATE, 12) plus_year FROM DUAL;Таблица Employees. Получить список всех сотрудников с полными зарплатами (salary + commission_pct(%)) в формате: $24,000.00
Решение
SELECT first_name, salary, TO_CHAR (salary + salary * NVL (commission_pct, 0), 'fm$99,999.00') full_salary FROM employees;Таблица Employees. Получить список всех сотрудников и информацию о наличии бонусов к зарплате (Yes/No)
Решение
SELECT first_name, commission_pct, NVL2 (commission_pct, 'Yes', 'No') has_bonus FROM employees;Таблица Employees. Получить уровень зарплаты каждого сотрудника: Меньше 5000 считается Low level, Больше или равно 5000 и меньше 10000 считается Normal level, Больше иои равно 10000 считается High level
Решение
SELECT first_name, salary, CASE WHEN salary < 5000 THEN 'Low' WHEN salary >= 5000 AND salary < 10000 THEN 'Normal' WHEN salary >= 10000 THEN 'High' ELSE 'Unknown' END salary_level FROM employees;Таблица Countries. Для каждой страны показать регион в котором он находится: 1-Europe, 2-America, 3-Asia, 4-Africa (без Join)
Решение
SELECT country_name country, DECODE (region_id, 1, 'Europe', 2, 'America', 3, 'Asia', 4, 'Africa', 'Unknown') region FROM countries; SELECT country_name country, CASE region_id WHEN 1 THEN 'Europe' WHEN 2 THEN 'America' WHEN 3 THEN 'Asia' WHEN 4 THEN 'Africa' ELSE 'Unknown' END region FROM countries;Reporting Aggregated Data Using the Group Functions
Таблица Employees. Получить репорт по department_id с минимальной и максимальной зарплатой, с ранней и поздней датой прихода на работу и с количествов сотрудников. Сорировать по количеству сотрудников (по убыванию)
Решение
SELECT department_id, MIN (salary) min_salary, MAX (salary) max_salary, MIN (hire_date) min_hire_date, MAX (hire_date) max_hire_Date, COUNT (*) count FROM employees GROUP BY department_id order by count(*) desc;Таблица Employees. Сколько сотрудников имена которых начинается с одной и той же буквы? Сортировать по количеству. Показывать только те где количество больше 1
Решение
SELECT SUBSTR (first_name, 1, 1) first_char, COUNT (*) FROM employees GROUP BY SUBSTR (first_name, 1, 1) HAVING COUNT (*) > 1 ORDER BY 2 DESC;Таблица Employees. Сколько сотрудников которые работают в одном и тоже отделе и получают одинаковую зарплату?
Решение
SELECT department_id, salary, COUNT (*) FROM employees GROUP BY department_id, salary HAVING COUNT (*) > 1;Таблица Employees. Получить репорт сколько сотрудников приняли на работу в каждый день недели. Сортировать по количеству
Решение
SELECT TO_CHAR (hire_Date, 'Day') day, COUNT (*) FROM employees GROUP BY TO_CHAR (hire_Date, 'Day') ORDER BY 2 DESC;Таблица Employees. Получить репорт сколько сотрудников приняли на работу по годам. Сортировать по количеству
Решение
SELECT TO_CHAR (hire_date, 'YYYY') year, COUNT (*) FROM employees GROUP BY TO_CHAR (hire_date, 'YYYY');Таблица Employees. Получить количество департаментов в котором есть сотрудники
РешениеSELECT COUNT (COUNT (*)) department_count FROM employees WHERE department_id IS NOT NULL GROUP BY department_id;Таблица Employees. Получить список department_id в котором работают больше 30 сотрудников
РешениеSELECT department_id FROM employees GROUP BY department_id HAVING COUNT (*) > 30;Таблица Employees. Получить список department_id и округленную среднюю зарплату работников в каждом департаменте.
Решение
SELECT department_id, ROUND (AVG (salary)) avg_salary FROM employees GROUP BY department_id;Таблица Countries. Получить список region_id сумма всех букв всех country_name в котором больше 60ти
РешениеSELECT region_id FROM countries GROUP BY region_id HAVING SUM (LENGTH (country_name)) > 60;Таблица Employees. Получить список department_id в котором работают работники нескольких (>1) job_id
РешениеSELECT department_id FROM employees GROUP BY department_id HAVING COUNT (DISTINCT job_id) > 1;Таблица Employees. Получить список manager_id у которых количество подчиненных больше 5 и сумма всех зарплат его подчиненных больше 50000
Решение
SELECT manager_id FROM employees GROUP BY manager_id HAVING COUNT (*) > 5 AND SUM (salary) > 50000;Таблица Employees. Получить список manager_id у которых средняя зарплата всех его подчиненных находится в промежутке от 6000 до 9000 которые не получают бонусы (commission_pct пустой)
Решение
SELECT manager_id, AVG (salary) avg_salary FROM employees WHERE commission_pct IS NULL GROUP BY manager_id HAVING AVG (salary) BETWEEN 6000 AND 9000;Таблица Employees. Получить максимальную зарплату из всех сотрудников job_id которыз заканчивается на слово ‘CLERK’
Решение
SELECT MAX (salary) max_salary FROM employees WHERE job_id LIKE '%CLERK'; SELECT MAX (salary) max_salary FROM employees WHERE SUBSTR (job_id, -5) = 'CLERK';Таблица Employees. Получить максимальную зарплату среди всех средних зарплат по департаменту
РешениеSELECT MAX (AVG (salary)) FROM employees GROUP BY department_id;Таблица Employees. Получить количество сотрудников с одинаковым количеством букв в имени. При этом показать только тех у кого длина имени больше 5 и количество сотрудников с таким именем больше 20. Сортировать по длинне имени
Решение
SELECT LENGTH (first_name), COUNT (*) FROM employees GROUP BY LENGTH (first_name) HAVING LENGTH (first_name) > 5 AND COUNT (*) > 20 ORDER BY LENGTH (first_name); SELECT LENGTH (first_name), COUNT (*) FROM employees WHERE LENGTH (first_name) > 5 GROUP BY LENGTH (first_name) HAVING COUNT (*) > 20 ORDER BY LENGTH (first_name);Displaying Data from Multiple Tables Using Joins
Таблица Employees, Departaments, Locations, Countries, Regions. Получить список регионов и количество сотрудников в каждом регионе
Решение
SELECT region_name, COUNT (*) FROM employees e JOIN departments d ON (e.department_id = d.department_id) JOIN locations l ON (d.location_id = l.location_id) JOIN countries c ON (l.country_id = c.country_id) JOIN regions r ON (c.region_id = r.region_id) GROUP BY region_name;Таблица Employees, Departaments, Locations, Countries, Regions. Получить детальную информацию о каждом сотруднике:
First_name, Last_name, Departament, Job, Street, Country, RegionРешение
SELECT First_name, Last_name, Department_name, Job_id, street_address, Country_name, Region_name FROM employees e JOIN departments d ON (e.department_id = d.department_id) JOIN locations l ON (d.location_id = l.location_id) JOIN countries c ON (l.country_id = c.country_id) JOIN regions r ON (c.region_id = r.region_id);Таблица Employees. Показать всех менеджеров которые имеют в подчинении больше 6ти сотрудников
РешениеSELECT man.first_name, COUNT (*) FROM employees emp JOIN employees man ON (emp.manager_id = man.employee_id) GROUP BY man.first_name HAVING COUNT (*) > 6;Таблица Employees. Показать всех сотрудников которые ни кому не подчиняются
РешениеSELECT emp.first_name FROM employees emp LEFT JOIN employees man ON (emp.manager_id = man.employee_id) WHERE man.FIRST_NAME IS NULL; SELECT first_name FROM employees WHERE manager_id IS NULL;Таблица Employees, Job_history. В таблице Employee хранятся все сотрудники. В таблице Job_history хранятся сотрудники которые покинули компанию. Получить репорт о всех сотрудниках и о его статусе в компании (Работает или покинул компанию с датой ухода)
Пример:
first_name | status
Jennifer | Left the company at 31 of December, 2006
Clara | Currently WorkingРешение
SELECT first_name, NVL2 ( end_date, TO_CHAR (end_date, 'fm""Left the company at"" DD ""of"" Month, YYYY'), 'Currently Working') status FROM employees e LEFT JOIN job_history j ON (e.employee_id = j.employee_id);Таблица Employees, Departaments, Locations, Countries, Regions. Получить список сотрудников которые живут в Europe (region_name)
Решение
SELECT first_name FROM employees JOIN departments USING (department_id) JOIN locations USING (location_id) JOIN countries USING (country_id) JOIN regions USING (region_id) WHERE region_name = 'Europe'; SELECT first_name FROM employees e JOIN departments d ON (e.department_id = d.department_id) JOIN locations l ON (d.location_id = l.location_id) JOIN countries c ON (l.country_id = c.country_id) JOIN regions r ON (c.region_id = r.region_id) WHERE region_name = 'Europe';Таблица Employees, Departaments. Показать все департаменты в которых работают больше 30ти сотрудников
Решение
SELECT department_name, COUNT (*) FROM employees e JOIN departments d ON (e.department_id = d.department_id) GROUP BY department_name HAVING COUNT (*) > 30;Таблица Employees, Departaments. Показать всех сотрудников которые не состоят ни в одном департаменте
Решение
SELECT first_name FROM employees e LEFT JOIN departments d ON (e.department_id = d.department_id) WHERE d.department_name IS NULL; SELECT first_name FROM employees WHERE department_id IS NULL;Таблица Employees, Departaments. Показать все департаменты в которых нет ни одного сотрудника
РешениеSELECT department_name FROM employees e RIGHT JOIN departments d ON (e.department_id = d.department_id) WHERE first_name IS NULL;Таблица Employees. Показать всех сотрудников у которых нет ни кого в подчинении
РешениеSELECT man.first_name FROM employees emp RIGHT JOIN employees man ON (emp.manager_id = man.employee_id) WHERE emp.FIRST_NAME IS NULL;Таблица Employees, Jobs, Departaments. Показать сотрудников в формате: First_name, Job_title, Department_name.
Пример:
First_name | Job_title | Department_name
Donald | Shipping | Clerk ShippingРешение
SELECT first_name, job_title, department_name FROM employees e JOIN jobs j ON (e.job_id = j.job_id) JOIN departments d ON (d.department_id = e.department_id);Таблица Employees. Получить список сотрудников менеджеры которых устроились на работу в 2005ом году но при это сами эти работники устроились на работу до 2005 года
Решение
SELECT emp.* FROM employees emp JOIN employees man ON (emp.manager_id = man.employee_id) WHERE TO_CHAR (man.hire_date, 'YYYY') = '2005' AND emp.hire_date < TO_DATE ('01012005', 'DDMMYYYY');Таблица Employees. Получить список сотрудников менеджеры которых устроились на работу в январе месяце любого года и длина job_title этих сотрудников больше 15ти символов
Решение
SELECT emp.* FROM employees emp JOIN employees man ON (emp.manager_id = man.employee_id) JOIN jobs j ON (emp.job_id = j.job_id) WHERE TO_CHAR (man.hire_date, 'MM') = '01' AND LENGTH (j.job_title) > 15;Using Subqueries to Solve Queries
Таблица Employees. Получить список сотрудников с самым длинным именем.
Решение
SELECT * FROM employees WHERE LENGTH (first_name) = (SELECT MAX (LENGTH (first_name)) FROM employees);Таблица Employees. Получить список сотрудников с зарплатой большей средней зарплаты всех сотрудников.
Решение
SELECT * FROM employees WHERE salary > (SELECT AVG (salary) FROM employees);Таблица Employees, Departments, Locations. Получить город в котором сотрудники в сумме зарабатывают меньше всех.
Решение
SELECT city FROM employees e JOIN departments d ON (e.department_id = d.department_id) JOIN locations l ON (d.location_id = l.location_id) GROUP BY city HAVING SUM (salary) = ( SELECT MIN (SUM (salary)) FROM employees e JOIN departments d ON (e.department_id = d.department_id) JOIN locations l ON (d.location_id = l.location_id) GROUP BY city);Таблица Employees. Получить список сотрудников у которых менеджер получает зарплату больше 15000.
РешениеSELECT * FROM employees WHERE manager_id IN (SELECT employee_id FROM employees WHERE salary > 15000)Таблица Employees, Departaments. Показать все департаменты в которых нет ни одного сотрудника
РешениеSELECT * FROM departments WHERE department_id NOT IN (SELECT department_id FROM employees WHERE department_id IS NOT NULL);Таблица Employees. Показать всех сотрудников которые не являются менеджерами
РешениеSELECT * FROM employees WHERE employee_id NOT IN (SELECT manager_id FROM employees WHERE manager_id IS NOT NULL)Таблица Employees. Показать всех менеджеров которые имеют в подчинении больше 6ти сотрудников
РешениеSELECT * FROM employees e WHERE (SELECT COUNT (*) FROM employees WHERE manager_id = e.employee_id) > 6;Таблица Employees, Departaments. Показать сотрудников которые работают в департаменте IT
РешениеSELECT * FROM employees WHERE department_id = (SELECT department_id FROM departments WHERE department_name = 'IT');Таблица Employees, Jobs, Departaments. Показать сотрудников в формате: First_name, Job_title, Department_name.
Пример:
First_name | Job_title | Department_name
Donald | Shipping | Clerk ShippingРешение
SELECT first_name, (SELECT job_title FROM jobs WHERE job_id = e.job_id) job_title, (SELECT department_name FROM departments WHERE department_id = e.department_id) department_name FROM employees e;Таблица Employees. Получить список сотрудников менеджеры которых устроились на работу в 2005ом году но при это сами эти работники устроились на работу до 2005 года
Решение
SELECT * FROM employees WHERE manager_id IN (SELECT employee_id FROM employees WHERE TO_CHAR (hire_date, 'YYYY') = '2005') AND hire_date < TO_DATE ('01012005', 'DDMMYYYY');Таблица Employees. Получить список сотрудников менеджеры которых устроились на работу в январе месяце любого года и длина job_title этих сотрудников больше 15ти символов
Решение
SELECT * FROM employees e WHERE manager_id IN (SELECT employee_id FROM employees WHERE TO_CHAR (hire_date, 'MM') = '01') AND (SELECT LENGTH (job_title) FROM jobs WHERE job_id = e.job_id) > 15;На этом пока всё.
Надеюсь, задачи были интересными и увлекательными.
Буду по возможности дополнять этот список задач.
Также буду рад любым замечаниям и предложениям.P.S.: Если кому то в голову придет интересная задача на SELECT, пишите в коментариях, добавлю в список.
- Занимательные задачки
- Oracle
- SQL
- Учебный процесс в IT
[Перевод] Обработка ошибок и транзакций в SQL Server. Часть 1. Обработка ошибок – быстрый старт
Привет, Хабр! Представляю вашему вниманию перевод статьи «Error and Transaction Handling in SQL Server. Part One – Jumpstart Error Handling» автора Erland Sommarskog.
1. Введение
Эта статья – первая в серии из трёх статей, посвященных обработке ошибок и транзакций в SQL Server. Её цель – дать вам быстрый старт в теме обработки ошибок, показав базовый пример, который подходит для большей части вашего кода. Эта часть написана в расчете на неопытного читателя, и по этой причине я намеренно умалчиваю о многих деталях. В данный момент задача состоит в том, чтобы рассказать как без упора на почему. Если вы принимаете мои слова на веру, вы можете прочесть только эту часть и отложить остальные две для дальнейших этапов в вашей карьере.
С другой стороны, если вы ставите под сомнение мои рекомендации, вам определенно необходимо прочитать две остальные части, где я погружаюсь в детали намного более глубоко, исследуя очень запутанный мир обработки ошибок и транзакций в SQL Server. Вторая и третья части, так же, как и три приложения, предназначены для читателей с более глубоким опытом. Первая статья — короткая, вторая и третья значительно длиннее.
Все статьи описывают обработку ошибок и транзакций в SQL Server для версии 2005 и более поздних версий.
1.1 Зачем нужна обработка ошибок?
Почему мы обрабатываем ошибки в нашем коде? На это есть много причин. Например, на формах в приложении мы проверяем введенные данные и информируем пользователей о допущенных при вводе ошибках. Ошибки пользователя – это предвиденные ошибки. Но нам также нужно обрабатывать непредвиденные ошибки. То есть, ошибки могут возникнуть из-за того, что мы что-то упустили при написании кода. Простой подход – это прервать выполнение или хотя бы вернуться на этап, в котором мы имеем полный контроль над происходящим. Недостаточно будет просто подчеркнуть, что совершенно непозволительно игнорировать непредвиденные ошибки. Это недостаток, который может вызвать губительные последствия: например, стать причиной того, что приложение будет предоставлять некорректную информацию пользователю или, что еще хуже, сохранять некорректные данные в базе. Также важно сообщать о возникновении ошибки с той целью, чтобы пользователь не думал о том, что операция прошла успешно, в то время как ваш код на самом деле ничего не выполнил.
Мы часто хотим, чтобы в базе данных изменения были атомарными. Например, задача по переводу денег с одного счета на другой. С этой целью мы должны изменить две записи в таблице CashHoldings и добавить две записи в таблицу Transactions. Абсолютно недопустимо, чтобы ошибки или сбой привели к тому, что деньги будут переведены на счет получателя, а со счета отправителя они не будут списаны. По этой причине обработка ошибок также касается и обработки транзакций. В приведенном примере нам нужно обернуть операцию в BEGIN TRANSACTION и COMMIT TRANSACTION, но не только это: в случае ошибки мы должны убедиться, что транзакция откачена.
2. Основные команды
Мы начнем с обзора наиболее важных команд, которые необходимы для обработки ошибок. Во второй части я опишу все команды, относящиеся к обработке ошибок и транзакций.
2.1 TRY-CATCH
Основным механизмом обработки ошибок является конструкция TRY-CATCH, очень напоминающая подобные конструкции в других языках. Структура такова:
BEGIN TRY END TRY BEGIN CATCH END CATCHЕсли какая-либо ошибка появится в , выполнение будет переведено в блок CATCH, и будет выполнен код обработки ошибок.
Как правило, в CATCH откатывают любую открытую транзакцию и повторно вызывают ошибку. Таким образом, вызывающая клиентская программа понимает, что что-то пошло не так. Повторный вызов ошибки мы обсудим позже в этой статье.
Вот очень быстрый пример:
BEGIN TRY DECLARE @x int SELECT @x = 1/0 PRINT 'Not reached' END TRY BEGIN CATCH PRINT 'This is the error: ' + error_message() END CATCHРезультат выполнения: This is the error: Divide by zero error encountered.
Мы вернемся к функции error_message() позднее. Стоит отметить, что использование PRINT в обработчике CATCH приводится только в рамках экспериментов и не следует делать так в коде реального приложения.
Если вызывает хранимую процедуру или запускает триггеры, то любая ошибка, которая в них возникнет, передаст выполнение в блок CATCH. Если более точно, то, когда возникает ошибка, SQL Server раскручивает стек до тех пор, пока не найдёт обработчик CATCH. И если такого обработчика нет, SQL Server отправляет сообщение об ошибке напрямую клиенту.
Есть одно очень важное ограничение у конструкции TRY-CATCH, которое нужно знать: она не ловит ошибки компиляции, которые возникают в той же области видимости. Рассмотрим пример:
CREATE PROCEDURE inner_sp AS BEGIN TRY PRINT 'This prints' SELECT * FROM NoSuchTable PRINT 'This does not print' END TRY BEGIN CATCH PRINT 'And nor does this print' END CATCH go EXEC inner_spThis prints Msg 208, Level 16, State 1, Procedure inner_sp, Line 4 Invalid object name 'NoSuchTable'Как можно видеть, блок TRY присутствует, но при возникновении ошибки выполнение не передается блоку CATCH, как это ожидалось. Это применимо ко всем ошибкам компиляции, таким как пропуск колонок, некорректные псевдонимы и тому подобное, которые возникают во время выполнения. (Ошибки компиляции могут возникнуть в SQL Server во время выполнения из-за отложенного разрешения имен – особенность, благодаря которой SQL Server позволяет создать процедуру, которая обращается к несуществующим таблицам.)
Эти ошибки не являются полностью неуловимыми; вы не можете поймать их в области, в которой они возникают, но вы можете поймать их во внешней области. Добавим такой код к предыдущему примеру:
CREATE PROCEDURE outer_sp AS BEGIN TRY EXEC inner_sp END TRY BEGIN CATCH PRINT 'The error message is: ' + error_message() END CATCH go EXEC outer_spТеперь мы получим на выходе это:
This prints The error message is: Invalid object name 'NoSuchTable'.На этот раз ошибка была перехвачена, потому что сработал внешний обработчик CATCH.
2.2 SET XACT_ABORT ON
В начало ваших хранимых процедур следует всегда добавлять это выражение:
SET XACT_ABORT, NOCOUNT ONОно активирует два параметра сессии, которые выключены по умолчанию в целях совместимости с предыдущими версиями, но опыт доказывает, что лучший подход – это иметь эти параметры всегда включенными. Поведение SQL Server по умолчанию в той ситуации, когда не используется TRY-CATCH, заключается в том, что некоторые ошибки прерывают выполнение и откатывают любые открытые транзакции, в то время как с другими ошибками выполнение последующих инструкций продолжается. Когда вы включаете XACT_ABORT ON, почти все ошибки начинают вызывать одинаковый эффект: любая открытая транзакция откатывается, и выполнение кода прерывается. Есть несколько исключений, среди которых наиболее заметным является выражение RAISERROR.
Параметр XACT_ABORT необходим для более надежной обработки ошибок и транзакций. В частности, при настройках по умолчанию есть несколько ситуаций, когда выполнение может быть прервано без какого-либо отката транзакции, даже если у вас есть TRY-CATCH. Мы видели такой пример в предыдущем разделе, где мы выяснили, что TRY-CATCH не перехватывает ошибки компиляции, возникшие в той же области. Открытая транзакция, которая не была откачена из-за ошибки, может вызвать серьезные проблемы, если приложение работает дальше без завершения транзакции или ее отката.
Для надежной обработки ошибок в SQL Server вам необходимы как TRY-CATCH, так и SET XACT_ABORT ON. Среди них инструкция SET XACT_ABORT ON наиболее важна. Если для кода на промышленной среде только на нее полагаться не стоит, то для быстрых и простых решений она вполне подходит.
Параметр NOCOUNT не имеет к обработке ошибок никакого отношения, но включение его в код является хорошей практикой. NOCOUNT подавляет сообщения вида (1 row(s) affected), которые вы можете видеть в панели Message в SQL Server Management Studio. В то время как эти сообщения могут быть полезны при работе c SSMS, они могут негативно повлиять на производительность в приложении, так как увеличивают сетевой трафик. Сообщение о количестве строк также может привести к ошибке в плохо написанных клиентских приложениях, которые могут подумать, что это данные, которые вернул запрос.
Выше я использовал синтаксис, который немного необычен. Большинство людей написали бы два отдельных выражения:
SET NOCOUNT ON SET XACT_ABORT ONМежду ними нет никакого отличия. Я предпочитаю версию с SET и запятой, т.к. это снижает уровень шума в коде. Поскольку эти выражения должны появляться во всех ваших хранимых процедурах, они должны занимать как можно меньше места.
3. Основной пример обработки ошибок
После того, как мы посмотрели на TRY-CATCH и SET XACT_ABORT ON, давайте соединим их вместе в примере, который мы можем использовать во всех наших хранимых процедурах. Для начала я покажу пример, в котором ошибка генерируется в простой форме, а в следующем разделе я рассмотрю решения получше.
Для примера я буду использовать эту простую таблицу.
CREATE TABLE sometable(a int NOT NULL, b int NOT NULL, CONSTRAINT pk_sometable PRIMARY KEY(a, b))Вот хранимая процедура, которая демонстрирует, как вы должны работать с ошибками и транзакциями.
CREATE PROCEDURE insert_data @a int, @b int AS SET XACT_ABORT, NOCOUNT ON BEGIN TRY BEGIN TRANSACTION INSERT sometable(a, b) VALUES (@a, @b) INSERT sometable(a, b) VALUES (@b, @a) COMMIT TRANSACTION END TRY BEGIN CATCH IF @@trancount > 0 ROLLBACK TRANSACTION DECLARE @msg nvarchar(2048) = error_message() RAISERROR (@msg, 16, 1) RETURN 55555 END CATCHПервая строка в процедуре включает XACT_ABORT и NOCOUNT в одном выражении, как я показывал выше. Эта строка – единственная перед BEGIN TRY. Все остальное в процедуре должно располагаться после BEGIN TRY: объявление переменных, создание временных таблиц, табличных переменных, всё. Даже если у вас есть другие SET-команды в процедуре (хотя причины для этого встречаются редко), они должны идти после BEGIN TRY.
Причина, по которой я предпочитаю указывать SET XACT_ABORT и NOCOUNT перед BEGIN TRY, заключается в том, что я рассматриваю это как одну строку шума: она всегда должна быть там, но я не хочу, чтобы это мешало взгляду. Конечно же, это дело вкуса, и если вы предпочитаете ставить SET-команды после BEGIN TRY, ничего страшного. Важно то, что вам не следует ставить что-либо другое перед BEGIN TRY.
Часть между BEGIN TRY и END TRY является основной составляющей процедуры. Поскольку я хотел использовать транзакцию, определенную пользователем, я ввел довольно надуманное бизнес-правило, в котором говорится, что если вы вставляете пару, то обратная пара также должна быть вставлена. Два выражения INSERT находятся внутри BEGIN и COMMIT TRANSACTION. Во многих случаях у вас будет много строк кода между BEGIN TRY и BEGIN TRANSACTION. Иногда у вас также будет код между COMMIT TRANSACTION и END TRY, хотя обычно это только финальный SELECT, возвращающий данные или присваивающий значения выходным параметрам. Если ваша процедура не выполняет каких-либо изменений или имеет только одно выражение INSERT/UPDATE/DELETE/MERGE, то обычно вам вообще не нужно явно указывать транзакцию.
В то время как блок TRY будет выглядеть по-разному от процедуры к процедуре, блок CATCH должен быть более или менее результатом копирования и вставки. То есть вы делаете что-то короткое и простое и затем используете повсюду, не особо задумываясь. Обработчик CATCH, приведенный выше, выполняет три действия:
- Откатывает любые открытые транзакции.
- Повторно вызывает ошибку.
- Убеждается, что возвращаемое процедурой значение отлично от нуля.
IF @@trancount > 0 ROLLBACK TRANSACTIONне нужна, если нет явной транзакции в процедуре, но это абсолютно неверно. Возможно, вы вызываете хранимую процедуру, которая открывает транзакцию, но которая не может ее откатить из-за ограничений TRY-CATCH. Возможно, вы или кто-то другой добавите явную транзакцию через два года. Вспомните ли вы тогда о том, что нужно добавить строку с откатом? Не рассчитывайте на это. Я также слышу читателей, которые возражают, что если тот, кто вызывает процедуру, открыл транзакцию, мы не должны ее откатывать… Нет, мы должны, и если вы хотите знать почему, вам нужно прочитать вторую и третью части. Откат транзакции в обработчике CATCH – это категорический императив, у которого нет исключений.
Код повторной генерации ошибки включает такую строку:
DECLARE @msg nvarchar(2048) = error_message()Встроенная функция error_message() возвращает текст возникшей ошибки. В следующей строке ошибка повторно вызывается с помощью выражения RAISERROR. Это не самый простой способ вызова ошибки, но он работает. Другие способы мы рассмотрим в следующей главе.
Замечание: синтаксис для присвоения начального значения переменной в DECLARE был внедрен в SQL Server 2008. Если у вас SQL Server 2005, вам нужно разбить строку на DECLARE и выражение SELECT.
Финальное выражение RETURN – это страховка. RAISERROR никогда не прерывает выполнение, поэтому выполнение следующего выражения будет продолжено. Пока все процедуры используют TRY-CATCH, а также весь клиентский код обрабатывает исключения, нет повода для беспокойства. Но ваша процедура может быть вызвана из старого кода, написанного до SQL Server 2005 и до внедрения TRY-CATCH. В те времена лучшее, что мы могли делать, это смотреть на возвращаемые значения. То, что вы возвращаете с помощью RETURN, не имеет особого значения, если это не нулевое значение (ноль обычно обозначает успешное завершение работы).
Последнее выражение в процедуре – это END CATCH. Никогда не следует помещать какой-либо код после END CATCH. Кто-нибудь, читающий процедуру, может не увидеть этот кусок кода.
После прочтения теории давайте попробуем тестовый пример:
EXEC insert_data 9, NULLMsg 50000, Level 16, State 1, Procedure insert_data, Line 12 Cannot insert the value NULL into column 'b', table 'tempdb.dbo.sometable'; column does not allow nulls. INSERT fails.Давайте добавим внешнюю процедуру для того, чтобы увидеть, что происходит при повторном вызове ошибки:
CREATE PROCEDURE outer_sp @a int, @b int AS SET XACT_ABORT, NOCOUNT ON BEGIN TRY EXEC insert_data @a, @b END TRY BEGIN CATCH IF @@trancount > 0 ROLLBACK TRANSACTION DECLARE @msg nvarchar(2048) = error_message() RAISERROR (@msg, 16, 1) RETURN 55555 END CATCH go EXEC outer_sp 8, 8Msg 50000, Level 16, State 1, Procedure outer_sp, Line 9 Violation of PRIMARY KEY constraint 'pk_sometable'. Cannot insert duplicate key in object 'dbo.sometable'. The duplicate key value is (8, 8).Мы получили корректное сообщение об ошибке, но если вы посмотрите на заголовки этого сообщения и на предыдущее поближе, то можете заметить проблему:
Msg 50000, Level 16, State 1, Procedure insert_data, Line 12 Msg 50000, Level 16, State 1, Procedure outer_sp, Line 9Сообщение об ошибке выводит информацию о расположении конечного выражения RAISERROR. В первом случае некорректен только номер строки. Во втором случае некорректно также имя процедуры. Для простых процедур, таких как наш тестовый пример, это не является большой проблемой. Но если у вас есть несколько уровней вложенных сложных процедур, то наличие сообщения об ошибке с отсутствием указания на место её возникновения сделает поиск и устранение ошибки намного более сложным делом. По этой причине желательно генерировать ошибку таким образом, чтобы можно было определить нахождение ошибочного фрагмента кода быстро, и это то, что мы рассмотрим в следующей главе.
4. Три способа генерации ошибки
4.1 Использование error_handler_sp
Мы рассмотрели функцию error_message(), которая возвращает текст сообщения об ошибке. Сообщение об ошибке состоит из нескольких компонентов, и существует своя функция error_xxx() для каждого из них. Мы можем использовать их для повторной генерации полного сообщения, которое содержит оригинальную информацию, хотя и в другом формате. Если делать это в каждом обработчике CATCH, это будет большой недостаток — дублирование кода. Вам не обязательно находиться в блоке CATCH для вызова error_message() и других подобных функций, и они вернут ту же самую информацию, если будут вызваны из хранимой процедуры, которую выполнит блок CATCH.
Позвольте представить вам error_handler_sp:
CREATE PROCEDURE error_handler_sp AS DECLARE @errmsg nvarchar(2048), @severity tinyint, @state tinyint, @errno int, @proc sysname, @lineno int SELECT @errmsg = error_message(), @severity = error_severity(), @state = error_state(), @errno = error_number(), @proc = error_procedure(), @lineno = error_line() IF @errmsg NOT LIKE '***%' BEGIN SELECT @errmsg = '*** ' + coalesce(quotename(@proc), '') + ', Line ' + ltrim(str(@lineno)) + '. Errno ' + ltrim(str(@errno)) + ': ' + @errmsg END RAISERROR('%s', @severity, @state, @errmsg)Первое из того, что делает error_handler_sp – это сохраняет значение всех error_xxx() функций в локальные переменные. Я вернусь к выражению IF через секунду. Вместо него давайте посмотрим на выражение SELECT внутри IF:
SELECT @errmsg = '*** ' + coalesce(quotename(@proc), '') + ', Line ' + ltrim(str(@lineno)) + '. Errno ' + ltrim(str(@errno)) + ': ' + @errmsgЦель этого SELECT заключается в форматировании сообщения об ошибке, которое передается в RAISERROR. Оно включает в себя всю информацию из оригинального сообщения об ошибке, которое мы не можем вставить напрямую в RAISERROR. Мы должны обработать имя процедуры, которое может быть NULL для ошибок в обычных скриптах или в динамическом SQL. Поэтому используется функция COALESCE. (Если вы не понимаете форму выражения RAISERROR, я рассказываю о нем более детально во второй части.)
Отформатированное сообщение об ошибке начинается с трех звездочек. Этим достигаются две цели: 1) Мы можем сразу видеть, что это сообщение вызвано из обработчика CATCH. 2) Это дает возможность для error_handler_sp отфильтровать ошибки, которые уже были сгенерированы один или более раз, с помощью условия NOT LIKE ‘***%’ для того, чтобы избежать изменения сообщения во второй раз.
Вот как обработчик CATCH должен выглядеть, когда вы используете error_handler_sp:
BEGIN CATCH IF @@trancount > 0 ROLLBACK TRANSACTION EXEC error_handler_sp RETURN 55555 END CATCHДавайте попробуем несколько тестовых сценариев.
EXEC insert_data 8, NULL EXEC outer_sp 8, 8Msg 50000, Level 16, State 2, Procedure error_handler_sp, Line 20 *** [insert_data], Line 5. Errno 515: Cannot insert the value NULL into column 'b', table 'tempdb.dbo.sometable'; column does not allow nulls. INSERT fails. Msg 50000, Level 14, State 1, Procedure error_handler_sp, Line 20 *** [insert_data], Line 6. Errno 2627: Violation of PRIMARY KEY constraint 'pk_sometable'. Cannot insert duplicate key in object 'dbo.sometable'. The duplicate key value is (8, 8).Заголовки сообщений говорят о том, что ошибка возникла в процедуре error_handler_sp, но текст сообщений об ошибках дает нам настоящее местонахождение ошибки – как название процедуры, так и номер строки.
Я покажу еще два метода вызова ошибок. Однако error_handler_sp является моей главной рекомендацией для читателей, которые читают эту часть. Это — простой вариант, который работает на всех версиях SQL Server начиная с 2005. Существует только один недостаток: в некоторых случаях SQL Server генерирует два сообщения об ошибках, но функции error_xxx() возвращают только одну из них, и поэтому одно из сообщений теряется. Это может быть неудобно при работе с административными командами наподобие BACKUP\RESTORE, но проблема редко возникает в коде, предназначенном чисто для приложений.
4.2. Использование ;THROW
В SQL Server 2012 Microsoft представил выражение ;THROW для более легкой обработки ошибок. К сожалению, Microsoft сделал серьезную ошибку при проектировании этой команды и создал опасную ловушку.
С выражением ;THROW вам не нужно никаких хранимых процедур. Ваш обработчик CATCH становится таким же простым, как этот:
BEGIN CATCH IF @@trancount > 0 ROLLBACK TRANSACTION ;THROW RETURN 55555 END CATCHДостоинство ;THROW в том, что сообщение об ошибке генерируется точно таким же, как и оригинальное сообщение. Если изначально было два сообщения об ошибках, оба сообщения воспроизводятся, что делает это выражение еще привлекательнее. Как и со всеми другими сообщениями об ошибках, ошибки, сгенерированные ;THROW, могут быть перехвачены внешним обработчиком CATCH и воспроизведены. Если обработчика CATCH нет, выполнение прерывается, поэтому оператор RETURN в данном случае оказывается не нужным. (Я все еще рекомендую оставлять его, на случай, если вы измените свое отношение к ;THROW позже).
Если у вас SQL Server 2012 или более поздняя версия, измените определение insert_data и outer_sp и попробуйте выполнить тесты еще раз. Результат в этот раз будет такой:
Msg 515, Level 16, State 2, Procedure insert_data, Line 5 Cannot insert the value NULL into column 'b', table 'tempdb.dbo.sometable'; column does not allow nulls. INSERT fails. Msg 2627, Level 14, State 1, Procedure insert_data, Line 6 Violation of PRIMARY KEY constraint 'pk_sometable'. Cannot insert duplicate key in object 'dbo.sometable'. The duplicate key value is (8, 8).Имя процедуры и номер строки верны и нет никакого другого имени процедуры, которое может нас запутать. Также сохранены оригинальные номера ошибок.
В этом месте вы можете сказать себе: действительно ли Microsoft назвал команду ;THROW? Разве это не просто THROW? На самом деле, если вы посмотрите в Books Online, там не будет точки с запятой. Но точка с запятой должны быть. Официально они отделяют предыдущее выражение, но это опционально, и далеко не все используют точку с запятой в выражениях T-SQL. Более важно, что если вы пропустите точку с запятой перед THROW, то не будет никакой синтаксической ошибки. Но это повлияет на поведение при выполнении выражения, и это поведение будет непостижимым для непосвященных. При наличии активной транзакции вы получите сообщение об ошибке, которое будет полностью отличаться от оригинального. И еще хуже, что при отсутствии активной транзакции ошибка будет тихо выведена без обработки. Такая вещь, как пропуск точки с запятой, не должно иметь таких абсурдных последствий. Для уменьшения риска такого поведения, всегда думайте о команде как о ;THROW (с точкой с запятой).
Нельзя отрицать того, что ;THROW имеет свои преимущества, но точка с запятой не единственная ловушка этой команды. Если вы хотите использовать ее, я призываю вас прочитать по крайней мере вторую часть этой серии, где я раскрываю больше деталей о команде ;THROW. До этого момента, используйте error_handler_sp.
4.3. Использование SqlEventLog
Третий способ обработки ошибок – это использование SqlEventLog, который я описываю очень детально в третьей части. Здесь я лишь сделаю короткий обзор.
SqlEventLog предоставляет хранимую процедуру slog.catchhandler_sp, которая работает так же, как и error_handler_sp: она использует функции error_xxx() для сбора информации и выводит сообщение об ошибке, сохраняя всю информацию о ней. Вдобавок к этому, она логирует ошибку в таблицу splog.sqleventlog. В зависимости от типа приложения, которое у вас есть, эта таблица может быть очень ценным объектом.
Для использования SqlEventLog, ваш обработчик CATCH должен быть таким:
BEGIN CATCH IF @@trancount > 0 ROLLBACK TRANSACTION EXEC slog.catchhandler_sp @@procid RETURN 55555 END CATCH@@procid возвращает идентификатор объекта текущей хранимой процедуры. Это то, что SqlEventLog использует для логирования информации в таблицу. Используя те же тестовые сценарии, получим результат их работы с использованием catchhandler_sp:
Msg 50000, Level 16, State 2, Procedure catchhandler_sp, Line 125 Procedure insert_data, Line 5 Cannot insert the value NULL into column 'b', table 'tempdb.dbo.sometable'; column does not allow nulls. INSERT fails. Msg 50000, Level 14, State 1, Procedure catchhandler_sp, Line 125 Procedure insert_data, Line 6 Violation of PRIMARY KEY constraint 'pk_sometable'. Cannot insert duplicate key in object 'dbo.sometable'. The duplicate key value is (8, 8).Как вы видите, сообщение об ошибке отформатировано немного не так, как это делает error_handler_sp, но основная идея такая же. Вот образец того, что было записано в таблицу slog.sqleventlog:
logid logdate errno severity logproc linenum msgtext 1 2015-01-25 22:40:24.393 515 16 insert_data 5 Cannot insert . 2 2015-01-25 22:40:24.395 2627 14 insert_data 6 Violation of . Если вы хотите попробовать SqlEventLog, вы можете загрузить файл sqleventlog.zip. Инструкция по установке находится в третьей части, раздел Установка SqlEventLog.
5. Финальные замечания
Вы изучили основной образец для обработки ошибок и транзакций в хранимых процедурах. Он не идеален, но он должен работать в 90-95% вашего кода. Есть несколько ограничений, на которые стоит обратить внимание:
- Как мы видели, ошибки компиляции не могут быть перехвачены в той же процедуре, в которой они возникли, а только во внешней процедуре.
- Пример не работает с пользовательскими функциями, так как ни TRY-CATCH, ни RAISERROR нельзя в них использовать.
- Когда хранимая процедура на Linked Server вызывает ошибку, эта ошибка может миновать обработчик в хранимой процедуре на локальном сервере и отправиться напрямую клиенту.
- Когда процедура вызвана как INSERT-EXEC, вы получите неприятную ошибку, потому что ROLLBACK TRANSACTION не допускается в данном случае.
- Как упомянуто выше, если вы используете error_handler_sp или SqlEventLog, мы потеряете одно сообщение, когда SQL Server выдаст два сообщения для одной ошибки. При использовании ;THROW такой проблемы нет.
Перед тем как закончить, я хочу кратко коснуться триггеров и клиентского кода.
Триггеры
Пример для обработки ошибок в триггерах не сильно отличается от того, что используется в хранимых процедурах, за исключением одной маленькой детали: вы не должны использовать выражение RETURN (потому что RETURN не допускается использовать в триггерах).
С триггерами важно понимать, что они являются частью команды, которая запустила триггер, и в триггере вы находитесь внутри транзакции, даже если не используете BEGIN TRANSACTION.
Иногда я вижу на форумах людей, которые спрашивают, могут ли они написать триггер, который не откатывает в случае падения запустившую его команду. Ответ таков: нет способа сделать это надежно, поэтому не стоит даже пытаться. Если в этом есть необходимость, по возможности не следует использовать триггер вообще, а найти другое решение. Во второй и третьей частях я рассматриваю обработку ошибок в триггерах более подробно.Клиентский код
У вас должна быть обработка ошибок в коде клиента, если он имеет доступ к базе. То есть вы должны всегда предполагать, что при любом вызове что-то может пойти не так. Как именно внедрить обработку ошибок, зависит от конкретной среды.
Здесь я только обращу внимание на важную вещь: реакцией на ошибку, возвращенную SQL Server, должно быть завершение запроса во избежание открытых бесхозных транзакций:
IF @@trancount > 0 ROLLBACK TRANSACTIONЭто также применимо к знаменитому сообщению Timeout expired (которое является не сообщением от SQL Server, а от API).
6. Конец первой части
Это конец первой из трех частей серии. Если вы хотели изучить вопрос обработки ошибок быстро, вы можете закончить чтение здесь. Если вы настроены идти дальше, вам следует прочитать вторую часть, где наше путешествие по запутанным джунглям обработки ошибок и транзакций в SQL Server начинается по-настоящему.
… и не забывайте добавлять эту строку в начало ваших хранимых процедур:
SET XACT_ABORT, NOCOUNT ON- SQL
- SQL Server
- ms sql server
- обработка ошибок
- обработка транзакций
