
Как получить список баз данных SQL Server из VB.NET
Экран подключения к базе почти всегда просит выбрать сервер и базу из списка. В VB.NET список баз SQL Server получают одним запросом, но выбор способа влияет на права, версии и скорость.
Где 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. Он экранирует значения и не даст сломать строку именем сервера с точкой с запятой.
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 Modulesp_databases и GetSchema
Если не хочется писать SQL, есть два готовых варианта. Системная процедура sp_databases возвращает имя базы, её размер в килобайтах и примечание. Она работает и в SQL Server 2000, и в новых версиях, но размер вычисляет по всем файлам каждой базы, поэтому на сервере с сотнями баз заметно медленнее простого SELECT.
Второй вариант: метод GetSchema у открытого SqlConnection с коллекцией «Databases». Он возвращает DataTable с колонками database_name, dbid и create_date, которую можно сразу отдать в DataSource комбобокса. Внутри метод выполняет почти тот же запрос, но без фильтрации по правам и состоянию.
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 говорят, что сервер недоступен по сети.
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».
Как это делают сегодня
Вопросы и ответы
Как получить список баз в 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. Если служба остановлена или порт закрыт файрволом, сервер в список не попадёт, но подключиться по имени всё равно можно.