Как получить список баз данных SQL Server из VB.NET

РазделVB.NET и WinFormsУровеньСредний Чтение7 минОпубликовано ТегиVB.NETSQL ServerADO.NET

Экран подключения к базе почти всегда просит выбрать сервер и базу из списка. В VB.NET список баз SQL Server получают одним запросом, но выбор способа влияет на права, версии и скорость.

Главное. Список баз SQL Server надёжнее всего получать запросом к sys.databases с фильтром HAS_DBACCESS и state_desc, подключаясь к master с коротким таймаутом. sp_databases и GetSchema удобны, но не учитывают права пользователя.

Где SQL Server хранит список баз

Каждый экземпляр SQL Server ведёт системный каталог в базе master. Начиная с версии 2005 список баз доступен через представление sys.databases, а в SQL Server 2000 ту же роль играла таблица master.dbo.sysdatabases. Старая таблица в новых версиях оставлена как представление совместимости, поэтому код, написанный под 2000, продолжает работать.

Для приложения это значит, что список баз получается обычным запросом SELECT. Подключаться можно к любой базе на сервере, но логичнее указать Initial Catalog=master: она есть всегда, и на неё есть права у любого логина.

  • sys.databases: имя, идентификатор, состояние, модель восстановления, уровень совместимости;
  • database_id от 1 до 4 занимают системные базы master, tempdb, model и msdb;
  • колонка state_desc показывает ONLINE, OFFLINE, RESTORING и другие состояния.

Запрос к sys.databases

Самый гибкий способ: прямой запрос к представлению. Он позволяет сразу отфильтровать системные базы, базы в состоянии OFFLINE и те, к которым у текущего логина нет доступа. Последнее делает функция HAS_DBACCESS: без неё пользователь увидит в списке базы, при выборе которых получит ошибку входа.

Строку подключения удобно собирать через SqlConnectionStringBuilder. Он экранирует значения и не даст сломать строку именем сервера с точкой с запятой.

DatabaseList.vbVB.NET
Imports System.Data.SqlClient
Imports System.Collections.Generic

Public Module DatabaseList
    ' Список пользовательских баз, доступных текущему логину
    Public Function GetDatabases(server As String) As List(Of String)
        Dim csb As New SqlConnectionStringBuilder()
        csb.DataSource = server
        csb.InitialCatalog = "master"
        csb.IntegratedSecurity = True
        csb.ConnectTimeout = 5 ' не держим пользователя 15 секунд

        Const sql As String =
            "SELECT name FROM sys.databases " &
            "WHERE database_id > 4 AND state_desc = 'ONLINE' " &
            "AND HAS_DBACCESS(name) = 1 ORDER BY name"

        Dim result As New List(Of String)
        Using cn As New SqlConnection(csb.ConnectionString),
              cmd As New SqlCommand(sql, cn)
            cn.Open()
            Using rd = cmd.ExecuteReader()
                While rd.Read()
                    result.Add(rd.GetString(0))
                End While
            End Using
        End Using
        Return result
    End Function
End Module

sp_databases и GetSchema

Если не хочется писать SQL, есть два готовых варианта. Системная процедура sp_databases возвращает имя базы, её размер в килобайтах и примечание. Она работает и в SQL Server 2000, и в новых версиях, но размер вычисляет по всем файлам каждой базы, поэтому на сервере с сотнями баз заметно медленнее простого SELECT.

Второй вариант: метод GetSchema у открытого SqlConnection с коллекцией «Databases». Он возвращает DataTable с колонками database_name, dbid и create_date, которую можно сразу отдать в DataSource комбобокса. Внутри метод выполняет почти тот же запрос, но без фильтрации по правам и состоянию.

SchemaDatabases.vbVB.NET
Imports System.Data
Imports System.Data.SqlClient

Public Module SchemaDatabases
    ' Список баз через схему провайдера
    Public Function GetDatabaseTable(connectionString As String) As DataTable
        Using cn As New SqlConnection(connectionString)
            cn.Open()
            Return cn.GetSchema("Databases")
        End Using
    End Function
End Module

Заполняем ComboBox на форме

Типичный экран подключения: поле или комбобокс с сервером, кнопка «Обновить» и комбобокс со списком баз. Список стоит загружать не при каждом изменении текста в поле сервера, а по кнопке или при уходе фокуса: подключение к несуществующему серверу ждёт до истечения таймаута.

Ошибки подключения нужно перехватывать и показывать пользователю понятным текстом. SqlException с номером 18456 означает неверный логин, а ошибки с кодом 53 или -1 говорят, что сервер недоступен по сети.

ConnectForm.vbVB.NET
Private Sub btnRefresh_Click(sender As Object, e As EventArgs) _
        Handles btnRefresh.Click
    cboDatabase.DataSource = Nothing
    Cursor = Cursors.WaitCursor
    Try
        cboDatabase.DataSource = DatabaseList.GetDatabases(txtServer.Text.Trim())
    Catch ex As SqlClient.SqlException When ex.Number = 18456
        MessageBox.Show("Неверный логин или нет прав на сервер.")
    Catch ex As SqlClient.SqlException
        MessageBox.Show("Сервер недоступен: " & ex.Message)
    Finally
        Cursor = Cursors.Default
    End Try
End Sub

Как найти сами серверы

Иногда нужно показать и список серверов. Класс SqlDataSourceEnumerator из System.Data.Sql опрашивает сеть через службу SQL Browser и возвращает таблицу с именами серверов и экземпляров. Результат неполный по определению: серверы за файрволом, со скрытыми экземплярами или с отключённым Browser в него не попадут, а сам опрос занимает несколько секунд.

Поэтому список серверов лучше использовать как подсказку, а не как единственный способ ввода. Пользователь должен иметь возможность вписать имя сервера вручную, например «srv01\SQLEXPRESS» или «srv01,1433».

Не храните в коде логин sa и пароль для получения списка баз. Если нужна SQL-аутентификация, берите учётные данные из поля ввода и не сохраняйте пароль в открытом виде.

Как это делают сегодня

Сегодня вместо System.Data.SqlClient используют пакет Microsoft.Data.SqlClient: в нём шифрование соединения включено по умолчанию, поэтому для внутренних серверов с самоподписанным сертификатом добавляют TrustServerCertificate. Запросы выполняют асинхронно через OpenAsync и ExecuteReaderAsync, а для администрирования удобнее библиотека SMO (Microsoft.SqlServer.Management.Smo) с коллекцией Server.Databases.

Вопросы и ответы

Как получить список баз в SQL Server 2000?

Используйте запрос к master.dbo.sysdatabases или процедуру sp_databases. Представления sys.databases в версии 2000 ещё нет.

Почему в списке есть базы, к которым нельзя подключиться?

Представление sys.databases показывает все базы, если у логина есть право VIEW ANY DATABASE. Добавьте в запрос условие HAS_DBACCESS(name) = 1.

Почему SqlDataSourceEnumerator не видит мой сервер?

Перечисление работает через службу SQL Browser по UDP-порту 1434. Если служба остановлена или порт закрыт файрволом, сервер в список не попадёт, но подключиться по имени всё равно можно.

Читайте также