Рефакторинг схем баз данных
Время прочтения
19 мин
Просмотры 19K
Я хочу рассказать о рефакторинге схем баз данных MS SQL Server.
Рефакторинг — изменение во внутренней структуре программного обеспечения, имеющее целью облегчить понимание его работы и упростить модификацию, не затрагивая наблюдаемого поведения.
— Martin Fowler
О рефакторинге кода говорят уже давно. На данный момент написано немало литературы, создано множество инструментов, помогающих выполнять рефакторинг кода.
А вот про рефакторинг схем баз данных не так уж и много информации. Я решил немного восполнить этот пробел и поделиться своим опытом.
Как понять что настала пора проводить рефакторинг?
Делая что-то в первый раз, вы просто это делаете. Делая что-то аналогичное во второй раз, вы морщитесь от необходимости повторения, но все-таки повторяете то же самое. Делая что-то похожее в третий раз, вы начинаете рефакторинг.
— Don Roberts
Мартин Фаулер ввел понятие «Код с душком», обозначив так код который нужно подвергнуть рефакторингу.
С душком называется код в котором:
- встречается дублирование
- есть большие методы
- существуют методы с большим количеством параметров
- встречается оператор switch
По аналогии с этим можно выделить общие недостатки схемы базы данных, которые указывают на необходимость применения рефакторинга. К этим недостаткам можно выделить следующие.
- Многоцелевые столбцы (или столбцы используемые не по назначению). Допустим у нас есть таблица содержащая информацию по заказам. В таблице есть необязательное для заполнения поле InvoiceId типа int. Представим что процесс продаж в компании построен таким образом, что это поле никогда не заполняется. Начиная с нового года менеджерам стало необходимо проставлять у заказов оценку клиента (от 1 до 10 по результатам обзвона). Такого поля в таблице нет и менеджеры начинают вбивать эти данные в поле InvoiceId (например потому, что IT-шники сказали им что на добавление нового поля уйдет целый месяц). Это приведет к проблемам когда поле InvoiceId станет использоваться по назначению.
- Многоцелевые таблицы. Примером может послужить таблица Customer в которой хранится информация о физических и юридических лицах. В подобном случае неизбежно появляются столбы с NULL значениями.
- Избыточные данные. Например, наличие поля Адрес клиента в таблице заказов может привести к случаю когда у нескольких заказов одного и того же клиента будут разные адреса.
- Таблицы с большим количеством столбцов. Наличие большого количества столбцов может означать что в таблице хранятся атрибуты более чем одной сущности. В таком случае вероятно нужно применить рефакторинг «Разбиение таблицы».
- Многозначные столбцы. Многозначными называются столбцы, в которых в различных позициях представлено несколько разных фрагментов информации. Например в таблице заказов есть поле OrderNumber содержащее данные вида XXX20150908000125. Где XXX — код товара, 20150908 — дата заказа, 000125 — порядковый номер заказа. На практике часто обнаруживается необходимость разбить поле на части, чтобы можно было проще обрабатывать эти поля в виде отдельных элементов.
Несколько полезных советов по применению рефакторинга
- Оцените масштаб бедствий.
Прежде чем что-то менять убедитесь что Вы не сломаете внешние приложения, использующие Вашу базу данных. Если Вам пришлось поддерживать базу данных, которая досталась «по наследству», вероятнее всего Вы не знаете кто (что) и как ее используют. Составьте список приложений, использующих Вашу базу. Попросите, по возможности, коллег разрабатывающих эти приложения выдать Вам список объектов, которые они используют. После чего согласуйте с ними Ваши изменения, договоритесь о совместном тестировании.
Особое внимание уделите таблицам базы на которые выданы права. Это потенциальный источник проблем.
Обсудите с коллегами чтобы они вместо таблиц перешли на использование представлений (процедур).
Когда все обращения к базе будут реализованы посредством процедур/представлений/функций, Вам будет намного легче проводить рефакторинг. - Не делайте много изменений за один раз.
Чем меньше будет изменение, тем проще будет найти ошибку в случае сбоя. - Проверяйте изменения тестами.
После каждого изменения запускайте тесты, чтобы убедиться что ничего не сломалось. - Используйте песочницы.
Не нужно заниматься рефакторингом на продуктиве, даже если изменение ничтожно мало. Используйте для рефакторинга тестовые площадки. После чего проводите полное регрессионное тестирование. И только после этого выполняйте изменение в продуктивной базе данных.
Практические примеры
Я покажу применение некоторых методов рефакторинга на примере базы данных Northwind (ссылка на скачивание).
В качестве инструмента я буду использовать SQL Server Management Studio (SSMS) с установленным плагином SQL Refactor Studio. Данный плагин добавляет в SSMS функции рефакторинга.
Исходная схема
Тестирование
После каждого изменения мы будем запускать тест, чтобы убедиться что все по прежнему работает.
Для примера я создал процедуру dbo.RunTests, которая выбирает данные из всех представлений в базе (разумеется это не обеспечивает нам полное покрытие тестами).
Если в процессе работы процедуры не было ошибок, процедура выдает OK, иначе Failed.
CREATE PROCEDURE dbo.RunTests
AS
DECLARE
@Script nvarchar(max) = '',
@Failed bit = 0
DECLARE crs CURSOR FOR
SELECT 'IF OBJECT_ID(''tempdb..#tmp'') IS NOT NULL DROP TABLE #tmp
SELECT * INTO #tmp FROM [' + object_schema_name(o.object_id) + '].[' + o.name + ']'
FROM sys.objects o
WHERE o.type = 'V'
OPEN crs
FETCH NEXT FROM crs INTO @Script
WHILE @@fetch_status = 0
BEGIN
BEGIN TRY
EXEC sp_executesql @Script
END TRY
BEGIN CATCH
SET @Failed = 1
SELECT 'Failed' AS Status, ERROR_MESSAGE() AS Details, @Script AS [Script]
END CATCH
FETCH NEXT FROM crs INTO @Script
END
CLOSE crs
DEALLOCATE crs
IF @Failed = 0
SELECT 'OK' AS [Status]
RETURN 0
/*
EXEC dbo.RunTests
*/
GO
Рефакторинг «Переименование объекта»
Не знаю конечно как Вы, но я при создании таблицы даю ей имя в единственном числе (dbo.Entry а не dbo.Entrie
s
).
Итак, давайте попробуем переименовать таблицу dbo.Customers в dbo.Customer. Тут есть один неприятный (и очень рутинный) процесс. Нужно переименовать таблицу так чтобы не сломался код использующий ее. Для этого его нужно найти и внести в него исправление. Воспользовавшись стандартным View Dependencies видим что таблица используется в одном преставлении и есть две таблицы ссылающиеся на dbo.Customers.
В принципе, внести исправление в одно представление после переименования таблицы — плевое дело.
Ну что же, в бой! Переименовываем таблицу и запускаем тест (должна сломаться вьюха dbo.Customer and Suppliers by City).
Однако, вместо ожидаемой одной строки тест мне выдал целых пять.
Тогда я решил проверить зависимости таблицы используя расширенный View Dependencies входящий в состав пакета SQL Refactor Studio. Он уже насчитал пять вьюшек (тест показал что сломалось именно пять представлений) и нашел одну процедуру (не покрыта тестами).
Шесть объектов — это уже посерьезнее. А представьте себе что Вам нужно поправить код в 50+ объектах. Вы все еще хотите переименовать таблицу? 
Воспользуемся функцией Rename входящий в пакет SQL Refactor Studio. Выбираем таблицу в Object Explorer (далее OE), из контекстного меню выбираем пункт SQL Refactor Studio -> Rename. Вводим новое имя (Customer) и нажимаем кнопку Genarate rename script. Тут также есть возможность посмотреть зависимости и снять галочки напротив объектов в которых не нужно производить переименование.
В результате открылась новая вкладка с сформированным скриптом.
Сгенерированный скрипт
use northwind
go
set transaction isolation level serializable
set xact_abort on
go
if object_id('tempdb..#err') is not null
drop table #err
go
create table #err(flag bit)
go
begin transaction
go
exec sp_rename 'dbo.Customers', 'Customer', 'OBJECT'
go
if (@@error <> 0) and (@@trancount > 0)
rollback transaction
go
if (@@trancount = 0)
begin
insert into #err(flag) select cast(1 as bit)
begin transaction
end
go
ALTER view dbo.[Orders Qry] AS
SELECT Orders.OrderID, Orders.CustomerID, Orders.EmployeeID, Orders.OrderDate, Orders.RequiredDate,
Orders.ShippedDate, Orders.ShipVia, Orders.Freight, Orders.ShipName, Orders.ShipAddress, Orders.ShipCity,
Orders.ShipRegion, Orders.ShipPostalCode, Orders.ShipCountry,
Customer.CompanyName, Customer.Address, Customer.City, Customer.Region, Customer.PostalCode, Customer.Country
FROM Customer INNER JOIN Orders ON Customer.CustomerID = Orders.CustomerID
go
raiserror('update view <dbo.Orders Qry>...', 0, 1) with nowait
go
if (@@error <> 0) and (@@trancount > 0)
rollback transaction
go
if (@@trancount = 0)
begin
insert into #err(flag) select cast(1 as bit)
begin transaction
end
go
ALTER view dbo.[Quarterly Orders] AS
SELECT DISTINCT Customer.CustomerID, Customer.CompanyName, Customer.City, Customer.Country
FROM Customer RIGHT JOIN Orders ON Customer.CustomerID = Orders.CustomerID
WHERE Orders.OrderDate BETWEEN '19970101' And '19971231'
go
raiserror('update view <dbo.Quarterly Orders>...', 0, 1) with nowait
go
if (@@error <> 0) and (@@trancount > 0)
rollback transaction
go
if (@@trancount = 0)
begin
insert into #err(flag) select cast(1 as bit)
begin transaction
end
go
ALTER view dbo.Invoices AS
SELECT Orders.ShipName, Orders.ShipAddress, Orders.ShipCity, Orders.ShipRegion, Orders.ShipPostalCode,
Orders.ShipCountry, Orders.CustomerID, Customer.CompanyName AS CustomerName, Customer.Address, Customer.City,
Customer.Region, Customer.PostalCode, Customer.Country,
(FirstName + ' ' + LastName) AS Salesperson,
Orders.OrderID, Orders.OrderDate, Orders.RequiredDate, Orders.ShippedDate, Shippers.CompanyName As ShipperName,
"Order Details".ProductID, Products.ProductName, "Order Details".UnitPrice, "Order Details".Quantity,
"Order Details".Discount,
(CONVERT(money,("Order Details".UnitPrice*Quantity*(1-Discount)/100))*100) AS ExtendedPrice, Orders.Freight
FROM Shippers INNER JOIN
(Products INNER JOIN
(
(Employees INNER JOIN
(Customer INNER JOIN Orders ON Customer.CustomerID = Orders.CustomerID)
ON Employees.EmployeeID = Orders.EmployeeID)
INNER JOIN "Order Details" ON Orders.OrderID = "Order Details".OrderID)
ON Products.ProductID = "Order Details".ProductID)
ON Shippers.ShipperID = Orders.ShipVia
go
raiserror('update view <dbo.Invoices>...', 0, 1) with nowait
go
if (@@error <> 0) and (@@trancount > 0)
rollback transaction
go
if (@@trancount = 0)
begin
insert into #err(flag) select cast(1 as bit)
begin transaction
end
go
ALTER PROCEDURE dbo.CustOrderHist @CustomerID nchar(5)
AS
SELECT ProductName, Total=SUM(Quantity)
FROM Products P, [Order Details] OD, Orders O, Customer C
WHERE C.CustomerID = @CustomerID
AND C.CustomerID = O.CustomerID AND O.OrderID = OD.OrderID AND OD.ProductID = P.ProductID
GROUP BY ProductName
go
raiserror('update storedprocedure <dbo.CustOrderHist>...', 0, 1) with nowait
go
if (@@error <> 0) and (@@trancount > 0)
rollback transaction
go
if (@@trancount = 0)
begin
insert into #err(flag) select cast(1 as bit)
begin transaction
end
go
ALTER view dbo.[Customer and Suppliers by City] AS
SELECT City, CompanyName, ContactName, 'Customers' AS Relationship
FROM Customer
UNION SELECT City, CompanyName, ContactName, 'Suppliers'
FROM Suppliers
--ORDER BY City, CompanyName
go
raiserror('update view <dbo.Customer and Suppliers by City>...', 0, 1) with nowait
go
if (@@error <> 0) and (@@trancount > 0)
rollback transaction
go
if (@@trancount = 0)
begin
insert into #err(flag) select cast(1 as bit)
begin transaction
end
go
ALTER view dbo.[Sales Totals by Amount] AS
SELECT "Order Subtotals".Subtotal AS SaleAmount, Orders.OrderID, Customer.CompanyName, Orders.ShippedDate
FROM Customer INNER JOIN
(Orders INNER JOIN "Order Subtotals" ON Orders.OrderID = "Order Subtotals".OrderID)
ON Customer.CustomerID = Orders.CustomerID
WHERE ("Order Subtotals".Subtotal >2500) AND (Orders.ShippedDate BETWEEN '19970101' And '19971231')
go
raiserror('update view <dbo.Sales Totals by Amount>...', 0, 1) with nowait
go
if (@@error <> 0) and (@@trancount > 0)
rollback transaction
go
if (@@trancount = 0)
begin
insert into #err(flag) select cast(1 as bit)
begin transaction
end
go
if exists (select * from #err)
begin
print 'the database <northwind> update failed'
rollback transaction
end
else
begin
print 'the database <northwind> update succeeded'
commit transaction
end
go
Запускаем скрипт на выполнение.
Запускаем тест.
Вуаля! Мы переименовали таблицу и не сломали существующий код.
Поступим также со всеми таблицами, имена которых заканчиваются на s. Это ведь так просто, неправда ли?
Рефакторинг «Добавление поисковой таблицы»
Эта операция позволяет создать поисковую таблицу для существующего столбца.
Необходимость этой операции может быть обусловлена следующими причинами:
- Введение ссылочной целостности для обеспечения качества данных;
- Предоставление подробных описаний. Например, может понадобится добавить в описание той или иной сущности новый атрибут. Если сущность при этой не выделена в таблицу — придется добавлять этот атрибут в нужные таблицы, что приведет к денормализации схемы.
Посмотрим еще раз на нашу таблицу dbo.Customer. Вас не смущает наличие полей City, Region и Country в одном месте? Это ведь атрибуты одной сущности.
В таблице dbo.Employees та же беда. На вид явное нарушение 3-й нормальной формы.
Давайте начинать исправлять дело следующим образом:
1. Создадим справочник dbo.City(CityId, CityName)
2. В таблице dbo.Customer добавим поле CityId.
3. Создадим внешний ключ.
Опять же, для экономии времени, используем функцию Add Lookup Table. В OE выбираем поле City таблицы dbo.Customer и в контекстном меню вызываем пункт SQL Refactor Studio -> Add Lookup Table.
В появившемся окне заполняем поля. Жмем Next, потом Finish и в новом окне формируется скрипт.
Скрипт создает таблицу dbo.City, заполняет ее данными, создает поле CityId в таблице dbo.Customer, создает внешний ключ.
Сгенерированный скрипт
use northwind
go
-- Step 1. Create lookup table.
CREATE TABLE dbo.City (
CityId INT NOT NULL identity(1, 1)
,CityName NVARCHAR(15) NULL
,CONSTRAINT PK_City PRIMARY KEY CLUSTERED (CityId)
,CONSTRAINT City_ixCityName UNIQUE (CityName)
)
GO
-- Step 2. Fill lookup table.
INSERT dbo.City (CityName)
SELECT DISTINCT City
FROM dbo.Customer
GO
-- Step 3. Add column.
ALTER TABLE dbo.Customer ADD CityId INT NULL
GO
-- Step 4. Update table dbo.Customer.
UPDATE s
SET s.CityId = t.CityId
FROM dbo.Customer s
INNER JOIN dbo.City t ON s.City = t.CityName
GO
-- Step 5. Create foreign key constraint.
ALTER TABLE dbo.Customer ADD CONSTRAINT FK_Customer_City FOREIGN KEY (CityId) REFERENCES dbo.City (CityId)
GO
Запускаем скрипт и тесты (хотя тут мы вроде ничего не должны были сломать).
Все готово. Идем далее.
Рефакторинг «Перемещение полей»
Итак, с полем City мы разобрались. Осталось разобраться с полями Region и Country. Данные поля являются атрибутами сущности City. Так давайте же перенесем их из таблицы dbo.Customer в dbo.City.
Опять же, SQL Refactor Studio предоставляет функцию Move Columns. Ею и воспользуемся!
Выбираем в OE таблицу dbo.Customer, в контекстном меню выбираем пункт «SQL Refactor Studio -> Move columns». В появившемся диалоге, в выпадающем списке, выбираем таблицу dbo.City. Переносим поля Region и Country в dbo.City.
Если при переносе поля, вы получите сообщение о том, что нельзя переместить поле на котором построен индекс — удалите на время этот индекс.
Нажимаем Next, потом Finish. Получаем скрипт в новом окне.
Скрипт создает поля Region и Country в таблице dbo.City и заполняет их данными.
В скрипте есть также закомментированный код удаляющий поля и приведен список объектов в которых нужно внести изменения.
Давайте не будем сейчас удалять поля, сделаем это на следующем шаге.
Сгенерированный скрипт
USE northwind
GO
-- STEP 1. Add new column(s) --
IF NOT EXISTS (
SELECT *
FROM syscolumns s
WHERE s.NAME = 'Region'
AND s.id = object_id(N'dbo.City')
)
BEGIN
ALTER TABLE dbo.City ADD Region NVARCHAR(15) NULL
END
GO
IF NOT EXISTS (
SELECT *
FROM syscolumns s
WHERE s.NAME = 'Country'
AND s.id = object_id(N'dbo.City')
)
BEGIN
ALTER TABLE dbo.City ADD Country NVARCHAR(15) NULL
END
GO
GO
-- STEP 2. Copy data --
-- (You can modify this query if needed)
SET IDENTITY_INSERT dbo.City ON
INSERT INTO dbo.City WITH (TABLOCKX) (CityId)
SELECT CityId
FROM dbo.Customer src
WHERE NOT EXISTS (
SELECT *
FROM dbo.City dest
WHERE src.CityId = dest.CityId
)
SET IDENTITY_INSERT dbo.City OFF
UPDATE dest
WITH (TABLOCKX)
SET dest.Region = src.Region
,dest.Country = src.Country
FROM dbo.City dest
INNER JOIN dbo.Customer src ON (src.CityId = dest.CityId)
GO
-- STEP 3. Check and modify this dependent objects --
/*
northwind.dbo.[Orders Qry] /*View*/
northwind.dbo.[Quarterly Orders] /*View*/
northwind.dbo.Invoices /*View*/
northwind.dbo.CustOrderHist /*StoredProcedure*/
northwind.dbo.[Customer and Suppliers by City] /*View*/
northwind.dbo.[Sales Totals by Amount] /*View*/
*/
-- STEP 4. Drop column(s) --
-- (Uncomment or run separately this query)
/*
alter table dbo.Customer drop column Region
alter table dbo.Customer drop column Country
*/
GO
Выполняем скрипт и тесты.
Переходим к следующему шагу.
Рефакторинг «Удаление объекта»
Выполняя предыдущие рефакторинги, мы оставили немного мусора (поля City, Region, Country в таблице dbo.Customer).
Давайте наводить чистоту! Но если мы просто так удалим поля, у нас опять все сломается.
Можно воспользоваться рефакторингом Encapsulate Table With View.
Создадим представление dbo.CustomerV и заменим использование таблицы его представлением во всей базе данных.
CREATE VIEW dbo.CustomerV
AS
SELECT
c.CustomerID,
c.CompanyName,
c.ContactName,
c.ContactTitle,
c.Address,
ct.CityName City,
ct.Region,
c.PostalCode,
ct.Country,
c.Phone,
c.Fax,
c.CityId
FROM dbo.Customer AS c
LEFT JOIN dbo.City AS ct
ON c.CityId = ct.CityId
Далее, при помощи View Dependencies смотрим зависимости для таблицы dbo.Customer:
Просматриваем каждый объект. Если в каком-либо объекте используются наши поля, скриптуем объект (кнопка Script object на тулбаре) и вносим изменения.
В результате у меня получился вот такой скрипт:
Encapsulate Table With View
ALTER view dbo.[Sales Totals by Amount] AS
SELECT st.Subtotal AS SaleAmount, o.OrderID, c.CompanyName, o.ShippedDate
FROM dbo.CustomerV c
JOIN (
dbo.Orders o
JOIN "Order Subtotals" st
ON o.OrderID = st.OrderID
)
ON c.CustomerID = o.CustomerID
WHERE (st.Subtotal >2500) AND (o.ShippedDate BETWEEN '19970101' And '19971231')
GO
ALTER view dbo.[Quarterly Orders]
AS
SELECT DISTINCT c.CustomerID, c.CompanyName, c.City, c.Country
FROM dbo.CustomerV c
RIGHT JOIN dbo.Orders o
ON c.CustomerID = o.CustomerID
WHERE
o.OrderDate BETWEEN '19970101' And '19971231'
GO
ALTER view dbo.[Orders Qry]
AS
SELECT o.OrderID, o.CustomerID, o.EmployeeID, o.OrderDate, o.RequiredDate,
o.ShippedDate, o.ShipVia, o.Freight, o.ShipName, o.ShipAddress, o.ShipCity,
o.ShipRegion, o.ShipPostalCode, o.ShipCountry,
c.CompanyName, c.Address, c.City, c.Region, c.PostalCode, c.Country
FROM dbo.CustomerV c
INNER JOIN dbo.Orders o
ON c.CustomerID = o.CustomerID
GO
ALTER view dbo.Invoices AS
SELECT Orders.ShipName, Orders.ShipAddress, Orders.ShipCity, Orders.ShipRegion, Orders.ShipPostalCode,
Orders.ShipCountry, Orders.CustomerID, Customer.CompanyName AS CustomerName, Customer.Address, Customer.City,
Customer.Region, Customer.PostalCode, Customer.Country,
(FirstName + ' ' + LastName) AS Salesperson,
Orders.OrderID, Orders.OrderDate, Orders.RequiredDate, Orders.ShippedDate, Shippers.CompanyName As ShipperName,
"Order Details".ProductID, Products.ProductName, "Order Details".UnitPrice, "Order Details".Quantity,
"Order Details".Discount,
(CONVERT(money,("Order Details".UnitPrice * Quantity*(1-Discount)/100))*100) AS ExtendedPrice, Orders.Freight
FROM Shippers INNER JOIN
(Products INNER JOIN
(
(Employees INNER JOIN
(dbo.CustomerV Customer INNER JOIN Orders ON Customer.CustomerID = Orders.CustomerID)
ON Employees.EmployeeID = Orders.EmployeeID)
INNER JOIN "Order Details" ON Orders.OrderID = "Order Details".OrderID)
ON Products.ProductID = "Order Details".ProductID)
ON Shippers.ShipperID = Orders.ShipVia
GO
ALTER view dbo.[Customer and Suppliers by City] AS
SELECT City, CompanyName, ContactName, 'Customers' AS Relationship
FROM dbo.CustomerV
UNION SELECT City, CompanyName, ContactName, 'Suppliers'
FROM Suppliers
--ORDER BY City, CompanyName
GO
Запускаем скрипт, после чего удаляем поля и прогоняем тесты.
Если все ОК — Вы молодец, все аккуратно сделали.
Рефакторинг «Добавление методов CRUD»
Данный рефакторинг предусматривает создание хранимых процедур, обеспечивающих доступ (SELECT, INSERT, UPDATE, DELETE) к таблицам базы данных.
Можно выделить следующие причины использовать этот рефакторинг:
- Скрыть от внешних приложений структуру базы данных. Внешние приложения будут обращаться к базе данных только посредством хранимых процедур. Это позволит с легкостью изменять структуру базы данных без изменения внешних приложений.
- Реализация в процедурах дополнительных проверок (бизнес-логики, прав доступа и т.д.);
- Сохранение информации о том кто и когда какие данные запрашивал;
- Проблемы с производительностью эффективнее решать когда SQL-код находится в базе данных.
Итак, давайте создадим для нашей замученной таблицы dbo.Customer методы доступа. Воспользуемся методом Add CRUD Methods из пакета SQL Refactor Studio. Выбираем в OE таблицу, далее в контекстном меню выбираем пункт SQL Refactor Studio -> Add CRUD Methods.
В появившемся диалоге выбираем какие методы нам нужно создать. При необходимости меняем названия методов. Жмем Next. При желании можно настроить права на процедуры, для этого нужно отметить нужные роли. Нажимаем Finish и получаем скрипт с хранимыми процедурами. Выполняем скрипт.
Сгенерированный скрипт
IF (object_id(N'dbo.Customer_Create') IS NULL)
BEGIN
EXEC ('create procedure dbo.Customer_Create as return 0')
END
GO
-- =============================================
--
-- dbo.Customer_Create
--
-- Create method.
--
-- Date: 07.09.2015, @HabraUser
--
-- =============================================
ALTER PROCEDURE dbo.Customer_Create
@CustomerID NCHAR(5)
,@CompanyName NVARCHAR(40)
,@ContactName NVARCHAR(30) = NULL
,@ContactTitle NVARCHAR(30) = NULL
,@Address NVARCHAR(60) = NULL
,@PostalCode NVARCHAR(10) = NULL
,@Phone NVARCHAR(24) = NULL
,@Fax NVARCHAR(24) = NULL
,@CityId INT = NULL
AS
BEGIN
SET NOCOUNT ON
INSERT INTO dbo.Customer (
CustomerID
,CompanyName
,ContactName
,ContactTitle
,Address
,PostalCode
,Phone
,Fax
,CityId
)
VALUES (
@CustomerID
,@CompanyName
,@ContactName
,@ContactTitle
,@Address
,@PostalCode
,@Phone
,@Fax
,@CityId
)
RETURN 0
END
/*
declare
@CustomerID NChar(5),
@CompanyName NVarChar(40),
@ContactName NVarChar(30),
@ContactTitle NVarChar(30),
@Address NVarChar(60),
@PostalCode NVarChar(10),
@Phone NVarChar(24),
@Fax NVarChar(24),
@CityId Int
select
@CustomerID = 'CustomerID',
@CompanyName = 'CompanyName',
@ContactName = 'ContactName',
@ContactTitle = 'ContactTitle',
@Address = 'Address',
@PostalCode = 'PostalCode',
@Phone = 'Phone',
@Fax = 'Fax',
@CityId = null
exec dbo.Customer_Create
@CustomerID = @CustomerID,
@CompanyName = @CompanyName,
@ContactName = @ContactName,
@ContactTitle = @ContactTitle,
@Address = @Address,
@PostalCode = @PostalCode,
@Phone = @Phone,
@Fax = @Fax,
@CityId = @CityId
*/
GO
IF (object_id(N'dbo.Customer_Get') IS NULL)
BEGIN
EXEC ('create procedure dbo.Customer_Get as return 0')
END
GO
-- =============================================
--
-- dbo.Customer_Get
--
-- Read method.
--
-- Date: 07.09.2015, @HabraUser
--
-- =============================================
ALTER PROCEDURE dbo.Customer_Get
@CustomerID NCHAR(5)
AS
BEGIN
SET NOCOUNT ON
SELECT
CustomerID
,CompanyName
,ContactName
,ContactTitle
,Address
,PostalCode
,Phone
,Fax
,CityId
FROM dbo.Customer
WHERE
CustomerID = @CustomerID
RETURN 0
END
/*
declare
@CustomerID NChar(5)
select
@CustomerID = ?
exec dbo.Customer_Get
@CustomerID = @CustomerID
*/
GO
IF (object_id(N'dbo.Customer_Save') IS NULL)
BEGIN
EXEC ('create procedure dbo.Customer_Save as return 0')
END
GO
-- =============================================
--
-- dbo.Customer_Save
--
-- Update method.
--
-- Date: 07.09.2015, @HabraUser
--
-- =============================================
ALTER PROCEDURE dbo.Customer_Save
@CustomerID NCHAR(5)
,@CompanyName NVARCHAR(40)
,@ContactName NVARCHAR(30) = NULL
,@ContactTitle NVARCHAR(30) = NULL
,@Address NVARCHAR(60) = NULL
,@PostalCode NVARCHAR(10) = NULL
,@Phone NVARCHAR(24) = NULL
,@Fax NVARCHAR(24) = NULL
,@CityId INT = NULL
AS
BEGIN
SET NOCOUNT ON
UPDATE t
SET t.CompanyName = @CompanyName
,t.ContactName = @ContactName
,t.ContactTitle = @ContactTitle
,t.Address = @Address
,t.PostalCode = @PostalCode
,t.Phone = @Phone
,t.Fax = @Fax
,t.CityId = @CityId
FROM dbo.Customer AS t
WHERE
t.CustomerID = @CustomerID
RETURN 0
END
/*
set nocount on
set quoted_identifier, ansi_nulls, ansi_warnings, arithabort, concat_null_yields_null, ansi_padding on
set numeric_roundabort off
set transaction isolation level read uncommitted
declare
@CustomerID NChar(5),
@CompanyName NVarChar(40),
@ContactName NVarChar(30),
@ContactTitle NVarChar(30),
@Address NVarChar(60),
@PostalCode NVarChar(10),
@Phone NVarChar(24),
@Fax NVarChar(24),
@CityId Int
select
@CustomerID = 'CustomerID',
@CompanyName = 'CompanyName',
@ContactName = 'ContactName',
@ContactTitle = 'ContactTitle',
@Address = 'Address',
@PostalCode = 'PostalCode',
@Phone = 'Phone',
@Fax = 'Fax',
@CityId = null
begin try
begin tran
exec dbo.Customer_Save
@CustomerID = @CustomerID,
@CompanyName = @CompanyName,
@ContactName = @ContactName,
@ContactTitle = @ContactTitle,
@Address = @Address,
@PostalCode = @PostalCode,
@Phone = @Phone,
@Fax = @Fax,
@CityId = @CityId
select t.*
from dbo.Customer as t
where
t.CustomerID = @CustomerID
if @@trancount > 0
rollback tran
end try
begin catch
if @@trancount > 0
rollback tran
declare
@err nvarchar(2000)
set @err =
'login: ' + suser_sname() + char(10)
+ 'ErrorNumber: ' + cast(isnull(error_number(), 0) as varchar) + char(10)
+ 'ErrorProcedure: ' + isnull(error_procedure(), '') + char(10)
+ 'ErrorLine: ' + cast(isnull(error_line(), 0) as varchar) + char(10)
+ 'ErrorMessage: ' + isnull(error_message(), '') + char(10)
+ 'Date: ' + cast(getdate() as varchar) + char(10)
print @err
raiserror(@err, 16, 1)
end catch
*/
GO
IF (object_id(N'dbo.Customer_Del') IS NULL)
BEGIN
EXEC ('create procedure dbo.Customer_Del as return 0')
END
GO
-- =============================================
--
-- dbo.Customer_Del
--
-- Delete method.
--
-- Date: 07.09.2015, @HabraUser
--
-- =============================================
ALTER PROCEDURE dbo.Customer_Del @CustomerID NCHAR(5)
AS
BEGIN
SET NOCOUNT ON
BEGIN TRY
BEGIN TRAN
/* uncomment if needed
delete from dbo.CustomerCustomerDemo where CustomerID = ?
delete from dbo.Orders where CustomerID = ?
*/
DELETE
FROM dbo.Customer
WHERE CustomerID = @CustomerID
COMMIT TRAN
END TRY
BEGIN CATCH
IF @@trancount > 0
ROLLBACK TRAN
-- catch exception (add you code here)
DECLARE @err NVARCHAR(2000)
SET @err = ERROR_MESSAGE()
RAISERROR (@err, 16, 1)
END CATCH
RETURN 0
END
/*
declare
@CustomerID NChar(5)
select
@CustomerID = ?
exec dbo.Customer_Del
@CustomerID = @CustomerID
*/
GO
IF (object_id(N'dbo.Customer_List') IS NULL)
BEGIN
EXEC ('create procedure dbo.Customer_List as return 0')
END
GO
-- =============================================
--
-- dbo.Customer_List
--
-- List method.
--
-- Date: 07.09.2015, @HabraUser
--
-- =============================================
ALTER PROCEDURE dbo.Customer_List
AS
BEGIN
SET NOCOUNT ON
SELECT
CustomerID
,CompanyName
,ContactName
,ContactTitle
,Address
,PostalCode
,Phone
,Fax
,CityId
FROM dbo.Customer
/* uncomment if needed
left join dbo.City as t1 on t1.CityId = t.CityId
*/
RETURN 0
END
/*
exec dbo.Customer_List
*/
GO
Если Вам нужно поправить тело генерируемых процедур, заходим в настройки.
Каждая процедура представляет собой шаблон T4. Про T4 можно почитать тут и тут.
Рефакторинг «Введение триггера для накопления исторических данных»
Эта операция позволяет ввести новый триггер, предназначенный для накопления информации об изменениях в данных в целях изучения истории внесения изменений или проведения аудита.
Необходимость в применении операции «Введение триггера для накопления исторических данных» в основном обусловлена требованием передать функции отслеживания изменений в данных самой базе данных. Такой подход гарантирует, что в случае модификации важных данных в любом внешнем приложении это изменение можно будет отследить и подвергнуть аудиту.
Единственным недостатком, на мой взгляд, является тот факт что наличие триггера будет увеличивать время выполнения операции DML.
Как альтернативу данному методу можно рассмотреть Change Data Capture (работает асинхронно, тем самым не увеличивает время операции, но имеет ряд особенностей).
Давайте для таблицы dbo.Customer применим данный рефакторинг.
Выбираем таблицу в OE, выбираем в контекстном меню пункт SQL Refactor Studio — Introduce Trigger for History. Выбираем поля таблицы для отслеживания изменений.
Жмем Next. Изменяем при необходимости имя создаваемой таблицы и триггера.
Жмем Finish и получаем скрипт. Скрипт создает триггер и таблицу для хранения истории изменений.
Сгенерированный скрипт
CREATE TABLE [dbo].[CustomerHistory] (
[id] [bigint] IDENTITY NOT NULL
,[action_type] [char](1) NOT NULL
,[modified_date] [datetime] CONSTRAINT [DF_CustomerHistory_modified_date] DEFAULT getdate()
,[modified_login] [sysname] CONSTRAINT [DF_CustomerHistory_modified_login] DEFAULT suser_sname()
,[host_name] [nvarchar](128) CONSTRAINT [DF_CustomerHistory_host_name] DEFAULT host_name()
,[program_name] [nvarchar](128) CONSTRAINT [DF_CustomerHistory_program_name] DEFAULT program_name()
,[CompanyName_old] [nvarchar](40)
,[CompanyName_new] [nvarchar](40)
,[ContactName_old] [nvarchar](30)
,[ContactName_new] [nvarchar](30)
,[ContactTitle_old] [nvarchar](30)
,[ContactTitle_new] [nvarchar](30)
,[Address_old] [nvarchar](60)
,[Address_new] [nvarchar](60)
,[PostalCode_old] [nvarchar](10)
,[PostalCode_new] [nvarchar](10)
,[Phone_old] [nvarchar](24)
,[Phone_new] [nvarchar](24)
,[Fax_old] [nvarchar](24)
,[Fax_new] [nvarchar](24)
,[CityId_old] [int]
,[CityId_new] [int]
,[CustomerID_old] [nchar](5)
,[CustomerID_new] [nchar](5)
,CONSTRAINT [PK_CustomerHistory] PRIMARY KEY ([id])
)
GO
CREATE TRIGGER [dbo].[trg_CustomerHistory] ON [dbo].[Customer]
AFTER INSERT, DELETE, UPDATE
AS
SET NOCOUNT ON
DECLARE @action_type CHAR(1)
IF EXISTS (SELECT *FROM inserted) AND EXISTS (SELECT * FROM deleted)
SET @action_type = 'U'
ELSE IF EXISTS (SELECT * FROM inserted) AND NOT EXISTS (SELECT * FROM deleted)
SET @action_type = 'I'
ELSE IF NOT EXISTS (SELECT * FROM inserted) AND EXISTS (SELECT * FROM deleted)
SET @action_type = 'D'
INSERT INTO dbo.CustomerHistory (
action_type
,CompanyName_old
,CompanyName_new
,ContactName_old
,ContactName_new
,ContactTitle_old
,ContactTitle_new
,Address_old
,Address_new
,PostalCode_old
,PostalCode_new
,Phone_old
,Phone_new
,Fax_old
,Fax_new
,CityId_old
,CityId_new
,CustomerID_old
,CustomerID_new
)
SELECT
@action_type
,d.CompanyName
,i.CompanyName
,d.ContactName
,i.ContactName
,d.ContactTitle
,i.ContactTitle
,d.Address
,i.Address
,d.PostalCode
,i.PostalCode
,d.Phone
,i.Phone
,d.Fax
,i.Fax
,d.CityId
,i.CityId
,d.CustomerID
,i.CustomerID
FROM inserted i
FULL OUTER JOIN deleted d
ON (i.CustomerID = d.CustomerID)
GO
На этом пока всё. Надеюсь информация Вам пригодится и в Ваших базах данных всегда будет полный порядок.
Удачи!
Полезные ресурсы
- http://databaserefactoring.com
- Скотт Амблер, Прамодкумар Дж. Садаладж Рефакторинг баз данных. Эволюционное проектирование
- Мартин Фаулер Рефакторинг.Улучшение существующего кода
- Плагин для SSMS SQL Refactor Studio
Под модификацией структуры базы данных
понимается изменение структуры отдельных
таблиц или связей между ними.
Изменение макета
таблицы
Для удобства работы с таблицей можно
изменить ее представления на экране.
При этом можно менять ширину столбца,
высоту строки, шрифт данных таблицы,
цвет текста, линий сетки и фона, оформление,
которое может быть обычным, приподнятым
или утопленным. Эти параметры отображения
таблицы на экране называются макетом
таблицы и сохраняются вместе с ней.
Настройка макета выполняется в режиме
таблицы. При этом может быть использована
панель инструментов Форматы в режиме
таблицы и пункт меню Формат.
Изменение структуры
таблиц
Все изменения структуры таблиц
производятся в режиме конструктора
таблиц.
Изменение полей, которые не являются
ключами или полями связи. Состав и
последовательность, а также тип данных,
свойства или имена этих полей можно
изменять независимо от наличия связей
таблицы с другими таблицами базы данных.
Для заполненной таблицы беспрепятственно
выполняется изменение имени поля,
добавления нового поля, удаления поля
и изменение последовательности полей.
При изменении типа данных, размера и
других свойств производится преобразование
данных. Однако если преобразования
недопустимы, попытка изменить тип данных
может привести к потере данных.
Изменение или удаление
ключевого поля. Если надо изменить
ключ таблицы, которая имеет связи с
другими таблицами, необходимо
предварительно разорвать связи в схеме
данных.
Для загруженной и несвязанной таблицы
при попытке изменить свойства или
удалить ключевое поле, система
предупреждает о возможной потере данных
и удалении ключа. Если, например, удаляется
поле в составном ключе, с других полей
этого ключа будет снят признак ключа.
Изменения, сделанные в структуре таблиц,
автоматически не переносятся системой
в использующие их формы, запросы, отчеты.
Изменение схемы
данных
При модификации схемы данных осуществляется
изменение состава ее таблиц — удаление,
добавление таблиц и изменение связей.
Для внесения изменений в схему данных
необходимо закрыть все таблицы и
выполнить команду Сервис/Схема
данных или нажать кнопку Схема
данных на панели инструментов.
Добавление таблиц выполняется
при нажатии кнопки Добавить. В окне
Добавление таблицы надо выделить
нужную таблицу и нажать кнопку Добавить.
Удаление таблицы из схемы данных
осуществляется переходом в окне Схема
данных. В этом окне надо удалить
связи таблицы и при отмеченной таблице
выполнить команду Правка/ Удалить
или нажать клавишу <Del>.
Чтобы удалить связь нужно
отметить ее щелчком мыши, нажать правую
кнопку мыши, вызывающую контекстное
меню, а затем выполнить команду Удалить
связь. Помеченную связь можно также
удалить по команде Правка/Удалить
или по клавише <Del>.
Изменение параметров связи
выполняется командой Связи/Изменить
связь или соответствующей командой
контекстного меню.
3. Обработка данных
Обработка данных включает просмотр,
добавление и удаление записей, обновление
полей. При просмотре записей можно
осуществлять поиск записей по одному
или нескольким полям и фильтрацию
записей в соответствии с заданными
условиями отбора.
Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
- #
Материал из Info
Перейти к: навигация, поиск
В исключительно редких случаях возможно появление проблемы в базе данных. Для ее исправления предвидены соответствующие процедуры, которые зависят от вида базы.
Содержание
- 1 Подготовка к изменению базы
- 2 Access
- 3 MySQL
- 4 MSDE и Microsoft SQL Server 2000
- 5 Microsoft SQL Server 2005+
- 6 База в режиме Suspected
- 7 Для Microsoft SQL Server 2005/2008/2008 R2/2012/2014/2016:
- 8 Для MSDE и Microsoft SQL Server 2000 выполняются:
- 9 Некоторые полезные ресурсы
Подготовка к изменению базы
Прежде, чем начать реальное изменение базы данных, необходимо выполнить ряд процедур по проверке системы. Процедуры включают следующие шаги:
- Проверка свободного места на диске. Необходимо обратить внимание на папки, которые сохраняют базу данных, сам раздел с Windows и временные папки;
- Ошибки MSDE , Microsoft SQL Server 2005/2008/2012/2014/2016 записываются в Event Log Windows (Start/Control panel/Administrative Tools/Event Viewer), откуда можно понять, где именно проблема: в диске, в базе данных или в самом SQL сервере;
- Осуществляется проверка папок на наличие поврежденных секторов на диске;
- Осуществляется проверка последней архивной копии базы данных, при условии, что обнаружено наличие архива, в котором установлено повреждение, и он не проходит проверку на целостность. Простейшим решением проблемы является восстановление базы данных из архива.
После выполнения этих обязательных действий можно приступить к фактическому ремонту базы данных.
Access
При исправлении базы данных Access необходимо выполнить следующие действия:
- Обязательно создается архивная копия базы данных. Используются следующие методы:
- Если есть возможность открыть базу данных при помощи самой программы, то резервная копия базы создается в меню Файл / Резервное копирование и восстановление. Имя и путь к базе данных проверяется в меню Файл / Активная база данных;
- Если возникают проблемы с открытием базы данных при помощи программы, то осуществляется ее копирование вручную в другую, нерабочую папку. Папка базы данных описана в этой статье: Папка с продуктами.
- Открывается база данных в MS Access, версии 2003, 2007 или 2010. Для получения доступа к базе данных необходимо ввести инженерный пароль, который можно получить в отделе технической поддержки в Болгарии (Предоставляется только партнёрам Микринвест);
- Если при открытии базы данных Microsoft Access появляется сообщение, что файл не может быть использован, то возникает необходимость подключения дополнительного программного обеспечения для восстановления данных или поиска заархивированной копии базы данных;
- После открытия БД, в зависимости от версии MS Access, выбирается:
- MS Access 2003 — из меню Tools->Database Utilities->Compact and Repair Database…;
- MS Access 2007 — выбирается кнопка Office Button, которая находится в верхнем левом углу, потом опция Manage и Compact and Repair Database;
- MS Access 2010 — из меню File->Info->Compact & Repair;
Таким образом, изменение базы данных завершено.
MySQL
Для того чтобы произвести изменение базы данных сервера MySQL, необходимо наличие дополнительных инструментов:
- MySQL GUI Tools или MySQL Administrator. Данный продукт можно скачать отсюда: http://dev.mysql.com/downloads/gui-tools/5.0.html
После установки MySQL GUI Tools или MySQL Administrator, следует запустить MySQL Administrator, который запрашивает дополнительную информацию для соединения с сервером, содержащим базу данных.
Поля, необходимые для подключения к серверу:
Server Host — название или IP сервера, на котором находится база данных. Данные этого поля соответствуют аналогичным данным в настройке Microinvest Склад Pro;
Username — Имя пользователя для подключения к серверу. По умолчанию это root. Данные поля соответствуют аналогичным данным в настройке Microinvest Склад Pro;
Pasword — Пароль для подключения к серверу. Данные поля соответствуют аналогичным данным в настройке Microinvest Склад Pro.
После успешного запуска MySQL Administrator и осуществления подключения к базе данных, выполняются следующие шаги:
- Создаем архив базы данных:
- Используется Microinvest Архи Pro
- «MySQL Administrator» — содержит встроенные функции для создания архивов, выполняются следующие операции:
- В верхнем левом меню выбирается Backup;
- В нижнем правом углу нажимается кнопка New Project;
- Левой кнопкой мыши выделяется база данных, которую хотите заархивировать (Schema);
- Нажимается маленькая квадратная кнопка «>» для того, чтобы подтвердить выбор указанной базы данных. Она будет отражена в правой части приложения вместе с относящимися к ней таблицами (Backup Content);
- Подтверждение происходит нажатием кнопки Execute Backup Now;
- Открывается окошко Save As, в котором нужно указать, где будет сохранен архивный файл;
- После выбора кнопки Save начинается процесс создания архива. Необходимо дождаться появления системного сообщения «The Backup was finished successfully». Операция по созданию архива заканчивается нажатием OK;
- В верхнем левом меню выбирается Catalogs. В нижнем левом меню появляются базы данных, которые расположены на данном сервере;
- Выбирается база данных и выделяется левой кнопкой мыши. В правой части программы появляется структура баз данных и относящиеся к ней таблицы (Schema Tables);
- В нижнем правом углу расположена кнопка Maintenance. Открываются опции задач, и там указывается Repair Tables. Подтверждается с «Next >>», выделяется уровень изменения Extended. Последним шагом является активация «Repair Tables».
На этом процесс изменения БД MySQL завершен.
MSDE и Microsoft SQL Server 2000
Для выполнения изменения базы данных необходимо наличие инструментов для управления самим сервером:
- Процедуры осуществляются с помощью Microsoft SQL Server Management Studio;
- При условии, что версия Windows является 64-битной, скачать 64-битную версию можно по следующей ссылке: Microsoft SQL Server Management Studio;
- Если на компьютере не установлен MSXML6.0, то этот компонент необходимо инсталлировать до начала установки Microsoft SQL Server Management Studio;
- MSXML6.0 для 64-битной версии Windows доступен здесь;
- После успешной установки компонентов, запускается Microsoft SQL Server Management Studio и подключается к серверу MSDE путем указания имени и пароля пользователя для доступа к SQL серверу. Эти параметры настраиваются еще при установке MSDE и соответствуют параметрам для соединения с базой данных в программе;
- В Microsoft SQL Server Management Studio раскрывается папка Databases, выделяется база, которую необходимо восстановить, и при помощи правой кнопки мыши выбирается опция „New Query”. Аналогичный результат можно получить путем выделения левой кнопкой мыши базы данных, выбирая команду „New Query”;
- В открывшемся окне выписываются следующие команды:
ALTER DATABASE db_name SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DBCC CHECKDB ( 'db_name' , REPAIR_ALLOW_DATA_LOSS); ALTER DATABASE db_name SET MULTI_USER;
где вместо db_name записывается имя базы, нуждающейся в восстановлении. Все заявки выполняются в окне путем нажатия клавиши F5 или кнопки Execute в Microsoft SQL Server Management Studio. В зависимости от величины базы данных и вида ошибок, время обработки заявки может варьировать от нескольких секунд до нескольких часов.
- Когда завершается обработка заявок, в Microsoft SQL Server Management Studio выводится информация по базе данных. Если в разделе Messages присутствуют строчки, выделенные красным цветом, это означает, что заявка обнаружила проблемы в БД. В большинстве случаев обнаруженные проблемы обработаны, и, соответственно, в разделе Messages следует описание о том, удалось ли устранить обнаруженные ошибки или нет.
- В том случае, если не удается установить, исправлена ли база данных, можно заново запустить заявки для проверки. При повторном запуске заявок не должно быть строк, выделенных красным цветом.
Microsoft SQL Server 2005+
Для выполнения исправления базы данных необходимо наличие инструментов для управления самим сервером:
- Процедуры осуществляются с помощью Microsoft SQL Server Management Studio 2005 или Microsoft SQL Server Management Studio 2008;
- При условии, что версия Windows является 64-битной, скачать 64-битную версию можно по ссылке: Microsoft SQL Server Management Studio 2005 или Microsoft SQL Server Management Studio 2008;
- Если на компьютере не установлен MSXML6.0, то этот компонент необходимо инсталлировать до начала установки Microsoft SQL Server Management Studio;
- MSXML6.0 для 64-битной версии Windows доступен здесь;
- После успешной установки компонентов, запускается Microsoft SQL Server Management Studio и подключается к SQL Server. Имя пользователя и пароль соответствуют заданным при установке сервера и совпадают с логином и паролем программы;
- Раскрывается папка Databases, выделяется база, которую необходимо восстановить, и правой кнопкой мыши выбирается опция „New Query”. Аналогично базу данных можно выделить левой кнопкой мыши и выбрать „New Query”;
- В окне справа выводятся следующие команды:
ALTER DATABASE db_name SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DBCC CHECKDB ( 'db_name' , REPAIR_ALLOW_DATA_LOSS); ALTER DATABASE db_name SET MULTI_USER;
где вместо db_name записывается имя базы, нуждающейся в исправлении. Все заявки выполняются в окне путем нажатия клавиши F5 или кнопки Execute в Microsoft SQL Server Management Studio. В зависимости от величины базы данных и вида ошибок, время обработки заявки может варьировать от нескольких секунд до нескольких часов;
- Когда завершается обработка заявок, в Microsoft SQL Server Management Studio выводится информация по базе данных. Если в разделе Messages присутствуют строчки, выделенные красным цветом, это означает, что заявка обнаружила и исправила ошибки в базе данных. В большинстве случаев эти проблемы устранены. В разделе Messages описано исправлены ли обнаруженные ошибки или нет;
- В том случае, если не удается установить, изменена ли база данных успешно, можно заново запустить заявки для проверки. При повторном запуске заявок не должно быть строк, выделенных красным цветом.
База в режиме Suspected
Иногда сам сервер сообщает, что определенная база является «сомнительной». В таком случае она отмечается как «Suspected», и появляется необходимость выполнения ряда команд.
Для Microsoft SQL Server 2005/2008/2008 R2/2012/2014/2016:
sp_resetstatus 'db_name'; ALTER DATABASE db_name SET EMERGENCY; DBCC CHECKDB ( 'db_name' ) ; ALTER DATABASE db_name SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DBCC CHECKDB ( 'db_name' , REPAIR_ALLOW_DATA_LOSS); ALTER DATABASE db_name SET MULTI_USER;
где вместо db_name пишется имя базы данных. В случае обнаружения ошибки, в разделе Messages будут присутствовать строчки, выделенные красным цветом. Необходимо выполнить все строки запроса. Строку DBCC CHECKDB ( ‘db_name’ , REPAIR_ALLOW_DATA_LOSS), возможно, придется выполнить несколько раз, до устранения всех ошибок.
Заявки необходимо обрабатывать последовательно, строчка за строчкой, а не все вместе.
Для MSDE и Microsoft SQL Server 2000 выполняются:
USE master; sp_resetstatus 'db_name' ; sp_configure 'allow updates',1; reconfigure with override; UPDATE sysdatabases SET status = 32768 WHERE name = 'db_name' ; DBCC CHECKDB ( 'db_name' ) ; ALTER DATABASE db_name SET SINGLE_USER WITH ROLLBACK IMMEDIATE; DBCC CHECKDB ( 'db_name' , REPAIR_ALLOW_DATA_LOSS); ALTER DATABASE db_name SET MULTI_USER; UPDATE sysdatabases SET status = 4194328 WHERE name = 'db_name'; sp_configure 'allow updates',0; reconfigure with override;
где вместо db_name пишется имя поврежденной базы данных. При наличии проблем, в разделе Messages вновь будут присутствовать строчки, выделенные красным цветом.
Заявки необходимо обрабатывать последовательно, строчка за строчкой, а не все вместе;
Некоторые полезные ресурсы
- Документация по всем продуктам;
- Форум технической поддержки Microinvest, где Вы сможете быстро получить ответ на интересующий вопрос;
- Примеры автоматизации ресторанов и торговли на базе Microinvest в России и странах СНГ;
- График предстоящих обучений в России и странах СНГ;
- Программы для автоматизации ресторанов и кафе бесплатные полнофункциональные демо-версии продуктов Microinvest для ресторанного бизнеса;
- Программы для автоматизации торговли и склада бесплатные полнофункциональные демо-версии продуктов Microinvest для магазинов;
- Автоматизация торговли под Linux;
- www.microinvest.tv Видеоуроки, обучения;
- Корпоративный блог — интересные события в мире Microinvest;
- Все статьи Базы Знаний;
- Скачать офф-лайн версию Базы Знаний на свой компьютер.
Рассмотрите другие статьи в категории Решение проблем | Анализ
В нашем предыдущем материале Как эффективно управлять ростом объема данных речь шла о том, как с самого начала не допустить ошибок при работе с базами данных. В этой же статье предлагаем рассмотреть ситуации, когда с самого начала вы совершили просчеты и столкнулись с тем, что уже поздно менять структуру данных, но все еще есть возможность их оптимизировать.
- Как работать с индексами бд?
- Индексы в бд: о чем еще стоит помнить
- Критические ошибки: запросы
- Оптимизация индексов: о чем еще стоит задуматься?
Даже если вы прислушались к нашим рекомендациям и сделали все как по учебнику, после того как ваша система заработала и накопила определенное количество данных, с ними могут случаться проблемы, например, медленное выполнение запросов. Это самое частое явление, с которым обычно сталкиваются, когда работают с данными. И это нормально, потому что на этапе разработки структуры вы не всегда сможете предугадать, как будет развиваться ваша база данных. Предположим, вы запускаете MVP-проект, но впоследствии тестовый продукт может обрасти не тем функционалом, который был запланирован. С самого начала сложно все предусмотреть, поэтому заниматься оптимизацией не всегда имеет смысл, нужно сперва посмотреть, во что выльется проект. Все оптимизации, которые на этом этапе надо выполнять, обычно связаны с двумя вещами: во-первых, вы начинаете индексировать все, что индексировали до этого, а во-вторых, вы смотрите, как вообще осуществляются запросы, возможно, они выполняются неэффективно.
Как работать с индексами
Учимся читать план выполнения запроса.
Разработчики зачастую плохо понимают, как устроены базы данных. Нужно уметь разбираться в том, как их структурировать и проектировать. Также большинство разработчиков не понимает, что такое план выполнения запроса в СУБД. Но это несложно: достаточно изучить документацию и понять, где в запросах теряется время. Чаще всего помогает «навешивание» каких-нибудь индексов.
Частичный индекс компактнее полного.
По умолчанию индекс, который вы навешиваете на таблицу, индексирует вообще все значения. Но в большинстве случаев вам не нужны NULL-значения, если они есть базе. Так выкиньте их и постройте такой индекс (любая СУБД это позволяет), который NULL-значения не учитывает. Размер индекса в этом случае у вас сильно уменьшится и будет содержать в себе только то, что нужно. То же самое касается ситуации, когда значения, которые вы индексируете, тоже вам не нужны. В поле могут быть значения от 1 до 100, но вы чаще всего делаете запрос по значениям 1, 2, 3, поэтому можете легко проиндексировать только эти значения. В этом случае индекс у вас будет небольшого размера, и работать он будет быстро. Разницу между полным индексом и частичным можно даже увидеть на глаз в вашей системе мониторинга: время отклика резко упадет.
Кластеризованные индексы идеальны для дат.
Кластеризованным называют индекс, который гарантирует, что данные в вашей таблице идут друг за другом и так, как надо. Это бывает очень полезно, когда вы в своих запросах используете интервалы, и, чаще всего, интервалы по датам. Например, вы делаете запрос о поиске всех строк с 18 по 19 июня, и если вы используете на дате кластеризованный индекс, то строки будут идти подряд, а это значит, что СУБД их очень быстро считает с диска, а не будет «бегать» по всему диску и искать. Это минимизирует количество операций чтения и может значительно уменьшить время отклика.
Функциональный индекс поддерживают все СУБД.
С помощью него вы можете индексировать данные по какой-либо функции. Бывают ситуации, когда вам нужно найти всех людей, у которых день рождения 19 июня (не важно какого года). И именно функциональный индекс эффективен в данном случае.
Индексы по числам эффективнее, чем по строкам.
Храните все, что является числом, как число (номера телефонов, ИНН и так далее), потому что индекс по номерам телефонов и ИНН (его часто приходится навешивать и на одно, и на другое) будет занимать меньше места, а если что-либо в СУБД занимает меньше места, то оно быстрее находится, считывается, оптимально кэшируется и быстрее выдается. Все это влияет на производительность. Пока у вас маленькая база данных, это незаметно, а когда она вырастает до гигабайтов и количество запросов в секунду переваливает за несколько десятков, вы сразу это ощущаете.
Индексы: о чем стоит помнить?
Не все можно эффективно индексировать.
Не все понимают, что индексы имеют свои пределы и ограничения. Встречаются разработчики, которые при создании таблицы навешивали индексы на каждое поле с надеждой, что база будет работать быстрее. Вообще не рекомендуется индексы навешивать сразу при создании таблицы, нужно сначала дождаться результата, а потом смотреть, какая у вас логика и план выполнения запроса. Не нужно торопиться.
Поиск по нескольким индексированным полям — лотерея. Надо помнить, что если в одной таблице есть несколько индексированных полей и вы не по всем из них ищите данные, то результат будет непредсказуемым, как в лотерее. Ситуация может отличаться от сервера к серверу и от машины к машине. Почему? Разные условия, нагрузка, количество памяти, скорость работы с диском. Поэтому индексировать данные надо, изучая план запроса на том самом «боевом» сервере, на котором у вас возникли проблемы.
За индексами нужно ухаживать. Индексы со временем «разбухают», становятся большими, и их нужно постоянно чистить, сжимать, дефрагментировать — только тогда они будут работать эффективно.
Много индексов на таблице — признак перегруженной структуры.
Критические ошибки: запросы
Нижеприведенные рекомендации — это классика жанра, об этом написано в любом руководстве по тому, как правильно работать с SQL-базами данных. Никогда нельзя запрашивать все разом (SELECT *), запрашивайте только то, что вы ищите, это, если хотите, закон. Скорость работы СУБД зависит в том числе и от того, сколько вы из нее запрашиваете. Если вы запрашиваете много ненужных вещей, которые потом выкинете, значит вы делаете работу вхолостую. Такой запрос — зло, особенно в ситуации, когда таблица большая.
За SELECT COUNT (*) и LIMIT/OFFSET надо винить разработчиков таких интерфейсов, где есть списки с постраничным выводом данных. Это, может, выглядит красиво, но даже по опыту использования поисковиков мы знаем, что пользователи дальше второй страницы заходят редко. А если список длинный, то они и первую страницу просматривают не до конца. Нужно давать в интерфейсах возможность делать такие фильтры, чтобы данные можно было получить быстро и именно столько, сколько нужно.
Арифметика в фильтрах, функции и полнотекстовый поиск убивают индексы. Если в условиях поиска вы используете арифметику, функции или полнотекстовый поиск, то чаще всего это убивает любые индексы. Запросы не будут быстрыми: вам нужно будет или упрощать их, или использовать какие-то специфические решения (например, для полнотекстового поиска есть плагин либо функциональный индекс, о котором шла речь ранее).
Оптимизация: о чем еще стоит задуматься?
Настройка ORM.
На это месте нам начинает сильно мешать ORM. Разработчики любят ее использовать, это сильно упрощает работу, но она часто делает много лишних действий. Это могут быть как неоптимальные запросы, так и просто лишние. На практике при разборе ошибок мы видели ситуации, когда ORM запрашивает из базы что попало, когда ее об этом не просили. Ее нужно уметь настраивать, чтобы она этого не делала.
Рефакторинг бизнес-логики.
Иногда проще сделать два маленьких запроса, чем один большой — он будет медленнее. Но здесь каждый случай индивидуален, нужно уметь экспериментировать. Поэтому чтобы ускорить работу с базами, стоит и настроить ORM, и отрефакторить запросы к ним.
Контроль ссылочной целостности.
Речь идет о ситуациях, когда настроены каскадные удаления и обновления. В то время, как вы пытаетесь что-то удалить, СУБД ищет все связанные сущности и пытается понять, стоит их удалять или нет. В этом случае можно не удалять, а помечать как удаленную. Либо пытаться ссылочную целостность как-то порвать, но это может привести к несогласованности, когда вы случайно забудете удалить вместе с основной записью и все связанные с ней. В этом случае в вашей базе будут накапливаться «потерянные» записи. Они, как минимум, будут зря занимать место. Но в перспективе это может привести к неправильной работе бизнес-логики, что гораздо хуже. Так что, если вы очень часто удаляете что-то из БД, то рвать ссылочную целостность надо очень аккуратно, семь раз проверив, что вы удалили действительно всё.
Оптимизация структуры БД — исправление ошибок, совершенных ранее. Если к тому времени, когда у вас начались первые проблемы со скоростью вы видите, что ошиблись на первой стадии, настало время их поправить, потому что далее проблемы будут усугубляться.

Научиться настраивать MikroTik с нуля или систематизировать уже имеющиеся знания можно на углубленном курсе по администрированию MikroTik. Автор курса, сертифицированный тренер MikroTik Дмитрий Скоромнов, лично проверяет лабораторные работы и контролирует прогресс каждого своего студента. В три раза больше информации, чем в вендорской программе MTCNA, более 20 часов практики и доступ навсегда.
Давно известно: как вы лодку назовете — так она и поплывет. Это в полной мере применимо к такому известному инструменту как Тестирование и исправление информационной базы, название выбрано крайне неудачно, так как предполагает, что использовать представленные в нем возможности следует при возникновении проблем с информационной базой и исправлении ошибок. На самом деле это не так. Любой имеющий опыт работы с «серьезными» СУБД найдет в этом списке привычные ему инструменты обслуживания баз данных, которые следует применять регулярно для поддержания высокой производительности сервера. Но речь сейчас не о них, а о начинающих, либо имеющих к 1С опосредованное отношение.

Проверка логической целостности информационной базы проверяет и исправляет логические ошибки в структурах таблиц
Что это за ошибки, откуда они берутся, чем чреваты? Кто в теме — тот знает, а кто нет? Спросить? Да как бы неудобно, это же все знают… Вот и сводится большинство «знания» к тому, как правильно расставить в этой форме галочки и не забыть перед этим обязательно сделать резервную копию, а то мало ли…
Поэтому давайте разбираться, мы специально упростили многие вопросы, постаравшись сделать их понятными даже тем, кто имеет смутное представление о структуре и принципах работы баз данных.
Реиндексация таблиц информационной базы
Начнем с того, что такое индексы и для чего они нужны. Если рассматривать базу данных логически — то это некая совокупность связанных друг с другом таблиц, которые в свою очередь содержат какие-либо данные. Физически таблицы хранятся на диске в виде страниц и чем больше размер таблицы, тем большее количество страниц она будет содержать.
Допустим у нас есть некая таблица и мы хотим получить из нее все данные, связанные с фамилией Иванов. Для этого программе нужно последовательно считать все страницы, принадлежащие данной таблице и найти в них записи, соответствующие запросу.

В нашем случае роль такого каталога выполняет индекс. Это — специальный набор записей, связанный с определенным ключом — полем таблицы, в нашем случае фамилией, и указывающий на каких страницах хранятся интересующие записи. После того как мы создали индекс программе больше не нужно последовательно считывать всю таблицу, потому что известно, что записи, связанные с ключом Иванов, хранятся только на первой и третьей страницах.

Реиндексация — это простой и достаточно недорогой способ повысить эффективность работы с информационной базой, причем эффект может быть виден сразу, особенно в файловых базах. Поэтому реиндексацию делать нужно и желательно регулярно, включив ее в обязательный план регламентного обслуживания информационных баз.
Проверка логической целостности информационной базы
Если говорить о СУБД, то любая система управления базами данных, в том числе и та, которая используется файловой версией 1С, обеспечивает целостность данных встроенными средствами и вам вряд ли придется заниматься этим вопросом, если только, конечно, не произошло физического повреждения базы данных. Как мы уже говорили выше, база данных — это набор взаимосвязанных таблиц и самой СУБД в общем то все равно что мы храним в этих таблицах, для нее таблица Документа ничем не отличается от таблицы Справочника, кроме состава полей.
Но на уровне информационной базы 1С существует совсем иной набор объектов: Справочники, Документы, Регистры сведений и накопления и т.д. и т.п. При этом они связаны определенной внутренней логикой. Так элементы справочника могут иметь иерархическую структуру, являться подчиненными для другого справочника, а документы быть основанием для других документов, формировать проводки, записи регистров и т.д. и т.п. В процессе работы данная логика может быть нарушена, как по причине ошибок в программе, так и в результате некоторых действий пользователя.
Давайте рассмотрим следующую схему, отражающую некоторый набор бизнес-логики. У нас есть два документа: Реализация и Оплата, которые делают движения по некоторым регистрам. Так при реализации мы списываем нужное количество товара со склада и вносим в регистр взаиморасчетов задолженность покупателя. В момент оплаты мы вносим полученную сумму в регистр денежных средств и закрываем задолженность покупателя по отгрузке полностью или частично. Но как мы определим, какую именно задолженность погасил клиент? А для этого мы введем в документе оплата обязательное поле Основание, в котором будем указывать нужную реализацию.


Поэтому во всех подобных случаях, когда отчеты показывают неверные результаты или не сходятся друг с другом, следует запускать проверку логической целостности. Но не следует ожидать от нее какого-либо чуда, потому что она способна исправить только некоторые, самые очевидные ошибки (проводка без регистратора, неверный родитель элемента справочника и т.д.), в остальных случаях потребуется анализ ситуации и ручное исправление обнаруженных проблем. При этом нарушение логической целостности очень часто бывает связано с нарушением ссылочной целостности, о которой мы поговорим ниже.
Как часто нужно запускать эту проверку? Время от времени, особенно в файловых базах с обязательным изучением лога проверки, чем раньше вы узнаете о возможных проблемах в структуре данных и устраните их — тем лучше.
Проверка ссылочной целостности информационной базы
Данная проверка является одним из вариантов проверки логической целостности базы и выявляет проблемы со ссылками на отсутствующие элементы базы данных. Как мы уже говорили, база — это набор связанных между собой таблиц. В одной из них мы можем хранить контрагентов, в другой номенклатуру, в третьей — список складов, а внося запись о реализации товаров просто делаем ссылки на нужные элементы других таблиц. Например, наш условный документ Реализация может ссылаться на справочники Контрагенты, Номенклатура, Склады, выбирая и указывая в документе соответствующие значения мы не создаем дополнительных записей в таблице реализации, а даем ссылки на элементы связанных таблиц.

Но что будет, если используемый объект все-таки удалить? Возникнет битая ссылка. Внешне она выглядит как запись со ссылкой на уникальный идентификатор отсутствующего объекта:
<Объект не найден> (95:bc09ecd68a04705d11eb44а671518376)
Если подходить с чисто теоретических позиций, то битых ссылок в информационной базе быть не должно. Но не все так просто, если в базе используется РИБ, либо иные технологии обмена с другими базами и внешними источниками, то ряд второстепенных реквизитов может не передаваться. Например, сведения о подключаемом оборудовании.
В данном случае это нормально (Конфигурация Розница 2.3), так как конкретный экземпляр оборудования подключен именно к конкретному рабочему месту и передавать эти данные куда-то еще лишено особого смысла.
Но чаще к этой ошибке приводит повреждение базы или некорректные действия пользователя, скажем, удалившего объекты без контроля ссылочной целостности. В любом случае появление битых ссылок — это серьезный симптом и повод для отдельного разбирательства, рубить с плеча здесь неуместно. Поэтому сами разработчики при активации этой проверки переводят ее в безопасный режим, устанавливая действие Не изменять, по сути проверка лишь проинформирует вас о наличии битых ссылок, не более.
А вот после, установив сам факт их наличия следует думать. В ряде случаев, если выявленные ссылки являются второстепенными объектами подчиненных баз не нужно делать ничего. Наоборот, любая попытка «исправления» может привести к нарушению нормальной работы информационной базы. А вот в других надо предпринимать какие-либо действия.
Давайте посмотрим какие варианты у нас есть. Начнём со ссылок на несуществующие объекты. Здесь все довольно просто, мы можем или очистить ссылку, или создать новый объект нужного типа. Допустим, если запись справочника Номенклатура оказалась повреждена, но мы точно знаем по бумажным документам, что именно реализовывали, то ставим Создавать объекты, после чего переходим к ним и заполняем нужные реквизиты. Если же это какой-то второстепенный реквизит, то можем просто очистить ссылки. Второй вариант довольно часто применяется в тех случаях, когда надо быстро почистить базу и ряд объектов удаляется без контроля ссылочной целостности.
Теперь о частичной потере данных объектов. К ним могут относиться элемент подчиненного справочника без владельца или движение без регистратора. Мы можем либо удалить такие объекты, либо создать связанные с ними. Чаще всего такие объекты имеет смысл удалять, особенно если это движения, хотя если это элемент справочника, владелец которого потерян, то в ряде случаев имеет смысл создать владельца.
Внимание! Перед любыми исправлениями ссылочной целостности в базе обязательно создайте резервную копию. Помните, что данная операция необратима и может привести к полной или частичной потере данных!
Но даже имея показания к исправлению некоторых битых ссылок следует иметь ввиду, что данный инструмент применит выбранное действие ко всем ссылкам без разбора, что во многих случаях неприемлемо. Поэтому чаще всего проблему битых ссылок следует решать иными путями, один из них — выгрузка необходимых справочников или документов в XML из резервной копии и загрузка их в целевую базу в тех случаях когда просто восстановиться из бекапа невозможно.
Когда следует запускать эту проверку? В тех случаях, когда в базе не обнаружены битые ссылки или когда проверка логической целостности выявляет ошибки. Но в любом случае первый запуск следует производить с действиями Не изменять, а последующие решения принимать на основании глубокого анализа ситуации.
Пересчет итогов
В составе конфигурации 1С имеются специальные объекты — регистры, которые предназначены для хранения записей в разрезе определенных измерений. Например, регистр сведения Цены хранит сведения о ценах в разрезе измерений Номенклатура и Дата, а регистр накопления Товары хранит сведения об остатках товаров в разрезе Номенклатуры, Вида движения (расход или приход), Количества и Даты.
Начем с более простого, регистров сведения, допустим мы хотим получить действующие цены на определенную дату. Но если мы просто получим записи за этот день, то увидим, что цены менялись только для некоторых позиций номенклатуры, чтобы получить полный набор цен нам надо прочитать записи регистра на неопределенное количество дней назад, пока мы не получим последние цены для каждой позиции номенклатуры. Чтобы этого не делать регистр сведений имеет специальную виртуальную таблицу — СрезПоследних, которая содержит последние актуальные цены на каждый день. Теперь нам достаточно один раз обратиться к этой таблице, чтобы получить нужные сведения на интересующую дату.
Немного сложнее с регистрами накопления, записи в них содержат только сведения о движениях, скажем, такого-то числа в такое-то время на склад пришло 10 позиций некоторой номенклатуры, затем тем же днем продали 1 шт, потом 3 шт, за ней снова 5 шт и после еще 1 шт. При этом ряд вопросов, которые могут нас интересовать гораздо шире. Нас могут интересовать остатки на произвольный момент времени, либо обороты за некоторый период.
Чтобы не делать глубоких выборок по регистрам накоплений в них предусмотрены виртуальные таблицы Остатки, Обороты, Остатки и обороты. Каждая из них содержит актуальные данные за определенный период, в нашем случае день. И если таблица остатков в особом пояснении не нуждается, то таблица оборотов на нашей схеме может вызвать вопросы, так как ее содержимое полностью совпадает со значениями регистра. На самом деле редко когда регистр содержит единственную запись за день, чаще всего там множество записей движения: утром привезли на склад товар, потом его активно продавали, затем довезли еще немного и продолжили торговлю. При этом таблица обороты отобразит общий оборот за период.
Собственно, как и индексы, итоги предназначены для ускорения получения данных из информационной базы, теперь вам не нужно считывать весь или почти весь регистр, чтобы получить данные на определенный момент времени, достаточно обратиться к одной из виртуальных таблиц. Но со временем итоги тоже начинают терять эффективность: в них могут накапливаться мусорные записи, они могут фрагментироваться. Пересчет итогов решает эту проблему, заново создавая нужные виртуальные таблицы.
Пересчет итогов, как и реиндексация, простой и эффективный способ поддержания производительности информационной базы. Следует выполнять его регулярно в рамках обслуживания, а также каждый раз после исправления ошибок логической или ссылочной целостности.
Сжатие таблиц информационной базы
По мере работы информационной базы объем добавляемых в нее данных растет, вместе с ним растет и объем файла (файлов) базы данных. Но если мы удалим из базы часть информации, то объем файла базы данных не уменьшится, просто некоторые страницы будут помечены как пустые и снова доступные для записи. Если мы хотим уменьшить физически занимаемый объем, то следует произвести операцию сжатия таблиц информационной базы. В этом случае база переместит текущие данные на место освободившихся страниц, а затем уменьшит файл базы данных на объем освободившегося пространства.

Когда следует выполнять данное действие? Только если вы удалили из базы значительный объем данных, ну или если размер файла базы для вас критичен.
Реструктуризация таблиц информационной базы
Если реиндексация только перестраивала индексы, то реструктуризация полностью перестраивает содержимое базы данных, для каждой таблицы создается копия и записывается в отдельное место на диске, затем вся база данных полностью замещается копией. В чем смысл этого действия? Фактически мы произвели дефрагментацию базы данных, если ранее данные таблицы могли быть разбросаны по диску, то теперь они будут расположены последовательно.
Есть ли в этом практический смысл? В общем и целом, нет, чтение из страниц таблицы носит преимущественно случайный характер, последовательно считывание все таблицы — это уже ошибка построителя запросов. Но реструктуризация все-таки имеет смысл, скажем если вы добавили в базу собственный набор реквизитов или обновили релиз конфигурации (в этом случае реструктуризация будет выполнена автоматически). В любом случае лучше, чтобы связанные данные лежали рядом. Но следует понимать, что в процессе реструктуризации придется переместить весь объем информационной базы, а это может занять весьма продолжительное время.
И как раз-таки после реструктуризации будет уместно выполнить сжатие. Так как данные перемещать уже не надо, а пустое пространство уже сосредоточено в одном месте.
Как часто следует запускать? По необходимости, в том случае если вы изменили набор метаданных.
Пересоздание автономной конфигурации
Достаточно специфическая функция и относится к мобильному клиенту с возможностью автономной работы. Потеряв связь с основной базой данных такой клиент переключается в автономный режим и начинает использовать автономную конфигурацию. Если в данном режиме автономный клиент ведет себя неадекватно, то автономную конфигурацию следует пересоздать.
Проверка логической целостности расширений конфигурации
Не так давно фирма 1С добавила еще один способ доработки конфигураций — расширения. Основное их преимущество, что нет необходимости править код основной конфигурации или снимать ее с поддержки. Но современные расширения позволяют добавлять в базу новые реквизиты, справочники, документы, регистры и организовывать логические связи между ними.
Данная проверка аналогична проверке логической целостности, только с учетом подключенных расширений, которых может быть и не одно. Если вы используете расширения, то обязательно включайте данную проверку вместе с проверкой логической целостности.
Заключение
По умолчанию фирма 1С предлагает достаточно сбалансированный набор действий: реиндексация и пересчет итогов благотворно влияют на производительность, а проверка логической целостности позволяет на ранних этапах выявить возможные проблемы. Если вы используете расширения, то добавьте туда проверку логической целостности расширений.
Остальные проверки и действия следует выполнять только при наличии необходимости. Так обнаружив ошибки в логической целостности следует выполнить проверку ссылочной целостности, а существенно изменив структуру базы данных имеет смысл выполнить реструктуризацию.
Надеемся, что данный материал окажется вам полезен, а также поможет по-новому взглянуть и глубже понять привычные действия.
Научиться настраивать MikroTik с нуля или систематизировать уже имеющиеся знания можно на углубленном курсе по администрированию MikroTik. Автор курса, сертифицированный тренер MikroTik Дмитрий Скоромнов, лично проверяет лабораторные работы и контролирует прогресс каждого своего студента. В три раза больше информации, чем в вендорской программе MTCNA, более 20 часов практики и доступ навсегда.
Содержание
1. Спасти рядовую пятницу
2. Конфигуратор, у нас проблемы
3. Что делать, шеф?
4. Второй шаг – круг задач
5. Попытки решения
5.1. Попытка 1 — тестирование-исправление ИБ через директиву командной строк
5.2. Попытка 2 — копирование таблиц Config, Configsave, Params и DBSchema из работоспособной копии ИБ средствами MS SQL
5.3. Попытка 3 — очистка таблицы configsave средствами MS SQL
5.4. Попытка 4 — копирование разрушенных таблиц из работоспособной копии ИБ средствами MS SQL
6. Решение, которое помогло:
6.1. Подключение
6.2. Перенос справочников 1С
6.3. Перенос документов 1С
6.4. Проведение документов 1С
7. Выводы. Как избежать поломки структуры данных базы 1С
Спасти рядовую пятницу
Пятница – это не только друг Робинзона. Это – почти выходной. Должен же быть в рабочем календаре день, когда можно подумать о горячих выходных и прохладных напитках. Как минимум, стоит остерегаться резких движений, чтобы сберечь настоящие выходные для команды и пользователей. Во многих софтверных компаниях считается дурным тоном выпускать новые релизы в конце недели. К примеру, Apple «катит» по вторникам. В Яндексе запрещено «катить» по пятницам и перед Новым Годом.
Но однажды пятнице не повезло. Когда сроки горят, а дедлайны давят, всегда найдется чему пойти не так. И дальнейшее развитие событий сильно зависит от компетенций вашей uber-команды.
Конфигуратор, у нас проблемы
Это было обычное обновление конфигурации. Структура данных не поменялась, бизнес-логика тоже. По «большой просьбе» одного из пользователей, решили обновить рабочую базу.
Одно «но»: технологическое окно уже закончилось и основные изменения конфигурации уже были запущены в работу.
«Надо!» – пользователь просит. Что ж, надо – значит надо: программист привычно нажал F7 и согласился с предложением Конфигуратора «обновить динамически».
Через пару часов стало понятно, что база умерла: попытка войти в режиме «1С:Предприятие» отправляло систему «в дамп» после исполнения нескольких строк модуля приложения. Конфигуратор открывался и работал, но попытки внести изменения также приводили к краху без подробностей в журнале регистрации и технологическом журнале.
В результате база перестала открываться. Совсем.
Попытки войти в «1С:Предприятие» не пускали пользователей дальше окна авторизации. Конфигуратор открывался и работал, но без толку для решения задачи: попытки обновить информационную базу и/или выполнить восстановление также приводили к краху.
Анализ СУБД показал, что динамическое обновление разрушило структуру таблиц. Теперь-то каждый разработчик в команде знает, что «динамические обновления» – это зло. И все знают неформальное название – «демоническое обновление». Да, с этого момента динамические обновления в компании для рабочей базы запрещены. Но «фарш невозможно провернуть назад» и утерянная база не запускается.
Утеряно несколько сотен документов реализации, с сотнями товарных позиций.
Что делать, шеф?
Сначала мы оценили, чем в итоге располагаем.
Не так много, но не безнадежно:
* Слепок информационной базы, который делается регулярно по расписанию каждые два часа. Нам «повезло» и восстанавливать потребуется 1 час 53 минуты работы пользователей.
* База, работоспособна на уровне СУБД.
* На уровне COM-соединения работоспособность также сохранилась,
* Проектная команда собралась из разных проектов. (Да, специалист в фирме франчайзи – это универсальный боец и швец, и тд. Пожалуй, это главное, что помогло решить вопрос оперативно).
С этим понятно. Какие потери?
* Примерно два часа работы нескольких сотен пользователей. В основном – документы реализации и заказы покупателей.
* Да, можно восстановить данные по первичке. Но! Представьте себе пару часов работы сотен пользователей в разгар рабочего дня.
* На календаре конец первого квартала – это годовая бухгалтерская, налоговая отчетность. Плюс квартальная отчетность перед партнерами.
* Плюс скоро начислять зарплату и вознаграждения по договорам.
Второй шаг – круг задач
Оценив масштаб последствий, стало очевидно, что ситуация исправима. Как выяснилось позже – это оптимистичный вывод, но оптимистам везет.
Итак, утерян относительно небольшой по времени период – чуть меньше двух часов работы пользователей.
Это сотни документов с табличными частями до сотен строк. Несколько сотен элементов справочников.
И надо было определить, какие из потерь критичны для результата, а что можно пропустить и решить в дальнейшем другими путями.
Например, очень непросто восстанавливать записи (а тем более, итоги) регистров бухгалтерии по двум причинам. Первая – это низкая производительность по сравнению с другими структурами данных. Вторая – сложность организации как в СУБД, так и на уровне объектной модели «1С:Предприятие».
Но так как за эти два часа ручные корректировки регистров бухгалтерии не выполнялись, то можно записи отдельно от документов не восстанавливать. Все движения, подчиненные регистраторам, можно восстановить перепроведением восстановленных документов.
Попытки решения
Попытка 1. Тестирование-исправление ИБ через директиву командной строки Конфигуратора. Примерно так:
1cv8.exe config /IBCheckAndRepair -Rebuild
А также тестирование/исправление СУБД
dbcc checkdb(‘<db_name>’, REPAIR_ALLOW_DATA_LOSS )
Конфигуратор стартовал, висел в списке процессов некоторое время и дальше падал без объяснения причин. Технологический журнал при этом с упорством Капитана Очевидность сообщал о разрушении таблицы с планом обмена.
Попытка 2. Копирование таблиц Config, Configsave, Params и DBSchema из работоспособной копии ИБ средствами MS SQL
ins ert into [base2009].[Dbo].[Config] sel ect * from [BaseCopy].[Dbo].[Config]
Это помогло – удалось пройти дальше окна авторизации. Но модуль приложения все равно падал с исключением при попытке опросить планы обмена. Отключить проверку в этом куске кода не удалось, так как попытки сохранить конфигурацию приводили к падению Конфигуратора на этапе анализа структуры ИБ
То есть мы продвинулись дальше, но какая разница – перепрыгнул ты пропасть на четверть или наполовину? Как говорится, go fish.
Попытка 3. Очистка таблицы configsave средствами MS SQL
Примерно так:
DR OP TABLE [ConfigSave]
CRE ATE TABLE [ConfigSave]( [FileName] [nvarchar](128) NOT NULL, [Creation] [datetime] NOT NULL, [Modified] [datetime] NOT NULL, [Attributes] [smallint] NOT NULL, [DataSize] [int] NOT NULL, [BinaryData] [image] NOT NULL)
INS ERT IN TO ConfigSave SELECT * FR OM Config
Тоже помогло, Но с тем же эффектом, что и предыдущая попытка.
Попытка 4. Копирование разрушенных таблиц из работоспособной копии ИБ средствами MS SQL
Оценив количество модифицированных таблиц, мы прикинули сколько SELE CT’ов придется написать… и решили, что несколько дней без базы компания не выдержит
Решение, которое помогло
1. Была восстановлена база из «слепка», снятого за два часа до разрушения таблиц.
2. Дальше немного магии: код на 1С, который помог восстановить данные, накопленные пользователями со времени снятия крайнего слепка.
3. Так как платформа в режиме COM-соединения гораздо легче и более прозрачно оперирует с СУБД, то система успешно стартует и дает выполнить запросы.
Итак, код решения, которое сработало:
Подключение
1. Из копии подключаемся к разрушенной рабочей базе через COM-соединение. В отличие от обычного подключения, сеанс стартует успешно

Перенос справочников 1С
2. Загружаем те справочники, которых еще нет в копии

Перенос документов 1С
3. Загружаем те документы, которых еще нет в копии

Проведение документов 1С
4. Чтобы восстановить движения, перепроводим проведенные документы, загруженные в копию
Выводы. Как избежать поломки структуры данных базы 1С?
Удалось ли нам спасти ту пятницу? И да, и нет.
У пользователей случился «сокращенный рабочий день» и тут, пожалуй, да – отдыхать полезно.
Большинство проектной команды получило свою порцию адреналина. И ушли с работы тоже вовремя.
Руководитель проекта и эксперт по технологическим вопросам крупных внедрений получили много интересной и поучительной работы на выходные 
Успеху в этой ситуации способствовали регулярные бэкапы и быстрая диагностика. Бэкап облегчил восстановление. А диагностика «в восемь рук» позволила быстро найти тот вариант, который сработал. То ,что он оказался пятым по счету и его не было на Инфостарте, в школе или на форумах только подтверждает ценность проектного опыта команды.
Что делать в будущем во избежание подобных ситуаций? Всю округу соломкой не устелить, но вот чек-лист для критичных ситуаций.
• Диагностика
o Команде знать и уметь работать с:
— техническим журналом;
— подсистемой «Инструменты разработчика» (Сергей, привет и огромное спасибо за твой многолетний труд);
— Sql + инструменты администрирования субд;
— инструментом диагностики операционной системы и оборудования.
o Держать под рукой эти инструменты, желательно в виде коротких пошаговых инструкций и скриптов
• Лечение
— Будьте экспертом. Неважно в данном случае, лучший ты сыщик с дипломом или без диплома. Важен экспертный подход к решению задач
o Мыслить гибко
•Профилактика
— Бэкапы — регулярно
— Каждому знать, где лежат и как часто делаются
— Админам написать инструкции (а лучше скрипты) для оперативного восстановления.
Если Вы дочитали эту статью до конца, можно с уверенностью пожелать спокойных выходных!
Анатолий Бурнашев,
руководитель отдела внедрения ООО “Кодерлайн”
1. Общие свойства поля.
Общие свойства поля задаются на закладке Общие для каждого поля и зависят от выбранного типа данных.
Наиболее важные свойства полей:
• размер поля задает максимальный размер данных, сохраняе¬мых в поле. Рекомендуется задавать минимально допустимый размер поля, так как сохранение таких полей требует меньше памяти, и обработка выполняется быстрее;
• формат поля является форматом отображения заданного типа данных и задает правила представления данных при выводе их на экран или печать. Конкретный формат выбирается в рас¬крывающемся списке значений свойства Формат поля. Число десятичных знаков задает для числового и денежного типов данных число знаков после запятой. Можно задать число от О до 15;
• подпись поля задает текст, который выводится в таблицах, формах, отчетах;
• условие на значение позволяет осуществлять контроль ввода, задает ограничения на вводимые значения, при нарушении условий запрещает ввод и выводит текст, заданный свойством Сообщение об ошибке;
• сообщение об ошибке задает текст сообщения, выводимый на экран при нарушении ограничений, заданных свойством Ус¬ловие на значение.
Тип элемента управления — свойство, которое задается на за¬кладке Подстановка в окне конструктора таблиц. Это свойство оп¬ределяет, будет ли отображаться поле в таблице и в какой форме — в виде поля, списка или поля со списком. Таким образом, опреде¬ляется вид элемента управления, используемого по умолчанию для отображения поля.
Если для поля выбран тип элемента управления Список или Поле со списком, то на закладке Подстановка появляются дополни¬тельные свойства, которые определяют источник данных для строк списка и ряд других характеристик списка.
Если при определении типа поля был выбран Мастер подстано¬вок, то значения свойств на закладке Подстановка будут заполнены мастером.
2. Определение первичного ключа
Уникальный (первичный) ключ таблицы может быть простым или составным, включающим несколько полей. Для определения ключа выделяются поля, составляющие ключ, и на панели инстру¬ментов нажимается кнопка ключевое поле или выполняется команда Правка|ключевое поле.
3. Сохранение таблицы
После определения структуры таблицы ее надо сохранить. Для этого используется команда Файл|сохранить или кнопка панели ин¬струментов Сохранить. В окне Сохранение вводится имя таблицы.
После сохранения таблицы делается доступным режим таблицы, позволяющий перейти ко второму этапу создания таблицы — созда¬нию записей. Переход осуществляется нажатием кнопки Представ¬ление таблицы на панели инструментов таблиц.
В режиме таблицы можно вводить в таблицу новые записи, заполняя ее поля.
При заполнении таблиц со связями и вводе записей в подчи¬ненную таблицу необходимо отслеживать наличие записей с вводи¬мыми значениями ключевых полей в главной таблице.
После ввода значения в ячейку поля и попытке перейти к другой ячейке Access проверяет, являются ли введенные данные допустимыми для этого поля.
Если значение не является допустимым и его преобразование невозможно, то появляется предупреждающее сообщение. Для тог чтобы выйти из ячейки, следует ввести правильное значение.
4. Схема данных в Access
Структура реляционной базы данных в Access задается схемой данных, которая имеет иерархическую структуру и называется кано¬нической реляционной моделью предметной области.
Схема данных графически отображается в отдельном окне, где таблицы представлены списками полей, а связи — линиями между полями разных таблиц.
При построении схемы данных Access автоматически определя¬ет по выбранному полю тип связи между таблицами. Если поле, по которому нужно установить связь, является уникальным ключом как в главной таблице, так и в подчиненной, Access устанавливает связь «один к одному». Если поле связи является уникальным клю¬чом в главной таблице, а в подчиненной таблице является не клю¬чевым или входит в составной ключ, Access устанавливает связь «один ко многим» от главной таблицы к подчиненной.
Кроме указанных типов связей в Access существуют связи-объе¬динения, обеспечивающие объединение записей таблиц не по клю¬чевому полю, а в следующих случаях:
• связываемые записи в обеих таблицах совпадают (связи уста¬навливаются по умолчанию);
• для всех записей первой таблицы, для которых отсутствуют связи со второй, устанавливают связи с пустой записью вто¬рой таблицы;
• для всех записей второй таблицы, для которых отсутствуют связи с первой, устанавливают связи с пустой записью первой таблицы.
5. Обеспечение целостности данных
При создании схемы данных пользователь включает в нее таб¬лицы и устанавливает связи между таблицами. Причем для связей типа «один к одному» и «один ко многим» можно задать параметр обеспечение целостности данных, а также автоматическое каскад¬ное обновление и удаление связанных записей.
Обеспечение целостности данных означает выполнение для взаимосвязанных таблиц следующих условий корректировки базы данных:
• в подчиненную таблицу не может быть добавлена запись, для которой не существует в главной таблице ключа связи;
« в главной таблице нельзя удалить запись, если не удалены связанные с ней записи в подчиненной таблице;
• значения ключа связи главной таблицы должно приводить к изменению соответствующих значений в записях подчинен¬ной таблицы,
В случае если пользователь нарушил эти условия в операциях обновления или удаления данных в связанных таблицах, Access вы¬водит соответствующее сообщение и не допускает выполнения опе¬рации.
Access автоматически отслеживает целостность данных, если между таблицами в схеме данных установлена связь с параметрами обеспечения целостности. При вводе некорректных данных в свя¬занные таблицы выводится сообщение. Access не позволяет созда¬вать связи с параметрами целостности в схеме данных, если ранее введенные в таблицы данные не отвечают требованиям целостности.
Отметим, что установление между двумя таблицами связи типа «один к одному» или «один ко многим» и задание параметров цело¬стности данных возможно только при следующих условиях:
• связываемые поля имеют одинаковый тип данных, причем имена полей могут быть различными;
• обе таблицы сохраняются в одной базе данных Access;
• главная таблица связывается с подчиненной по первичному простому или составному ключу (уникальному индексу) глав¬ной таблицы.
Если для выбранной связи обеспечивается поддержание целост¬ности, то можно задать режим каскадного обновления и удаления связанных записей. В режиме каскадного обновления связанных за¬писей при изменении значения в поле связи главной таблицы Access автоматически изменит значения в соответствующем поле в подчиненных записях.
В режиме каскадного удаления связанных записей при удалении записи из главной таблицы Access выполняет каскадное удаление подчиненных записей на всех уровнях.
6. Первоначальное создание схемы данных
Для выделения нескольких таб¬лиц надо, держа нажатой клавишу <Ctrl>, щелкнуть на всех таблицах, переносимых в схему. После включения всех нужных таблиц в схему данных надо нажать кнопку Закрыть.
В результате в окне Схе¬ма данных будут представлены все включенные таблицы со списком своих полей. Для установления связи между парой таблиц в схеме данных надо выделить в главной таблице уникальное ключевое поле, по которому устанавливается связь.
Далее при нажатой кноп¬ке мыши протащить курсор в соответствующее поле подчиненной таблицы. При установлении связи по соответствующему ключу не¬обходимо выделить все поля, входящие в ключ главной таблицы, и перетащить их на одно из полей связи в подчиненной таблице.
Для выделения всех полей, входящих в составной уникальный ключ, не¬обходимо отмечать поля при нажатой клавише <Ctrl>. При установ¬лении связи откроется окно Связи.
При этом в строке Тип отноше¬ния автоматически устанавливается тип «один ко многим».
При составном ключе связи в окне Связи необходимо для каж¬дого ключевого поля главной таблицы — Таблица|3апрос — выбрать соответствующее поле подчиненной таблицы, названной Связанная таблица|3апрос.
В этом же окне можно задать параметр Обеспечение целостности данных для выбранной связи. Если же таблицы уже содержат дан¬ные, не отвечающие требованиям целостности, то связь «один ко многим» не устанавливается и появляется соответствующее сообще¬ние с соответствующей инструкцией.
После задания параметра це¬лостности можно в окне Связи отметить каскадное обновление свя¬занных полей или каскадное удаление связанных записей.
После установления связей между таблицами получим схему дан¬ных в окне Схема данных. Перемещения и изменения размеров таб¬лиц осуществляется принятыми в операционной системе Windows способами.
Проверка работоспособности схемы данных осуществляется при конструировании форм, запросов, отчетов и при их использовании, а также при непосредственной работе с таблицами.
7. Модификация структуры базы данных
К модификации структуры базы данных относятся изменения структуры отдельных таблиц и схемы данных. Все изменения струк¬туры таблиц производятся в режиме конструктора таблиц.
Изменения полей, которые не являются ключами или полями связи
Состав и последовательность, а также тип данных, свойства или имена этих полей можно изменять независимо от наличия связей таблицы с другими таблицами базы данных. Однако, если преобра¬зования недопустимы, попытка изменения типа данных может при¬вести к потере данных.
Изменение или удаление ключевого поля
Для загруженной и несвязанной таблицы при попытке изменить свойства или удалить ключевое поле система предупреждает о воз¬можности потери данных при удалении ключа. Если, например, удаляется поле в составном ключе, с других полей этого ключа бу¬дет снят признак ключа.
Для отказа от определения первичного ключа в таблице доста¬точно нажать кнопку ключевое поле или удалить индекс ключа в окне Индексы после нажатия соответствующей кнопки на панели инструментов. Если требуется изменить ключ таблицы, которая имеет связи с другими таблицами, то необходимо предварительно разорвать связи.
Изменения, сделанные в структуре таблиц, автома¬тически не переносятся системой в использующие их формы, за¬просы и отчеты.
Изменение схемы данных
При модификации схемы данных осуществляется изменение со¬става ее таблиц — удаление, добавление таблиц и изменение связей.
Необходимость в изменении связей возникает, в частности, при изменении ключей в таблицах. Изменение ключа по составу, по типу и размеру его полей не может производиться до тех пор, пока не удалены связи таблицы в схеме данных.
При изменении типа данных для не ключевых полей, но задей¬ствованных в связях таблицы, также предварительно необходимо удалить эти связи в схеме данных.
Для внесения изменений в схему данных необходимо закрыть все таблицы и выполнить команду Сервис|Схема данных или нажать кнопку Схема данных на панели инструментов.
Добавление таблиц выполняется при нажатии кнопки Добавить таблицу. В окне Добавление таблицы надо выделить нужную таблицу и нажать кнопку Добавить.
Удаление таблицы из схемы данных осуществляется переходом в окно Схема данных. В этом окне надо удалить связи таблицы и при отмеченной таблице выполнить команду Правка|Удалить или нажать клавишу <Del>.
Удаление связи осуществляется, если связь отметить щелчком мыши, нажать правую кнопку мыши, вызывающую контекстное меню, а затем выполнить команду Удалить связь. Помеченную связь можно также удалить по команде Правка|Удалить или клавишей
Изменение параметров связи выполняется командой Связи|Изменить связь или соответствующей командой контекстного меню.



















