Архив рубрики: SQL

Найди отличия в таблицах SQL

Понадобилось тут при переходе на новую систему сравнить данные справочников с архивной системой.

Для этих целей отлично пригодилась утилита Tablediff , главное требование чтобы таблицы имели одинаковую структуру и содержали первичный ключ либо столбец идентификатора  identity, rowguid или уникальный ключ.
Плюс этой утилиты в том что можно легко сравнивать таблицы с разных серверов и конечно же есть возможность привести таблицы к единому виду добавив всего лишь один параметр -f и получить необходимый скрипт .

Если таблицы не совпадают по структуре проще написать свой запрос для сравнения используя связанные серверы и команды INTESECT  и EXCEPT или создать представления и сравнить их. Читать

Дать права к представлению без права на таблицу.

В ситуации когда пользователь должен видеть только определенные данные из таблицы или не хочется заморачиваться с правами на таблицу
пригодится использование механизма цепочек владения.

Если кратко то  в случае когда  объектами владеет один и тот же  владелец то включается механизм цепочки владения  который позволяет исключить проверку прав для входящих в процедуру  или представление объектов.
Например хотим создать отдельную схему и дать доступ пользователю только на объекты данной схемы. Подробнее можно почитать тут
Создаем схему указываем владельца dbo, т.к будем использовать  в  представлениях и процедурах объекты данной схемы, таким образом включается цепочка наследования.
! Необходимо указать правильного владельца сразу иначе цепочка оборвется и придется пересоздавать схему.
CREATE SCHEMA [MDM] AUTHORIZATION [dbo]
Далее создаем представление, которое использует например  таблице TEST в схеме dbo
CREATE VIEW MDM.TESTVIEW AS
SELECT *
FROM dbo.TEST
Даем права нашему пользователю на селект и  грант, без гранта работать не будет.
GRANT SELECT , VIEW DEFINITION ON MDM.TESTVIEW TO [TestUser] WITH GRANT OPTION
Проверяем доступ  к представлению и убеждаемся что все работает

PS скриптом ниже можно проверить все права пользователя
EXECUTE AS LOGIN = N'TestUser';
GO
DECLARE @login NVARCHAR(256), @user NVARCHAR(256);
SELECT @login = login_name FROM sys.dm_exec_sessions WHERE session_id = @@SPID;
SELECT @user = d.name
  FROM sys.database_principals AS d
  INNER JOIN sys.server_principals AS s
  ON d.sid = s.sid
  WHERE s.name = @login;
SELECT u.name, r.name
  FROM sys.database_role_members AS m
  INNER JOIN sys.database_principals AS r
  ON m.role_principal_id = r.principal_id
  INNER JOIN sys.database_principals AS u
  ON u.principal_id = m.member_principal_id
  WHERE u.name = @user;
SELECT class_desc, major_id, permission_name, state_desc
  FROM sys.database_permissions
  WHERE grantee_principal_id = USER_ID(@user);
GO
REVERT;

Автор: Roman D
Дата публикации: 2016-03-04T06:42:00.001-08:00

Функция для подсчета количества рабочих часов

Бывает иногда полезной такая функция,  рассчитывает количество рабочих часов между датами
DECLARE @startdate datetime
DECLARE @enddate DATETIME
SET @startdate = '2015-04-28 12:33:19.000'
SET @enddate = '2015-04-30 09:45:20.000'
SELECT [dbo].[ufn_CalcWorkTime] (@startdate, @enddate) 
Результат расчета в часах
Текст самой функции
ALTER FUNCTION [dbo].[ufn_CalcWorkTime]
(
       @startdate     DATETIME,
       @enddate       DATETIME
)
RETURNS DECIMAL(10, 3)
AS
BEGIN
       —DECLARE @startdate datetime
       —DECLARE @enddate DATETIME
       DECLARE @ldate DATETIME,
               @rdate DATETIME,
               @ldaystartdate DATETIME,
               @rdaystartdate DATETIME,
               @StartWorkTime AS VARCHAR(8) = ' 9:30:00',
               @EndWorkTime AS VARCHAR(8) = ' 18:30:00',
               @DayzBetween AS INT
      
       DECLARE @result AS DECIMAL(10, 3)
       DECLARE @add_days       INT,
               @minus_days     INT
       —SET @startdate = '2015-12-18 16:07:54.000'
       —SET @enddate = '2015-12-22 12:21:56.000'
      
       SET @ldate = CAST(
               (CONVERT(VARCHAR(10), @startdate, 102) + @EndWorkTime) AS DATETIME
           )
      
       SET @ldate = CASE
                         WHEN @ldate > @startdate THEN @ldate
                         ELSE @startdate
                    END
      
       SET @ldaystartdate = CAST(
               (CONVERT(VARCHAR(10), @startdate, 102) + @StartWorkTime) AS DATETIME
           )
      
       SET @rdaystartdate = CASE
                                 WHEN @enddate > @rdaystartdate THEN @rdaystartdate
                                 ELSE @enddate
                            END
      
       SET @rdate = CAST(
               (CONVERT(VARCHAR(10), @enddate, 102) + @EndWorkTime) AS DATETIME
           )
      
       SET @rdate = CASE
                         WHEN @rdate < @enddate THEN @rdate
                         ELSE @enddate
                    END
      
       SET @rdaystartdate = CAST(
               (CONVERT(VARCHAR(10), @enddate, 102) + @StartWorkTime) AS DATETIME
           )
      
       SET @rdaystartdate = CASE
                                 WHEN @enddate > @rdaystartdate THEN @rdaystartdate
                                 ELSE @enddate
                            END
      
       SET @DayzBetween = (
               (
                   DATEDIFF(
                       dd,
                       CONVERT(VARCHAR(10), @startdate, 102),
                       CONVERT(VARCHAR(10), @enddate, 102)
                   )
               )
               -(DATEDIFF(wk, @StartDate, @EndDate) * 2)
               -(CASE WHEN DATEPART(DW, @StartDate) = 6 THEN 1 ELSE 0 END)
               -(CASE WHEN DATEPART(DW, @StartDate) = 7 THEN 1 ELSE 0 END)
           )
      
       —добавим рабочие праздники
       SELECT @add_days = COUNT(*)
       FROM   HolidaysAndWeekends (NOLOCK)
       WHERE  Дата BETWEEN @startdate AND @enddate
              AND [Рабочий День] = 1
      
      
       —вычтем выходные рабочие дни
       SELECT @minus_days = COUNT(*)
       FROM  HolidaysAndWeekends (NOLOCK)
     

Панель, она и в SQL панель!

Для тех кто давно задумывался об удобном инструменте анализа производительности своего SQL сервера, есть хорошая новость!
Кроме стандартных отчетов в SSMS,  оказывается уже есть на базе отчётов SSRS от MS готовый пакет отчетов под шикарным названием «панель» производительности.
Что удобно, для выполнения этих отчетов не нужны права супер одмина.
Если внимательно присмотреться то не всегда этот отчет выдает адекватные цифры, так что будьте готовы к этому. Как говорится доверяй, но проверяй.
Ссылка на отчеты  и горячее видео!

После установки отчеты можно найти здесь

Сама панель выглядит как то так.
Установка проста  на сервер устанавливаем необходимые процедуры из файла setup.sql в базу msdb
В файле найдете все необходимые скрипты которые потом можно использовать по отдельности при желании.
При желании разворачиваем все отчеты на сервере отчетов.
И настраиваем подписки.
В общем как всегда у MS реализация получилась не айс, идея была на поверхности давно, но до ума довести так и не получилось.
PS: Использование скриптов в анализе никто конечно не отменял

—задержки с IO (чтение,запись)
SELECT  DB_NAME(vfs.database_id) AS database_name ,
        vfs.database_id ,
        vfs.FILE_ID ,
        io_stall_read_ms / NULLIF(num_of_reads, 0) AS avg_read_latency ,
        io_stall_write_ms / NULLIF(num_of_writes, 0)
                                               AS avg_write_latency ,
        io_stall / NULLIF(num_of_reads + num_o
f_writes
, 0)
                                               AS avg_total_latency ,
        num_of_bytes_read / NULLIF(num_of_reads, 0)
                                               AS avg_bytes_per_read ,
        num_of_bytes_written / NULLIF(num_of_writes, 0)
                                               AS avg_bytes_per_write ,
        vfs.io_stall ,
        vfs.num_of_reads ,
        vfs.num_of_bytes_read ,
        vfs.io_stall_read_ms ,
        vfs.num_of_writes ,
        vfs.num_of_bytes_written ,
        vfs.io_stall_write_ms ,
        size_on_disk_bytes / 1024 / 1024. AS size_on_disk_mbytes ,
        physical_name
FROM    sys.dm_io_virtual_file_stats(NULL, NULL) AS vfs
        JOIN sys.master_files AS mf ON vfs.database_id = mf.database_id
                                       AND vfs.FILE_ID = mf.FILE_ID
ORDER BY avg_total_latency DESC
OPTION (RECOMPILE);
что в буферном < /span>пуле
SELECT count(*)*8/1024 AS 'Cached Size (MB)'
,CASE database_id
WHEN 32767 THEN 'ResourceDb'
ELSE db_name(database_id)
END AS 'Database'
FROM sys.dm_os_buffer_descriptors
GROUP BY db_name(database_id),database_id
ORDER BY 'Cached Size (MB)' DESC
OPTION (RECOMPILE);
Содержимое буферного пула, для базы
SELECT obj.name AS TableName, count(*)
FROM sys.dm_os_buffer_descriptors buf
INNER JOIN sys.allocation_units alloc ON alloc.allocation_unit_id = buf.allocation_unit_id
INNER JOIN sys.partitions part ON part.hobt_id = alloc.container_id
INNER JOIN sys.indexes ind ON ind.object_id = part.object_id AND ind.index_id = part.index_id
INNER JOIN sys.objects obj ON obj.object_id = part.object_id
WHERE buf.database_id = 2
group by obj.name
order by  count(*) desc
OPTION (RECOMPILE);
—10 запросов по времени работы и тд
SELECT TOP 10
QT.TEXT AS STATEMENT_TEXT,
QP.QUERY_PLAN,
QS.TOTAL_WORKER_TIME AS CPU_TIME
FROM SYS.DM_EXEC_QUERY_STATS QS
CROSS APPLY SYS.DM_EXEC_SQL_TEXT (QS.SQL_HANDLE) AS QT
CROSS APPLY SYS.DM_EXEC_QUERY_PLAN (QS.PLAN_HANDLE) AS QP
ORDER BY TOTAL_WORKER_TIME DESC
—plan_generation_num DESC
—[IO_total] DESC
—last_worker_time DESC
—last_logical_reads DESC
—last_elapsed_time DESC
OPTION (RECOMPILE);
проверка статистики
SELECT OBJECT_NAME(object_id) AS ObjectName,
    STATS_DATE(object_id, stats_id) AS StatisticsDate,
    *
FROM sys.stats
OPTION (RECOMPILE);
использование tempdb
SELECT
  sys.dm_exec_sessions.session_id AS [SESSION ID]
  ,DB_NAME(database_id) AS [DATABASE Name]
  ,HOST_NAME AS [System Name]
  ,program_name AS [Program Name]
  ,login_name AS [USER Name]
  ,status
  ,cpu_time AS [CPU TIME (in milisec)]
  ,total_scheduled_time AS [Total Scheduled TIME (in milisec)]
  ,total_elapsed_time AS    [Elapsed TIME (in milisec)]
  ,(memory_usage * 8)      AS [Memory USAGE (in KB)]
  ,(user_objects_alloc_page_count * 8) AS [SPACE Allocated FOR USER Objects (in KB)]
  ,(user_objects_dealloc_page_count * 8) AS [SPACE Deallocated FOR USER Objects (in KB)]
  ,(internal_objects
_alloc_page_count
* 8) AS [SPACE Allocated FOR Internal Objects (in KB)]
  ,(internal_objects_dealloc_page_count * 8) AS [SPACE Deallocated FOR Internal Objects (in KB)]
  ,CASE is_user_process
             WHEN 1      THEN 'user session'
             WHEN 0      THEN 'system session'
  END         AS [SESSION Type], row_count AS [ROW COUNT]
FROM
  sys.dm_db_session_space_usage
INNER join
  sys.dm_exec_sessions
ON  sys.dm_db_session_space_usage.session_id = sys.dm_exec_sessions.session_id
where status = 'running'
OPTION (RECOMPILE);
—текущие запросы
SELECT sqltext.TEXT,
req.session_id,
req.status,
req.command,
req.cpu_time,
req.total_elapsed_time,
req.logical_reads,
req.wait_type,
req.wait_time,
req.wait_resource,
req.blocking_session_id
FROM sys.dm_exec_requests req
CROSS APPLY sys.dm_exec_sql_text(sql_handle) AS sqltext
ORDER BY req.total_elapsed_time desc
OPTION (RECOMPILE);
использование памяти
select type, name, memory_node_id , (sum(single_pages_kb)+sum(multi_pages_kb)+ sum(virtual_memory_committed_kb)  )/1024  Totalmb
from sys.dm_os_memory_clerks
group by type, name, memory_node_id
ORDER BY TotalKB DESC
OPTION (RECOMPILE);
количество строк по таблицам
SELECT tbl.name , CAST(p.rows AS float)
FROM sys.tables AS tbl
INNER JOIN sys.indexes AS idx ON idx.object_id = tbl.object_id and idx.index_id < 2
INNER JOIN sys.partitions AS p ON p.object_id=CAST(tbl.object_id AS int)
AND p.index_id=idx.index_id
WHERE SCHEMA_NAME(tbl.schema_id)='dbo'
order by p.rows desc
OPTION (RECOMPILE);

Автор: Roman D
Дата публикации: 2015-07-15T08:20:00.001-07:00

Раздуло кэш SQL? Лечимся народными средствами.

Если при разработке приложений не пользоваться хранимыми процедурами и параметризованными запросами, чем грешат большинство разработчиков, посылая произвольные (Adhoc) запросы серверу
То рано или поздно можно столкнуться с проблемой раздутого процедурного кэша.
Подробнее о механизмах кэширования тут
1.Проверяем есть ли проблемы с процедурным кэшем.
Чтобы понять есть ли проблема с Adhoc запросами, достаточно запустить следующий запрос
SELECT T.*, cast(T.[Total Plans — USE Count 1]*1.0/nullif(t.[Total Plans],0) *100  AS decimal(5,2)) Percentage
FROM
(
       SELECT objtype AS [CacheType]
                    , count_big(*) AS [Total Plans]
                    , sum(cast(size_in_bytes as decimal(18,2)))/1024/1024 AS [Total MBs]
                    , sum(cast((CASE WHEN usecounts = 1 THEN size_in_bytes ELSE 0 END) as decimal(18,2)))/1024/1024 AS [Total MBs — USE Count 1]
                    , sum(CASE WHEN usecounts = 1 THEN 1 ELSE 0 END) AS [Total Plans — USE Count 1]
       FROM sys.dm_exec_cached_plans cp
       WHERE cp.cacheobjtype = N'Compiled Plan'
       AND cp.objtype IN (N'Adhoc', N'Prepared') — adhoc (произвольный запрос), prepared (параметризованный)
       GROUP BY objtype
) T
ORDER BY [Total MBs — USE Count 1] DESC
OPTION (RECOMPILE);  —план не будет сохраняться, эту опцию желательно использовать для adhoc запросов
Из результатов запроса понятно что процент  Adhoc  запросов  которые выполнялись только 1 раз очень высокий 82%

Ниже разница  в планах  adhoc и prepared планах
— выдать 100 первых запросов раздувающих кэш по размеру
SELECT top 100 *
FROM sys.dm_exec_cached_plans AS cp WITH (NOLOCK)
CROSS APPLY sys.dm_exec_sql_text(plan_handle)
WHERE cp.cacheobjtype = N'Compiled Plan'
AND cp.objtype = N'Adhoc'
AND cp.usecounts = 1
ORDER BY cp.size_in_bytes DESC OPTION (RECOMPILE);
Как-же это лечится если повлиять на разработчиков никак не возможно, есть как говорится одно народное средство …..

USE master
EXEC master.dbo.sp_configure 'show advanced options', 1
RECONFIGURE
EXEC master.dbo.sp_configure 'optimize for ad hoc workloads', 1
RECONFIGURE WITH OVERRIDE
GO
Использование этого параметра не очищает процедурный кэш, его очистку необходимо выполнить вручную, есть несколько вариантов:
1.
— очистить весь процедурный кэш для экземпляра
DBCC FREEPROCCACHE;

2. очистить для базы
—Идентификатор базы
DECLARE @intDBID INTEGER
SET @intDBID = (SELECT dbid FROM master.dbo.sysdatabases WHERE name = 'IntegrationDB')

—select @intDBID
—очищаем процедурный кэш для базы
DBCC FLUSHPROCINDB (@intDBID)

3. удалить конкретные планы вручную
— получаем указатель на план
SELECT cp.plan_handle, st.[text]
FROM sys.dm_exec_cached_plans AS cp
CROSS APPLY sys.dm_exec_sql_text(plan_handle) AS st
WHERE [text] LIKE N'%/* GetOnlineSearchResultsMonday %';

— удаляем план из кэша
DBCC FREEPROCCACHE (0x05000800F7BA926C40C15055070000000000000000000000);

Параметр optimize for ad hoc workloads используется для повышения эффективности кэширования планов рабочих нагрузок, содержащих много отдельных нерегламентированных пакетов. Если этот параметр имеет значение 1, компонент Database Engine при первой компиляции пакета, сохраняет в кэше планов небольшую скомпилированную заглушку плана Compiled Plan Stub, а не полный откомпилированный план. Это несколько снижает требования к памяти, так как кэш планов не заполняется скомпилированными, не используемыми повторно планами.
Скомпилированная заглушка плана принадлежит к объектам cacheobjtypes, которые можно просмотреть в представлении каталога sys.dm_exec_cached_plans. У каждой заглушки есть уникальный дескриптор SQL и дескриптор плана. Со скомпилированной заглушкой плана не связан план выполнения. Запрос по дескриптору плана не вернет XML-код Showplan
Для чего это надо ?
Каждая инструкция (T-SQL, SP и другие) сохраняет свой план выполнения в кэше, для дальнейшего использования. По этой причине, большое число планов хранится в системе и множество из них могут использоваться только 1 раз. Это в пустую тратит ресурсы сервера.
Чтобы не сохранялись в кэше планы запросов которые выолняются 1 раз придумали этот параметр сервера.
В загруженной системе легко может быть более миллиона разных инструкций, представьте сколько ресурсов вы тратите в пустую. Благодаря параметру optimize for ad hoc workloads  можно избежать хранения ненужных планов.
Если посмотреть на результаты  внизу после включения опции optimize for ad hoc workloads можно у