Поделиться через


С миру по нитке... (ru-RU)

В этой статье я рассмотрю несколько разных полезностей.

Логирование выполнения хранимых процедур
Для решения задачи логирования выполнения хранимых процедур следуйте следующему алгоритму.
Шаг 1: Создайте таблицу, в которую вы будете вести логирование

USE <DBName> -- This is the database where the SP and table will reside
GO
CREATE TABLE [dbo].[tbl_errlog] 
( [Logdate] [datetime] NULL,
[ProcessInfo] [varchar](100) NULL,
[Text] [varchar](1000) NULL
) ON [PRIMARY]
GO

Шаг 2: Создайте хранимую процедуру в <DBName>

create proc sp_logger
as
 begin
  declare @counter int
  select @counter = COUNT(*) from dbo.tbl_errlog
  if @counter = 0
begin
INSERT tbl_errlog
EXEC sp_readerrorlog 
end
else
begin
CREATE TABLE #tmp_errlog
( Logdate datetime,
ProcessInfo varchar (100),
Text varchar (1000) 
) 
INSERT #tmp_errlog 
EXEC sp_readerrorlog 
declare @lastlog datetime
select @lastlog= MAX(Logdate) from tbl_errlog 
INSERT INTO tbl_errlog (Logdate, ProcessInfo,Text) 
SELECT Logdate, ProcessInfo,Text 
FROM #tmp_errlog 
WHERE Logdate > @lastlog 
drop table #tmp_errlog 
end
end

Шаг 3: Запустите выполнение хранимой процедуры как задание по расписанию. Однако ваша целевая таблица tbl_errlog может быстро вырасти, поэтому подумайте о стратегии архивирования и поддержки.

В какой строке произошла ошибка?
Иногда вы можете получить ошибку вида

Msg 50000, Level 16, State 1, Procedure TestToSeeWhatTheErrorLineNumberRefersTo, Line 14
Some Error

Сразу же возникает вопрос, а где именно произошла ошибка?
Давайте промоделируем эту ситуацию. Выполните код.

go
create procedure TestToSeeWhatTheErrorLineNumberRefersTo 
as
raiserror ('Some Error',16,1) 
go 
exec TestToSeeWhatTheErrorLineNumberRefersTo

тут всё нормально, но ситуация будет интересной интересней, если вы используете динамический SQL. Измените хранимую процедуру следующим образом

alter procedure TestToSeeWhatTheErrorLineNumberRefersTo 
as
execute ('raiserror (''Some Error'',16,1)') 
go

Вы получите сообщение об ошибке.

Msg 50000, Level 16, State 1, Line 1
Some Error

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

alter procedure TestToSeeWhatTheErrorLineNumberRefersTo 
@debug int =0 
as
declare @sql nvarchar(1000) 
set @sql = '/* some comment or other code 
more comments 
*/ 
raiserror (''Some Error'',16,1)'
if @debug > 0 
print @sql 
exec sp_executesql @sql

Это позволяет получить инструкцию SQL, которая была выполнена и выяснить, какая строка относится к ошибке.

Быстрый способ получить количество строк в таблице
Вы конечно знаете как узнать сколько строк в таблице. Это вызов команды

SELECT COUNT(*) FROM TABLE

Но эта команда может выполняться очень долго
Начиная с версии SQL 2005 года системный каталог поддерживает (хранит) количество строк для таблицы и в отличие от в предыдущих версий поддерживает это значение в режиме реального времен. Таким образом мы получаем интересный подход к решению задачи.

select top 1 rows
from sys.partitions
whereobject_id =object_id(@tablename)

Для секционированной таблицы следует применить следующий код

select sum(rows) as row_count
from sys.partitions
where index_id in (0,1)
and object_id =object_id(@table)

Но этот способ с обращением к системному каталогу не транзакционен. Давайте рассмотрим пример. Выполните код.

create table fred (col1 int)
go
begin transaction
set nocount on
declare @i int = 0
while @i < 100000
begin
insert into fred values (@i)
set @i = @i + 1
end

А теперь запустите в другом процессе

select * from sys.partitions 
where object_id = object_id ('fred')

Вы увидите, что есть 100000 строк. Но транзакция в первом процессе ещё не завершена

Вы готовы к SQL Server 2012?
SQL Server 2012 уже вышел. Я хочу остановиться на том функционале, который отменён в SQL Server 2012.
DATABASEPROPERTY()
Вы теперь должны использовать DATABASEPROPERTYEX(), или ещё лучше, получать информацию о вашей базе данных из представления системного каталога sys.databases
80 (SQL Server 2000) compatibility
Вы не сможете теперь использовать режим совместимости SQL Server 2000 (80). Это также означает что вы не сможете напрямую подключить или восстановить базу данных в формате SQL Server 2000. Вам нужно будет сперва присоединить или восстановить базу в SQL Server 2005/2008/2008R2. Можно даже использовать пробные версии.
osql
используйте sqlcmd или PowerShell. Нет причин использовать антикварное утилиту командной строки
SQLMail
Вы теперь должны использовать только Database Mail.
sqlmaint.exe
Больше вы не сможете управлять планами обслуживания с помощью этой утилиты командной строки.
SQL-DMO
используйте SMO.
SET FMTONLY
В настоящее время код (и возможно драйверы, которые вы используете для подключения к SQL Server) могут использовать SET FMTONLY ON для инспектирования результирующего набора команды. Эта функция в настоящее время заменена. Следует проверить ваш код, особенно если вы используете старые поставщиков для подключения к SQL Server.
SET ROWCOUNT для DML
Если вы в настоящее время опираетесь на использование SET ROWCOUNT для ограничения количества строк, затронутых командой UPDATE или DELETE, вы можете захотеть использовать вместо этого TOP.
DBCC LOGINFO
Могут возникнуть проблемы при использовании этой команды. Например, в таком варианте

CREATE TABLE #LI
(
 [FileId] INT,
 [FileSize] BIGINT,
 [StartOffset] BIGINT,
 [FSeqNo] INT,
 [Status] INT,
 [Parity] INT,
 [CreateLSN] DECIMAL(25, 0)
);
 
INSERT #LI EXEC('DBCC LOGINFO(''msdb'');');

Этот код будет работать в SQL Server 2008 R2 и ниже, но не будет работать в SQL Server 2012.

Msg 213, Level 16, State 7, Line 1
Column name or number of supplied values does not match table definition

Это получается из-за того, что добавлен ещё один столбец в данных, возвращаемых этой командой. Что бы исправить ситуацию нужно внести изменения

CREATE TABLE #LI
(
[RecoveryUnitId] INT,
[FileId] INT,
[FileSize] BIGINT,
[StartOffset] BIGINT,
[FSeqNo] INT,
[Status] INT,
[Parity] INT,
[CreateLSN] DECIMAL(25, 0)
);

Правда теперь ваш код не будет универсальным и не сможет работать на всех версиях SQL Server.