[SQL, Microsoft SQL Server] Ведение разработки БД. Шаблоны создания/изменения объектов MSSQL
Автор
Сообщение
news_bot ®
Стаж: 6 лет 9 месяцев
Сообщений: 27286
При постоянной работе с MSSQL необходимо создавать различные объекты БД: таблицы, представления, триггеры и т.д.
В статье приведу шаблоны SQL запросов, которые помогут, и, возможно, стандартизируют подход создания кода на языке T-SQL.
Кроме этого, опишу о том, как я веду репозиторий БД в системе контроля версий.
Основные требования реализации SQL скриптов
1) скрипт должен выполняться многократно не выдавая ошибок
2) в скрипте должны быть предусмотрены операторы PRINT для удобства отладки
3) выполнение скриптов должно логироваться в один общий файл
4) скрипты должны выполняться через командную строку используя стандартный набор утилит (sqlcmd, bcp)
5) создание и изменение каждого объекта БД хранится отдельным SQL файлом
6) SQL скрипты (файлы) запускаются BAT файлом при каждом обновлении БД
Далее приводятся примеры SQL файлов и BAT файл для запуска этих SQL запросов.
Шаблоны T-SQL
Создание/изменение таблицы
SPL
--USE [DatabaseName]
--GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--проверяем на существование таблицы в БД
if objectproperty (object_id ('dbo.TableName'), N'IsUserTable') IS NULL
begin
print N'Создание таблицы - dbo.TableName'
create table dbo.TableName
(
TableNameId uniqueidentifier default newid() not null,
FieldName1 uniqueidentifier not null,
FieldName2 varchar(20) not null,
CONSTRAINT PK_TableName PRIMARY KEY (TableNameId),
CONSTRAINT FK_TableName_FieldName1 FOREIGN KEY (FieldName1)
REFERENCES dbo.ReferenceTableName (RefFieldName)
ON UPDATE CASCADE,
CONSTRAINT UQ_TableName_FieldName1_FieldName2 UNIQUE (FieldName1, FieldName2)
);
end
GO
-- Добавить поле
if not exists (
select *
from INFORMATION_SCHEMA.COLUMNS
where TABLE_SCHEMA = 'dbo'
and TABLE_NAME = 'TableName'
and COLUMN_NAME = 'FieldName'
)
begin
alter table dbo.TableName add FieldName varchar(500)
print N'Добавлено поле FieldName в таблице dbo.TableName'
end
-- Создать FOREIGN KEY, если его не существует
if not exists (select * from sys.foreign_keys where object_id = OBJECT_ID(N'dbo.FK_TableName_FieldName1') AND parent_object_id = OBJECT_ID(N'dbo.TableName'))
ALTER TABLE dbo.TableName WITH CHECK ADD CONSTRAINT FK_TableName_FieldName1 FOREIGN KEY(FieldName1)
REFERENCES dbo.ReferenceTableName (RefFieldName)
GO
IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'dbo.FK_TableName_FieldName1') AND parent_object_id = OBJECT_ID(N'dbo.TableName'))
ALTER TABLE dbo.TableName CHECK CONSTRAINT FK_TableName_FieldName1
GO
-- Создание уникального индекса, если он не существует
if not exists (select * from information_schema.key_column_usage where CONSTRAINT_NAME='UQ_TableName_FieldName1_FieldName2')
begin
ALTER TABLE dbo.TableName ADD CONSTRAINT UQ_TableName_FieldName1_FieldName2 UNIQUE (FieldName1, FieldName2)
end
-- Создание DEFAULT ограничения, если он не существует
if not exists (select * from sysconstraints where id = OBJECT_ID('dbo.TableName') AND COL_NAME(id,colid) = 'FieldName2' AND OBJECTPROPERTY(constid, 'IsDefaultCnst') = 1)
begin
ALTER TABLE dbo.TableName ADD CONSTRAINT DF_TableName_FieldName2 DEFAULT ('DefaultValue') FOR FieldName2
end
declare @SchemaName varchar(128) = 'dbo'
declare @TableName varchar(128) = 'TableName'
-- Создание описания таблицы
IF NOT EXISTS (SELECT * FROM fn_listextendedproperty('MS_Description', 'SCHEMA', @SchemaName, 'TABLE', @TableName, default, default))
EXECUTE sp_addextendedproperty
N'MS_Description', N'Описание таблицы',
N'SCHEMA', @SchemaName,
N'TABLE', @TableName
-- Создание описания поля, если его не существует
IF NOT EXISTS (SELECT * FROM fn_listextendedproperty ('MS_Description', 'schema', @SchemaName, 'table', @TableName, 'column', 'FieldName1'))
EXECUTE sp_addextendedproperty
N'MS_Description', N'Описание поля FieldName1',
N'SCHEMA', @SchemaName,
N'TABLE', @TableName,
N'COLUMN', N'FieldName1'
GO
Создание/изменение представления
SPL
--USE [DatabaseName]
--GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--проверяем на существование представления в БД
if objectproperty (object_id ('dbo.vw_ViewName'), N'IsView') is null
BEGIN
PRINT 'CREATE VIEW - '+db_name()+'.dbo.vw_ViewName'
EXECUTE('CREATE VIEW dbo.vw_ViewName AS SELECT 1/0 as ColumnName');
END
GO
PRINT 'ALTER VIEW - '+db_name()+'.dbo.vw_ViewName'
GO
alter view dbo.vw_ViewName
as
select
FieldName1, FieldName2
from dbo.TableName
Создание/изменение процедуры
SPL
--USE [DatabaseName]
--GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
if objectproperty (object_id ('dbo.usp_StoredProcedureName'), N'IsProcedure') is null
BEGIN
-- If procedure exists, we exclude script execution.
PRINT 'CREATE PROCEDURE - '+db_name()+'.dbo.usp_StoredProcedureName'
EXECUTE('CREATE PROCEDURE dbo.usp_StoredProcedureName as select 1/0');
END
ELSE
PRINT 'ALTER PROCEDURE - '+db_name()+'.dbo.usp_StoredProcedureName'
GO
alter procedure dbo.usp_StoredProcedureName
as select null
Создание/изменение триггера
SPL
--USE [DatabaseName]
--GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--проверяем на существование триггера в БД
if objectproperty (object_id ('dbo.TriggerName'), N'IsTrigger') is null
BEGIN
PRINT 'CREATE TRIGGER - '+db_name()+'.dbo.TriggerName'
EXECUTE('CREATE TRIGGER dbo.TriggerName on dbo.TableName for insert as print 0');
END
GO
PRINT 'ALTER TRIGGER - '+db_name()+'.dbo.TriggerName'
GO
alter trigger dbo.TriggerName on dbo.TableName
for insert, update, delete
as
if (exists(SELECT 1 from inserted) and exists (SELECT 1 from deleted)) -- определение UPDATE
begin
if update(FieldName)
begin
update dbo.TableName
set FieldName = FieldValue
where SearchFieldName in
(
select FieldValue from inserted
union
select FieldValue from deleted
)
end
end
else
if (exists(SELECT 1 from inserted) and not exists (SELECT 1 from deleted)) -- определение INSERT
begin
/* Ваш код обработки */
end
else
if (not exists(SELECT 1 from inserted) and exists (SELECT 1 from deleted)) -- определение DELETE
begin
/* Ваш код обработки */
end;
Создание/изменение табличного типа
SPL
--USE [DatabaseName]
--GO
/*
DROP PROCEDURE dbo.ProcedureName
DROP TYPE dbo.CustomUserType
*/
IF TYPE_ID(N'dbo.CustomUserType') IS NULL
BEGIN
PRINT N'Создание типа - dbo.CustomUserType'
--Если тип не существует, создаем его.
CREATE TYPE dbo.CustomUserType AS TABLE
(
FieldId TYPE,
PRIMARY KEY (FieldId)
)
END
GO
Создание/изменение скалярной функции
SPL
--USE [DatabaseName]
--GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
if objectproperty (object_id ('dbo.ufn_FunctionName'), N'IsScalarFunction') is null
BEGIN
-- If procedure exists, we exclude script execution.
PRINT 'CREATE FUNCTION - '+db_name()+'.dbo.ufn_FunctionName'
EXECUTE('CREATE FUNCTION dbo.ufn_FunctionName() returns int begin return 0 end');
END
ELSE
PRINT 'ALTER FUNCTION - '+db_name()+'.dbo.ufn_FunctionName'
GO
alter function dbo.ufn_FunctionName ()
returns varchar(100)
begin
return null;
end
GO
Создание/изменение табличной функции
SPL
--USE [DatabaseName]
--GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
if objectproperty (object_id ('dbo.ufn_TableFunctionName'), N'IsTableFunction') is null
BEGIN
-- If procedure exists, we exclude script execution.
PRINT 'CREATE FUNCTION - '+db_name()+'.dbo.ufn_TableFunctionName'
EXECUTE('CREATE FUNCTION dbo.ufn_TableFunctionName() returns table as return (select null as c)');
END
ELSE
PRINT 'ALTER FUNCTION - '+db_name()+'.dbo.ufn_TableFunctionName'
GO
ALTER function dbo.ufn_TableFunctionName()
returns table
as return
(
select 1 as Field1, 2 as Field2
)
Удаление/создание XSD-схем (XmlSchemaCollection)
SPL
--USE [DatabaseName]
--GO
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
if exists (select * from sys.xml_schema_collections where [schema_id] = schema_id('dbo') and [name] = 'SdmxXsd')
begin
drop XML SCHEMA COLLECTION dbo.SdmxXsd
print 'Удалена XSD схема - dbo.SdmxXsd'
end
--Создать XSD схему, если она отсутствует в БД
if not exists (select * from sys.xml_schema_collections where [schema_id] = schema_id('dbo') and [name] = 'SdmxXsd')
begin
CREATE XML SCHEMA COLLECTION dbo.SdmxXsd as
N'<?xml version="1.0" encoding="utf-16"?>
<xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Ref">
<xs:complexType>
<xs:attribute name="agencyID" type="xs:string" use="required" />
<xs:attribute name="id" type="xs:unsignedInt" use="required" />
<xs:attribute name="version" type="xs:decimal" use="required" />
</xs:complexType>
</xs:element>
</xs:schema>'
ALTER XML SCHEMA COLLECTION dbo.SdmxXsd add
N'<?xml version="1.0" encoding="utf-16"?>
<xs:schema xmlns:tns="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/common" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/common" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import />
<xs:element name="Structure">
<xs:complexType>
<xs:sequence>
<xs:element ref="Ref" />
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>'
ALTER XML SCHEMA COLLECTION dbo.SdmxXsd add
N'<?xml version="1.0" encoding="utf-16"?>
<xs:schema xmlns:tns="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:element name="Series">
<xs:complexType>
<xs:sequence>
<xs:element name="SeriesKey">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="Value">
<xs:complexType>
<xs:attribute name="id" type="xs:string" use="required" />
<xs:attribute name="value" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Attributes">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" name="Value">
<xs:complexType>
<xs:attribute name="id" type="xs:string" use="required" />
<xs:attribute name="value" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="Obs">
<xs:complexType>
<xs:sequence>
<xs:element name="ObsDimension">
<xs:complexType>
<xs:attribute name="value" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="ObsValue">
<xs:complexType>
<xs:attribute name="value" type="xs:decimal" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>'
ALTER XML SCHEMA COLLECTION dbo.SdmxXsd add
N'<?xml version="1.0" encoding="utf-16"?>
<xs:schema xmlns:message="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message" xmlns:common="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/common" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:generic="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message" xmlns:xs="http://www.w3.org/2001/XMLSchema">
<xs:import namespace="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/common" />
<xs:import namespace="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic" />
<xs:element name="GenericData">
<xs:complexType>
<xs:sequence>
<xs:element name="Header">
<xs:complexType>
<xs:sequence>
<xs:element name="ID" type="xs:string" />
<xs:element name="Test" type="xs:boolean" />
<xs:element name="Truncated" type="xs:boolean" />
<xs:element name="Prepared" type="xs:dateTime" />
<xs:element name="Sender">
<xs:complexType>
<xs:attribute name="id" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
<xs:element name="Structure">
<xs:complexType>
<xs:sequence>
<xs:element ref="common:Structure" />
</xs:sequence>
<xs:attribute name="structureID" type="xs:string" use="required" />
<xs:attribute name="dimensionAtObservation" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
<xs:element name="DataSet">
<xs:complexType>
<xs:sequence>
<xs:element maxOccurs="unbounded" ref="generic:Series" />
</xs:sequence>
<xs:attribute name="structureRef" type="xs:string" use="required" />
</xs:complexType>
</xs:element>
</xs:sequence>
</xs:complexType>
</xs:element>
</xs:schema>'
print 'Создана XSD схема - dbo.SdmxXsd'
end
go
Ведение репозитория
1) Репозиторий состоит из папок, разделенных по типу объектов (например: Tables, Views, Triggers и т.д.)
2) Каждый объект БД хранится в отдельном SQL файле для удобства просмотра истории изменений в системе контроля версий.
3) Если изменения касаются данных, то такие изменения ведутся в отдельном файле "CommonChanges (version 000).sql", который создается на каждое обновление БД
4) Для автоматизации применения изменений на нескольких серверах запросы выполняются с помощью BAT файла.
Пример BAT файла
SPL
:: Описание: Обновление БД Microsoft SQL Server
::
:: ВНИМАНИЕ: В случае если в разделе "1. Описание параметров" содержатся русские символы, то необходимо раскомментировать следующую строку
::chcp 1251
::
:: 1. ОПИСАНИЕ ПАРАМЕТРОВ
::
:: Получить общие настройки (соединение с БД и др.)
call Settings.bat
::
:: Лог-файл выполнения запроса (перезаписывается с каждым вызовом sqlcmd)
set LogFileName="%LogDir%\update_0001_%NowDateTime%_tmp.log"
::
:: Полный лог-файл обновления БД
set FullLogFileName="%LogDir%\update_0001_%NowDateTime%.log"
::
:: Лог файла командного выполнения
set LogCmdFileName="%LogDir%\update_0001_%NowDateTime%_cmd.log"
::
:: 2. ВЫПОЛНЕНИЕ SQL-ЗАПРОСОВ
::
set SqlFileName="..\CommonChanges (version 001).sql"
@If Exist %SqlFileName% (
%sqlcmd% -S %ServerName% -U %UserName% -P %Password% -d %DatabaseName% -i %SqlFileName% -o %LogFileName% -b
type %LogFileName% >> %FullLogFileName%
) ElSE (
echo Не найден файл скрипта %SqlFileName% >> %LogCmdFileName%
)
::
set SqlFileName="..\AlterObjects\StoredProcedures\dbo.SP.sql"
@If Exist %SqlFileName% (
%sqlcmd% -S %ServerName% -U %UserName% -P %Password% -d %DatabaseName% -i %SqlFileName% -o %LogFileName% -b
type %LogFileName% >> %FullLogFileName%
) ElSE (
echo Не найден файл скрипта %SqlFileName% >> %LogCmdFileName%
)
::
set SqlFileName="..\UpdateVersion.sql"
@If Exist %SqlFileName% (
%sqlcmd% -S %ServerName% -U %UserName% -P %Password% -d %DatabaseName% -i %SqlFileName% -o %LogFileName% -b
type %LogFileName% >> %FullLogFileName%
) ElSE (
echo Не найден файл скрипта %SqlFileName% >> %LogCmdFileName%
)
::
:: Вывести содержимое лога файла командного выполнения в общий лог, если он был сформирован
@If Exist %LogCmdFileName% (
echo ----Внимание, не найдены следующие файлы ожидающие выполнения:---- >> %FullLogFileName%
type %LogCmdFileName% >> %FullLogFileName%
:: Удалить лог файла командного выполнения
del %LogCmdFileName%
)
:: Удалить сокращенный лог (оставшийся при последнем вызове sqlcmd)
del %LogFileName%
::
:: Вывести содержимое файла-лога с результатом в командную строку
type %FullLogFileName%
pause
Файл Settings.bat
SPL
:: Настройки соединения с БД
::
:: ВНИМАНИЕ: В случае если в разделе "1. Описание параметров" содержатся русские символы, то необходимо раскомментировать следующую строку
::chcp 1251
::
:: 1. ОПИСАНИЕ ПАРАМЕТРОВ
::
:: Путь к файлу sqlcmd.exe на локальном диске
set sqlcmd="sqlcmd.exe"
::
:: Путь к файлу bcp.exe на локальном диске
set bcp="bcp.exe"
::
:: Имя SQL-сервера (именованный экземпляр)
set ServerName="(local)\InstanceName"
::
:: Имя пользователя
set UserName="sa"
::
:: Пароль
set Password="my_password"
::
:: Имя БД
set DatabaseName=my_db
::
:: Каталог логов
set LogDir=.\log
::
:: Использовать на русскоязычной версии Windows. Текущее время в формате YYYYMMDD_HHMMSS (рекомендуется использовать в имени файла лога)
set NowDateTime=%date:~6%%date:~3,2%%date:~0,2%_%TIME:~0,2%%TIME:~3,2%%TIME:~6,2%
:: Использовать на английской версии Windows. Текущее время в формате YYYYMMDD_HHMMSS (рекомендуется использовать в имени файла лога)
::set NowDateTime=%date:~10,4%%date:~4,2%%date:~7,2%_%TIME:~0,2%%TIME:~3,2%%TIME:~6,2%
::
:: 2. СОЗДАНИЕ КАТАЛОГА ДЛЯ ХРАНЕНИЯ ЛОГОВ
md %LogDir%
Достоинства
1) Удобно отслеживать историю изменения каждого объекта БД
2) Не требуется разрабатывать собственные приложения для выполнения SQL файлов
3) Работает на всех версиях MSSQL и Windows
4) Настройки соединения с БД, имена БД и др. переменные хранятся в одном файле "Settings.bat", которые легко изменить
Недостатки
1) Не предусмотрен общий откат изменений, если какой-то скрипт выполнится с ошибкой
2) Если репозиторий состоит из большого количества объектов БД, то чтобы не передавать заказчику весь репозиторий, нужно копировать файлы (входящие в обновление) в отдельный каталог
Выгрузка структуры БД
В качестве автоматизации выгрузки структуры БД, в формате один объект = один файл, на данный момент использую SSMS, но в скором времени подобный функционал будет в моей программе ImportExportDataSql, которую рекомендую всем разработчикам БД.
Немного о ImportExportDataSql
Приложение ImportExportDataSql бесплатное, без рекламы, оповещает о новых версиях, наличие командной строки и Вы можете скачать его и использовать в своих проектах.
С помощью ImportExportDataSql Вы сможете:
- быстро загружать CSV файлы большого объема (более 1Гб) в SQL Server
- загружать Excel файлы и CSV с возможностью настройки полей, а также с ограничением количества обрабатываемых строк (удобно при отладке)
- выгружать выборочные данные из БД, в SQL формате и затем выполнять этот скрипт на другой БД (т.е. использовать как средство синхронизации данных)
- копировать джобы с одной машины на другую
- выгружать структуру БД.
Главной особенностью ImportExportDataSql, является то, что можно объединять несколько SELECT запросов, выгружая результат в виде SQL в один файл.
Добавляйтесь в группу VK, пишите свои пожелания, буду рад доработать приложение под Ваши нужды.
Заключение
Рад был поделиться результатом своего опыта. Данный способ ведения репозитория меня вполне устраивает. Надеюсь, эта статья поможет нам навести порядок в базах данных.
===========
Источник:
habr.com
===========
Похожие новости:
- [Семантика, Программирование, Prolog, Бизнес-модели] Проектируем мульти-парадигменный язык программирования. Часть 3 — Обзор языков представления знаний
- [MySQL, Администрирование баз данных] Изучаем использование памяти MySQL с помощью Performance Schema (перевод)
- [SQL, Администрирование баз данных, Big Data, Data Engineering] Кто ответит за качество аналитики: QA для Хранилища Данных
- [PHP, MySQL, CSS, JavaScript, HTML] RevolveR Contents Management Framework v.1.9.4.9
- [PostgreSQL, SQL] Перечислимый тип и PostgreSQL
- [Open source, *nix, Виртуализация, Openshift] Создаем настраиваемые отчеты для оператора Metering
- [Microsoft SQL Server, ERP-системы] История одной миграции с SQL Server 2012 на SQL Server 2016+ в системе Microsoft Dynamics AX 2012
- [PostgreSQL] Этюд по PITR в PostgreSQL штатными средствами
- [Open source, PostgreSQL, Администрирование баз данных] Знакомство с pg_probackup. Третья часть
- [.NET, C#] Дерево синтаксиса и альтернатива LINQ при взаимодействии с базами данных SQL (перевод)
Теги для поиска: #_sql, #_microsoft_sql_server, #_sql_server, #_ms_sql_server, #_mssql, #_ms_sql, #_tsql, #_sql, #_sql, #_microsoft_sql_server
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:07
Часовой пояс: UTC + 5
Автор | Сообщение |
---|---|
news_bot ®
Стаж: 6 лет 9 месяцев |
|
При постоянной работе с MSSQL необходимо создавать различные объекты БД: таблицы, представления, триггеры и т.д. В статье приведу шаблоны SQL запросов, которые помогут, и, возможно, стандартизируют подход создания кода на языке T-SQL. Кроме этого, опишу о том, как я веду репозиторий БД в системе контроля версий. Основные требования реализации SQL скриптов 1) скрипт должен выполняться многократно не выдавая ошибок 2) в скрипте должны быть предусмотрены операторы PRINT для удобства отладки 3) выполнение скриптов должно логироваться в один общий файл 4) скрипты должны выполняться через командную строку используя стандартный набор утилит (sqlcmd, bcp) 5) создание и изменение каждого объекта БД хранится отдельным SQL файлом 6) SQL скрипты (файлы) запускаются BAT файлом при каждом обновлении БД Далее приводятся примеры SQL файлов и BAT файл для запуска этих SQL запросов. Шаблоны T-SQL Создание/изменение таблицыSPL--USE [DatabaseName]
--GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO --проверяем на существование таблицы в БД if objectproperty (object_id ('dbo.TableName'), N'IsUserTable') IS NULL begin print N'Создание таблицы - dbo.TableName' create table dbo.TableName ( TableNameId uniqueidentifier default newid() not null, FieldName1 uniqueidentifier not null, FieldName2 varchar(20) not null, CONSTRAINT PK_TableName PRIMARY KEY (TableNameId), CONSTRAINT FK_TableName_FieldName1 FOREIGN KEY (FieldName1) REFERENCES dbo.ReferenceTableName (RefFieldName) ON UPDATE CASCADE, CONSTRAINT UQ_TableName_FieldName1_FieldName2 UNIQUE (FieldName1, FieldName2) ); end GO -- Добавить поле if not exists ( select * from INFORMATION_SCHEMA.COLUMNS where TABLE_SCHEMA = 'dbo' and TABLE_NAME = 'TableName' and COLUMN_NAME = 'FieldName' ) begin alter table dbo.TableName add FieldName varchar(500) print N'Добавлено поле FieldName в таблице dbo.TableName' end -- Создать FOREIGN KEY, если его не существует if not exists (select * from sys.foreign_keys where object_id = OBJECT_ID(N'dbo.FK_TableName_FieldName1') AND parent_object_id = OBJECT_ID(N'dbo.TableName')) ALTER TABLE dbo.TableName WITH CHECK ADD CONSTRAINT FK_TableName_FieldName1 FOREIGN KEY(FieldName1) REFERENCES dbo.ReferenceTableName (RefFieldName) GO IF EXISTS (SELECT * FROM sys.foreign_keys WHERE object_id = OBJECT_ID(N'dbo.FK_TableName_FieldName1') AND parent_object_id = OBJECT_ID(N'dbo.TableName')) ALTER TABLE dbo.TableName CHECK CONSTRAINT FK_TableName_FieldName1 GO -- Создание уникального индекса, если он не существует if not exists (select * from information_schema.key_column_usage where CONSTRAINT_NAME='UQ_TableName_FieldName1_FieldName2') begin ALTER TABLE dbo.TableName ADD CONSTRAINT UQ_TableName_FieldName1_FieldName2 UNIQUE (FieldName1, FieldName2) end -- Создание DEFAULT ограничения, если он не существует if not exists (select * from sysconstraints where id = OBJECT_ID('dbo.TableName') AND COL_NAME(id,colid) = 'FieldName2' AND OBJECTPROPERTY(constid, 'IsDefaultCnst') = 1) begin ALTER TABLE dbo.TableName ADD CONSTRAINT DF_TableName_FieldName2 DEFAULT ('DefaultValue') FOR FieldName2 end declare @SchemaName varchar(128) = 'dbo' declare @TableName varchar(128) = 'TableName' -- Создание описания таблицы IF NOT EXISTS (SELECT * FROM fn_listextendedproperty('MS_Description', 'SCHEMA', @SchemaName, 'TABLE', @TableName, default, default)) EXECUTE sp_addextendedproperty N'MS_Description', N'Описание таблицы', N'SCHEMA', @SchemaName, N'TABLE', @TableName -- Создание описания поля, если его не существует IF NOT EXISTS (SELECT * FROM fn_listextendedproperty ('MS_Description', 'schema', @SchemaName, 'table', @TableName, 'column', 'FieldName1')) EXECUTE sp_addextendedproperty N'MS_Description', N'Описание поля FieldName1', N'SCHEMA', @SchemaName, N'TABLE', @TableName, N'COLUMN', N'FieldName1' GO Создание/изменение представленияSPL--USE [DatabaseName]
--GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO --проверяем на существование представления в БД if objectproperty (object_id ('dbo.vw_ViewName'), N'IsView') is null BEGIN PRINT 'CREATE VIEW - '+db_name()+'.dbo.vw_ViewName' EXECUTE('CREATE VIEW dbo.vw_ViewName AS SELECT 1/0 as ColumnName'); END GO PRINT 'ALTER VIEW - '+db_name()+'.dbo.vw_ViewName' GO alter view dbo.vw_ViewName as select FieldName1, FieldName2 from dbo.TableName Создание/изменение процедурыSPL--USE [DatabaseName]
--GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO if objectproperty (object_id ('dbo.usp_StoredProcedureName'), N'IsProcedure') is null BEGIN -- If procedure exists, we exclude script execution. PRINT 'CREATE PROCEDURE - '+db_name()+'.dbo.usp_StoredProcedureName' EXECUTE('CREATE PROCEDURE dbo.usp_StoredProcedureName as select 1/0'); END ELSE PRINT 'ALTER PROCEDURE - '+db_name()+'.dbo.usp_StoredProcedureName' GO alter procedure dbo.usp_StoredProcedureName as select null Создание/изменение триггераSPL--USE [DatabaseName]
--GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO --проверяем на существование триггера в БД if objectproperty (object_id ('dbo.TriggerName'), N'IsTrigger') is null BEGIN PRINT 'CREATE TRIGGER - '+db_name()+'.dbo.TriggerName' EXECUTE('CREATE TRIGGER dbo.TriggerName on dbo.TableName for insert as print 0'); END GO PRINT 'ALTER TRIGGER - '+db_name()+'.dbo.TriggerName' GO alter trigger dbo.TriggerName on dbo.TableName for insert, update, delete as if (exists(SELECT 1 from inserted) and exists (SELECT 1 from deleted)) -- определение UPDATE begin if update(FieldName) begin update dbo.TableName set FieldName = FieldValue where SearchFieldName in ( select FieldValue from inserted union select FieldValue from deleted ) end end else if (exists(SELECT 1 from inserted) and not exists (SELECT 1 from deleted)) -- определение INSERT begin /* Ваш код обработки */ end else if (not exists(SELECT 1 from inserted) and exists (SELECT 1 from deleted)) -- определение DELETE begin /* Ваш код обработки */ end; Создание/изменение табличного типаSPL--USE [DatabaseName]
--GO /* DROP PROCEDURE dbo.ProcedureName DROP TYPE dbo.CustomUserType */ IF TYPE_ID(N'dbo.CustomUserType') IS NULL BEGIN PRINT N'Создание типа - dbo.CustomUserType' --Если тип не существует, создаем его. CREATE TYPE dbo.CustomUserType AS TABLE ( FieldId TYPE, PRIMARY KEY (FieldId) ) END GO Создание/изменение скалярной функцииSPL--USE [DatabaseName]
--GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO if objectproperty (object_id ('dbo.ufn_FunctionName'), N'IsScalarFunction') is null BEGIN -- If procedure exists, we exclude script execution. PRINT 'CREATE FUNCTION - '+db_name()+'.dbo.ufn_FunctionName' EXECUTE('CREATE FUNCTION dbo.ufn_FunctionName() returns int begin return 0 end'); END ELSE PRINT 'ALTER FUNCTION - '+db_name()+'.dbo.ufn_FunctionName' GO alter function dbo.ufn_FunctionName () returns varchar(100) begin return null; end GO Создание/изменение табличной функцииSPL--USE [DatabaseName]
--GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO if objectproperty (object_id ('dbo.ufn_TableFunctionName'), N'IsTableFunction') is null BEGIN -- If procedure exists, we exclude script execution. PRINT 'CREATE FUNCTION - '+db_name()+'.dbo.ufn_TableFunctionName' EXECUTE('CREATE FUNCTION dbo.ufn_TableFunctionName() returns table as return (select null as c)'); END ELSE PRINT 'ALTER FUNCTION - '+db_name()+'.dbo.ufn_TableFunctionName' GO ALTER function dbo.ufn_TableFunctionName() returns table as return ( select 1 as Field1, 2 as Field2 ) Удаление/создание XSD-схем (XmlSchemaCollection)SPL--USE [DatabaseName]
--GO SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO if exists (select * from sys.xml_schema_collections where [schema_id] = schema_id('dbo') and [name] = 'SdmxXsd') begin drop XML SCHEMA COLLECTION dbo.SdmxXsd print 'Удалена XSD схема - dbo.SdmxXsd' end --Создать XSD схему, если она отсутствует в БД if not exists (select * from sys.xml_schema_collections where [schema_id] = schema_id('dbo') and [name] = 'SdmxXsd') begin CREATE XML SCHEMA COLLECTION dbo.SdmxXsd as N'<?xml version="1.0" encoding="utf-16"?> <xs:schema attributeFormDefault="unqualified" elementFormDefault="qualified" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Ref"> <xs:complexType> <xs:attribute name="agencyID" type="xs:string" use="required" /> <xs:attribute name="id" type="xs:unsignedInt" use="required" /> <xs:attribute name="version" type="xs:decimal" use="required" /> </xs:complexType> </xs:element> </xs:schema>' ALTER XML SCHEMA COLLECTION dbo.SdmxXsd add N'<?xml version="1.0" encoding="utf-16"?> <xs:schema xmlns:tns="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/common" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/common" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:import /> <xs:element name="Structure"> <xs:complexType> <xs:sequence> <xs:element ref="Ref" /> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>' ALTER XML SCHEMA COLLECTION dbo.SdmxXsd add N'<?xml version="1.0" encoding="utf-16"?> <xs:schema xmlns:tns="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:element name="Series"> <xs:complexType> <xs:sequence> <xs:element name="SeriesKey"> <xs:complexType> <xs:sequence> <xs:element maxOccurs="unbounded" name="Value"> <xs:complexType> <xs:attribute name="id" type="xs:string" use="required" /> <xs:attribute name="value" type="xs:string" use="required" /> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="Attributes"> <xs:complexType> <xs:sequence> <xs:element maxOccurs="unbounded" name="Value"> <xs:complexType> <xs:attribute name="id" type="xs:string" use="required" /> <xs:attribute name="value" type="xs:string" use="required" /> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="Obs"> <xs:complexType> <xs:sequence> <xs:element name="ObsDimension"> <xs:complexType> <xs:attribute name="value" type="xs:string" use="required" /> </xs:complexType> </xs:element> <xs:element name="ObsValue"> <xs:complexType> <xs:attribute name="value" type="xs:decimal" use="required" /> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>' ALTER XML SCHEMA COLLECTION dbo.SdmxXsd add N'<?xml version="1.0" encoding="utf-16"?> <xs:schema xmlns:message="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message" xmlns:common="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/common" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:generic="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic" attributeFormDefault="unqualified" elementFormDefault="qualified" targetNamespace="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/message" xmlns:xs="http://www.w3.org/2001/XMLSchema"> <xs:import namespace="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/common" /> <xs:import namespace="http://www.sdmx.org/resources/sdmxml/schemas/v2_1/data/generic" /> <xs:element name="GenericData"> <xs:complexType> <xs:sequence> <xs:element name="Header"> <xs:complexType> <xs:sequence> <xs:element name="ID" type="xs:string" /> <xs:element name="Test" type="xs:boolean" /> <xs:element name="Truncated" type="xs:boolean" /> <xs:element name="Prepared" type="xs:dateTime" /> <xs:element name="Sender"> <xs:complexType> <xs:attribute name="id" type="xs:string" use="required" /> </xs:complexType> </xs:element> <xs:element name="Structure"> <xs:complexType> <xs:sequence> <xs:element ref="common:Structure" /> </xs:sequence> <xs:attribute name="structureID" type="xs:string" use="required" /> <xs:attribute name="dimensionAtObservation" type="xs:string" use="required" /> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> <xs:element name="DataSet"> <xs:complexType> <xs:sequence> <xs:element maxOccurs="unbounded" ref="generic:Series" /> </xs:sequence> <xs:attribute name="structureRef" type="xs:string" use="required" /> </xs:complexType> </xs:element> </xs:sequence> </xs:complexType> </xs:element> </xs:schema>' print 'Создана XSD схема - dbo.SdmxXsd' end go Ведение репозитория 1) Репозиторий состоит из папок, разделенных по типу объектов (например: Tables, Views, Triggers и т.д.) 2) Каждый объект БД хранится в отдельном SQL файле для удобства просмотра истории изменений в системе контроля версий. 3) Если изменения касаются данных, то такие изменения ведутся в отдельном файле "CommonChanges (version 000).sql", который создается на каждое обновление БД 4) Для автоматизации применения изменений на нескольких серверах запросы выполняются с помощью BAT файла. Пример BAT файлаSPL:: Описание: Обновление БД Microsoft SQL Server
:: :: ВНИМАНИЕ: В случае если в разделе "1. Описание параметров" содержатся русские символы, то необходимо раскомментировать следующую строку ::chcp 1251 :: :: 1. ОПИСАНИЕ ПАРАМЕТРОВ :: :: Получить общие настройки (соединение с БД и др.) call Settings.bat :: :: Лог-файл выполнения запроса (перезаписывается с каждым вызовом sqlcmd) set LogFileName="%LogDir%\update_0001_%NowDateTime%_tmp.log" :: :: Полный лог-файл обновления БД set FullLogFileName="%LogDir%\update_0001_%NowDateTime%.log" :: :: Лог файла командного выполнения set LogCmdFileName="%LogDir%\update_0001_%NowDateTime%_cmd.log" :: :: 2. ВЫПОЛНЕНИЕ SQL-ЗАПРОСОВ :: set SqlFileName="..\CommonChanges (version 001).sql" @If Exist %SqlFileName% ( %sqlcmd% -S %ServerName% -U %UserName% -P %Password% -d %DatabaseName% -i %SqlFileName% -o %LogFileName% -b type %LogFileName% >> %FullLogFileName% ) ElSE ( echo Не найден файл скрипта %SqlFileName% >> %LogCmdFileName% ) :: set SqlFileName="..\AlterObjects\StoredProcedures\dbo.SP.sql" @If Exist %SqlFileName% ( %sqlcmd% -S %ServerName% -U %UserName% -P %Password% -d %DatabaseName% -i %SqlFileName% -o %LogFileName% -b type %LogFileName% >> %FullLogFileName% ) ElSE ( echo Не найден файл скрипта %SqlFileName% >> %LogCmdFileName% ) :: set SqlFileName="..\UpdateVersion.sql" @If Exist %SqlFileName% ( %sqlcmd% -S %ServerName% -U %UserName% -P %Password% -d %DatabaseName% -i %SqlFileName% -o %LogFileName% -b type %LogFileName% >> %FullLogFileName% ) ElSE ( echo Не найден файл скрипта %SqlFileName% >> %LogCmdFileName% ) :: :: Вывести содержимое лога файла командного выполнения в общий лог, если он был сформирован @If Exist %LogCmdFileName% ( echo ----Внимание, не найдены следующие файлы ожидающие выполнения:---- >> %FullLogFileName% type %LogCmdFileName% >> %FullLogFileName% :: Удалить лог файла командного выполнения del %LogCmdFileName% ) :: Удалить сокращенный лог (оставшийся при последнем вызове sqlcmd) del %LogFileName% :: :: Вывести содержимое файла-лога с результатом в командную строку type %FullLogFileName% pause Файл Settings.batSPL:: Настройки соединения с БД
:: :: ВНИМАНИЕ: В случае если в разделе "1. Описание параметров" содержатся русские символы, то необходимо раскомментировать следующую строку ::chcp 1251 :: :: 1. ОПИСАНИЕ ПАРАМЕТРОВ :: :: Путь к файлу sqlcmd.exe на локальном диске set sqlcmd="sqlcmd.exe" :: :: Путь к файлу bcp.exe на локальном диске set bcp="bcp.exe" :: :: Имя SQL-сервера (именованный экземпляр) set ServerName="(local)\InstanceName" :: :: Имя пользователя set UserName="sa" :: :: Пароль set Password="my_password" :: :: Имя БД set DatabaseName=my_db :: :: Каталог логов set LogDir=.\log :: :: Использовать на русскоязычной версии Windows. Текущее время в формате YYYYMMDD_HHMMSS (рекомендуется использовать в имени файла лога) set NowDateTime=%date:~6%%date:~3,2%%date:~0,2%_%TIME:~0,2%%TIME:~3,2%%TIME:~6,2% :: Использовать на английской версии Windows. Текущее время в формате YYYYMMDD_HHMMSS (рекомендуется использовать в имени файла лога) ::set NowDateTime=%date:~10,4%%date:~4,2%%date:~7,2%_%TIME:~0,2%%TIME:~3,2%%TIME:~6,2% :: :: 2. СОЗДАНИЕ КАТАЛОГА ДЛЯ ХРАНЕНИЯ ЛОГОВ md %LogDir% Достоинства 1) Удобно отслеживать историю изменения каждого объекта БД 2) Не требуется разрабатывать собственные приложения для выполнения SQL файлов 3) Работает на всех версиях MSSQL и Windows 4) Настройки соединения с БД, имена БД и др. переменные хранятся в одном файле "Settings.bat", которые легко изменить Недостатки 1) Не предусмотрен общий откат изменений, если какой-то скрипт выполнится с ошибкой 2) Если репозиторий состоит из большого количества объектов БД, то чтобы не передавать заказчику весь репозиторий, нужно копировать файлы (входящие в обновление) в отдельный каталог Выгрузка структуры БД В качестве автоматизации выгрузки структуры БД, в формате один объект = один файл, на данный момент использую SSMS, но в скором времени подобный функционал будет в моей программе ImportExportDataSql, которую рекомендую всем разработчикам БД. Немного о ImportExportDataSql Приложение ImportExportDataSql бесплатное, без рекламы, оповещает о новых версиях, наличие командной строки и Вы можете скачать его и использовать в своих проектах. С помощью ImportExportDataSql Вы сможете:
Главной особенностью ImportExportDataSql, является то, что можно объединять несколько SELECT запросов, выгружая результат в виде SQL в один файл. Добавляйтесь в группу VK, пишите свои пожелания, буду рад доработать приложение под Ваши нужды. Заключение Рад был поделиться результатом своего опыта. Данный способ ведения репозитория меня вполне устраивает. Надеюсь, эта статья поможет нам навести порядок в базах данных. =========== Источник: habr.com =========== Похожие новости:
|
|
Вы не можете начинать темы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Вы не можете отвечать на сообщения
Вы не можете редактировать свои сообщения
Вы не можете удалять свои сообщения
Вы не можете голосовать в опросах
Вы не можете прикреплять файлы к сообщениям
Вы не можете скачивать файлы
Текущее время: 22-Ноя 20:07
Часовой пояс: UTC + 5