Выявление неиспользуемых хранимых процедур


24

В следующем году я помогаю очистить несколько сред SQL Server.

У нас есть около 10 000 хранимых процедур, и, по нашим оценкам, только около 1000 из них используются на регулярной основе, а еще около 200 используются в редких случаях, что означает, что у нас много работы.

Поскольку у нас есть несколько отделов и групп, которые могут получить доступ к этим базам данных и процедурам, мы не всегда являемся теми, кто вызывает процедуры, а это означает, что мы должны определить, какие процедуры вызываются. Кроме того, мы хотим определить это в течение нескольких месяцев, а не нескольких дней (что исключает некоторые возможности).

Один из подходов к этому состоит в том, чтобы использовать SQL Server Profilerи отслеживать, какие процедуры вызываются, и сравнивать их со списком тех процедур, которые у нас есть, отмечая, используются ли эти процедуры или нет. С тех пор мы можем перенести процедуры в другую схему, если отдел кричит.

Является ли использование Profilerнаиболее эффективного подхода здесь? И / или кто-нибудь из вас сделал нечто подобное и нашел другой способ / лучший способ сделать это?

Ответы:


32

Вы можете использовать трассировку на стороне сервера (в отличие от графического пользовательского интерфейса Profiler, который требует больше ресурсов) во время тестирования или вашего бизнес-цикла и собирать только данные, связанные с SP. Затем вы можете загрузить это в таблицу или Excel для дальнейшего анализа.

Второй подход состоит в том, чтобы использовать DMV sys.dm_exec_procedure_stats (с ограничением, что, если сервер sql перезапускается, то данные сбрасываются).

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

 -- Get list of possibly unused SPs (SQL 2008 only)
    SELECT p.name AS 'SP Name'        -- Get list of all SPs in the current database
    FROM sys.procedures AS p
    WHERE p.is_ms_shipped = 0

    EXCEPT

    SELECT p.name AS 'SP Name'        -- Get list of all SPs from the current database 
    FROM sys.procedures AS p          -- that are in the procedure cache
    INNER JOIN sys.dm_exec_procedure_stats AS qs
    ON p.object_id = qs.object_id
    WHERE p.is_ms_shipped = 0;

Ссылаться на :


1
Также смотрите stackoverflow.com/questions/10421439/… и stackoverflow.com/questions/7150900/… (игнорируя, что в последнем случае ссылка на SQLServerPedia теперь не работает).
Аарон Бертран

2
Обязательно проверяйте DMV периодически в течение недель или даже месяцев, поскольку могут быть SP, которые запускаются только ежемесячно или даже ежеквартально. DMV удаляются, когда экземпляр перезапускается, очищается вручную или даже просто с течением времени.
Кеннет Фишер

1
@KennethFisher Вот почему я рекомендовал запланировать работу по сбору данных DMV в таблицу. Спасибо за упоминание, хотя!
Кин Шах

11

Вы можете найти этот вопрос полезным, он относится к таблицам и столбцам, но предлагает использовать сторонний инструмент ApexSQL Clean, который также может найти неиспользуемые хранимые процедуры, а также все объекты, на которые не ссылается ни один другой объект в базе данных или во внешних базах данных.

Отказ от ответственности: я работаю на ApexSQL в качестве инженера службы поддержки


3
ОП не хочет найти unreferenced stored procedures, вместо этого ОП хочет найти неиспользуемый СП. Ваш ответ не служит ответом на этот вопрос.
Кин Шах

Кин, я обновлю. ApexSQL Clean помечает неиспользуемые объекты как не связанные, поэтому я понимаю, что это вызвало путаницу
Милица Медик

10

Если вы используете SQL Server 2008+, вы также можете использовать расширенные события с целью гистограммы . Возможно, это будет более легкий вес, чем след.

AFAIK, вам нужно будет создать отдельный сеанс для каждой интересующей базы данных, хотя я не вижу никаких признаков того, что группирование по нескольким столбцам возможно. Быстрый пример ниже фильтров наdatabase_id=10

CREATE EVENT SESSION [count_module_start_database_10]
ON SERVER
ADD EVENT sqlserver.module_start
(  
        WHERE (source_database_id=10) 
)
ADD TARGET package0.asynchronous_bucketizer
(     SET  filtering_event_name='sqlserver.module_start', 
            source_type=0, 
            source='object_id',
            slots = 10000
)
WITH (MAX_DISPATCH_LATENCY = 5 SECONDS)
GO
ALTER EVENT SESSION [count_module_start_database_10]
ON SERVER
STATE=START

И затем после запуска некоторых хранимых процедур в этой БД несколько раз и извлечения данных с

SELECT CAST(target_data as XML) target_data
FROM sys.dm_xe_sessions AS s 
JOIN sys.dm_xe_session_targets t
    ON s.address = t.event_session_address
WHERE s.name = 'count_module_start_database_10'

Выход

<HistogramTarget truncated="0" buckets="16384">
  <Slot count="36">
    <value>1287675635</value>
  </Slot>
  <Slot count="3">
    <value>1271675578</value>
  </Slot>
  <Slot count="2">
    <value>1255675521</value>
  </Slot>
</HistogramTarget>

Показывает, что процедура с object_idof 1287675635была выполнена, например, 36 раз. Это asynchronous_bucketizerтолько память, поэтому было бы лучше настроить что-то, что опрашивает это время от времени и сохраняет в постоянное хранилище.


1
Это правда, вам нужен один сеанс на базу данных. Было бы здорово сказать, WHERE (source_database_id IN (10,15,20))но, увы, это не поддерживается.
Аарон Бертран

@AaronBertrand - И даже если бы он был поддержан, вам все равно нужно было бы подсчитывать вызовы процедур для объектов с одинаковыми object_id(или одинаковыми object_name) в разных базах данных отдельно, и я не думаю, что это возможно.
Мартин Смит

Поправьте меня, если я ошибаюсь, но extended eventsгде добавить в 2012 году, а не 2008?
Питер


1
Интерфейс расширенных событий не был представлен до SSMS 2012, и я не думаю, что они сделали его обратно совместимым. В 2008 году единственный способ создать сеансы из коробки был через TSQL, хотя существовал проект сообщества для аналогичной функциональности extendedeventmanager.codeplex.com
Мартин Смит

4

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

--  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--  Create the use table 
--  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
CREATE TABLE [dbo].[_ProcedureUseLog](
    [ObjectName] [nvarchar](255) NOT NULL,
    [UseCount] [int] NULL,
    [LastUse] [datetime] NULL,
    [LastCache] [datetime] NULL,
 CONSTRAINT [PK___PROCEDURE_USE] PRIMARY KEY CLUSTERED 
(
    [ObjectName] ASC
)WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON) ON [PRIMARY]
) ON [PRIMARY]
GO
ALTER TABLE [dbo].[_ProcedureUseLog] ADD  CONSTRAINT [DF_Table_1_References]  DEFAULT ((0)) FOR [UseCount]
GO

--  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--  Run this periodically to update the usage stats
--  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
DECLARE @UsesTable TABLE
(
    ObjectName nvarchar(255),
    Executions int,
    LastUse datetime,
    LastCache datetime
)

INSERT INTO @UsesTable       
SELECT p.name, qs.execution_count, qs.last_execution_time, qs.cached_time
FROM    sys.procedures AS p LEFT OUTER JOIN
        sys.dm_exec_procedure_stats AS qs ON p.object_id = qs.object_id
WHERE        (p.is_ms_shipped = 0)

MERGE [dbo].[_ProcedureUseLog]      AS [Target]
USING @UsesTable                    AS [Source]
    ON Target.ObjectName = Source.ObjectName
WHEN MATCHED AND 
        ( Target.LastCache <> Source.LastCache)
    THEN UPDATE SET
        Target.UseCount = Target.UseCount + Source.Executions,
        Target.LastCache = Source.LastCache,
        Target.LastUse = Source.LastUse
WHEN NOT MATCHED
    THEN INSERT (ObjectName, UseCount, LastUse, LastCache) 
    VALUES      (ObjectName, Executions, LastUse, LastCache);

--  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
--  This just shows what you've logged so far
--  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
SELECT * FROM [_ProcedureUseLog] ORDER BY UseCount DESC

0

В этом посте также представлен скрипт для поиска неиспользуемых объектов. Поиск неиспользуемых таблиц базы данных в SQL Server. Ниже приведен скрипт из этой статьи. Я изменил тип таблицы «U» на тип хранимой процедуры «P»:

   USE DBName;
   SELECT 

       ao.[name] [Table],
       s.[name] [Schema],
       [create_date] [Created],
        [modify_date] [LastModified]
    FROM
         sys.all_objects ao JOIN sys.schemas s
           ON ao.schema_id = s.schema_id
    WHERE
         OBJECT_ID NOT IN (
              SELECT OBJECT_ID
              FROM sys.dm_db_index_usage_stats
        )
        AND [type] = 'P'
    ORDER BY
        [modify_date] DESC

Это всегда будет возвращать все процедуры, так как процедуры не получают записи, сделанные в статистике использования индекса DMV ...
Martin Smith
Используя наш сайт, вы подтверждаете, что прочитали и поняли нашу Политику в отношении файлов cookie и Политику конфиденциальности.
Licensed under cc by-sa 3.0 with attribution required.