Почему ad-hoc SQL работает быстрее, чем выполнение того же кода в хранимой процедуре? - PullRequest
0 голосов
/ 30 июня 2011

У меня есть хранимая процедура, которая обрабатывает телефоны и адреса в пакете в SQL Server 2005

Если выполнение хранимой процедуры занимает 2 часа. Но если я запускаю тот же код и тот же пакетный режим, это занимает 2 секунды.

Я попробовал следующие шаги, чтобы сделать это быстрее, но они не сработали:

  • Переиндексация всей базы данных
  • SET ANSI_NULLS ON;
  • DBCC FreeProcCache
  • DBCC DROPCLEANBUFFERS

Вот основной код

USE [MyDB]

GO



/****** Object:  StoredProcedure [myschema].[ProccesBatch]    Script Date: 06/30/2011 10:37:33 ******/

SET ANSI_NULLS ON

GO

SET QUOTED_IDENTIFIER ON

GO

CREATE PROCEDURE [myschema].[ProccesBatch] 
-- Add the parameters for the stored procedure here

(@BatchId int)


AS

BEGIN

-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.

SET NOCOUNT ON;
SET ANSI_NULLS ON;

-- AD Hoc TESTING ONLY. This gets uncommented when running ad hoc.
-- DECLARE @BatchId Int
-- SET @BatchId = 59

DECLARE @MyList AS VARCHAR (500)
DECLARE @MySICList AS VARCHAR (500)
DECLARE @MyType AS CHAR (1)
DECLARE @MyProvider AS VARCHAR (500)
DECLARE @MyState AS VARCHAR (2)
DECLARE @MyCityList AS VARCHAR (500)
DECLARE @MyZipList AS VARCHAR (500)
DECLARE @MyStyle AS VARCHAR (1)
DECLARE @MySource AS VARCHAR (150)
DECLARE @MyStartDate AS DATETIME
DECLARE @MyEndDate AS DATETIME
DECLARE @MyCampaign  AS BIT
DECLARE @CheckExist AS INT
SET @CheckExist = 0
-- 
--  1. Check if Campaign Exist.
--  
SELECT 
    @CheckExist = Id 
FROM myschema.Destination
WHERE Id = @BatchId

IF @CheckExist > 0
     BEGIN
     RAISERROR('Creation has already been processed', 16, 1)
     RETURN
     END      

-- 
--  2. Get Header and parameters for controlling process.
-- 
SELECT       
    @MyList = ISNULL(LeadBatchHeaderList,''),       
    @MySICList = ISNULL(SICCodeList,''),
    @MyType = ISNULL(MyType,''),
    @MyProvider = ISNULL(LDCList,''),
    @MyState = ISNULL([State],''),
    @MyCityList = ISNULL(CityList,''),      
    @MyZipList = ISNULL(ZipCodeList,''),        
    @MyStyle = ISNULL(Commodities,''),
    @MySource = ISNULL(LeadSource,''),
    @MyStartDate = ISNULL(HeaderCreationStart,''),
    @MyEndDate = ISNULL(HeaderCreationEnd,''),
    @MyCampaign = ISNULL(AllCampaign ,'')
FROM myschema.Header
WHERE ID = @BatchId

IF @@ROWCOUNT < 1
     BEGIN
     RAISERROR('header id was not found', 16, 1)
     RETURN
     END   

-- Place Commas for charindex     
IF @MyList > ''
    SET @MyList = ',' + @MyList + ','
IF @MySICList  > ''
    SET @MySICList =  ',' + @MySICList + ','
IF @MyProvider > ''
    SET @MyProvider = ',' + @MyProvider + ','
IF @MyCityList > '' 
    SET @MyCityList = ',' + @MyCityList + ','   
IF @MyZipList > '' 
    SET @MyZipList = ',' + @MyZipList + ','    

--
-- 3. Add  qualifying leads.
--
INSERT INTO myschema.Destination    
(Id, LeadBatchDetailId, CustomerIdOne, CustomerIdTwo, MyProviderOne, MyProviderTwo, SicCode, SicDesc, SicCode2, SicDesc2, 
MyType, Company, CompanyURL, Title, Salutation, Suffix, FullName, FirstName, MiddleInitial, 
LastName, Email, MyPhone, Work, Cell, Home, Fax, Ext, Address1, Address2, City, [State], 
Zip5, Zip4, County, TSR, EmployeeSize, Revenue, MyProviderOne, MyProviderTwo, CustomerUsageOne, CustomerUsageTwo, MyExpenses, Remarks, Decline, 
WhyLeft, PCC, RCC, PCC, SCC)
SELECT 
    @BatchId, d.ID, d.CustomerIdOne, d.CustomerIdTwo, d.MyProviderOne, d.MyProviderTwo, d.SicCode, d.SicDesc, d.SicCode2, d.SicDesc2, 
    d.MyType, d.Company, d.CompanyURL, d.Title, d.Salutation, d.Suffix, d.FullName, d.FirstName, d.MiddleInitial, 
    d.LastName, d.Email, d.MyPhone, d.Work, d.Cell, d.Home, d.Fax, d.Ext, d.Address1, d.Address2, d.City, d.[State], 
    d.Zip5, d.Zip4, d.County, d.TSR, d.EmployeeSize, d.Revenue, d.MyProviderOne, d.MyProviderTwo,d.CustomerUsageOne, d.CustomerUsageTwo, d.MyExpenses, d.Remarks, d.Decline, 
    d.WhyLeft, d.PCC, d.RCC, d.PCC, d.SCC
FROM myschema.Source as d 
JOIN myschema.Summary as h ON d.MyId = h.ID
JOIN myschema.source AS s ON h.Id = s.ID
WHERE
    -- MyId. 
    (@MyList = '' OR (charindex(',' + CAST(d.MyId AS VARCHAR) + ',', @MyList) > 0)) AND

    -- SIC Code. 
    (@MySICList = '' OR (charindex(',' + CAST(d.SicCode AS VARCHAR) + ',', @MySICList) > 0)) AND

    -- My Types 
    (@MyType = '' OR @MyType = 'A' OR d.MyType = @MyType OR h.DefaultMyType = @MyType) AND

    -- MYProviders
    ((@MyProvider = '' OR (charindex(',' + CAST(d.MyProviderOne AS VARCHAR) + ',', @MyProvider) > 0)) OR
    (@MyProvider = '' OR (charindex(',' + CAST(d.MyProviderTwo AS VARCHAR) + ',', @MyProvider) > 0))) AND

    -- State. 
    (@MyState = '' OR d.[State] = @MyState) AND

    -- City.
    (@MyCityList = '' OR (charindex(',' + d.City + ',', @MyCityList) > 0)) AND

    -- Zip Code. 
    (@MyZipList = '' OR (charindex(',' + d.Zip5 + ',', @MyZipList) > 0)) AND

    -- LeadSource
    (@MySource = '' OR s.MySource = @MySource) AND

    -- Between Dates
    (@MyStartDate = '' AND @MyEndDate = '' OR h.CreationDate BETWEEN @MyStartDate AND @MyEndDate) AND 

     -- Mystyle
    ((@MyStyle = 'A' AND (d.MyProviderOne IS NOT NULL OR d.MyProviderOne > 0 OR d.CustomerUsageOne > 0)) OR
    (@MyStyle = 'B' AND (d.MyProviderTwo IS NOT NULL OR d.MyProviderTwo > 0 OR d.CustomerUsageTwo > 0)) OR
    (@MyStyle = '' OR @MyStyle IS NULL)) AND

    -- Source parameters are important. Only processed finished batches.
    (h.UseThisRecord = 1) AND
    (h.[status] = 'Finished') AND
    (d.MyDuplicate IS NULL) AND
    (d.DoNotUseFlag IS NULL) AND
    (d.DoNotUseIFlag IS NULL) AND
    (d.CustomerHome IS NULL) AND
    (d.CustomerWork IS NULL) AND
    (d.LeadDuplicate IS NULL) AND
    (d.MyPhone >'' OR d.MyPhone <> NULL) AND
    ((CAST(FLOOR( CAST( h.ExpirationDate AS FLOAT ) )AS DATETIME) > CAST(FLOOR( CAST( GETDATE() AS FLOAT ) )AS DATETIME)) OR 
    h.ExpirationDate IS NULL)



--
-- 4. Flag Phone Duplicates inside myschema.Destination
--

 UPDATE T1
  SET DeleteFlag = 1
  FROM myschema.Destination T1, myschema.Destination T2
  WHERE
        T1.MyPhone = T2.MyPhone AND                   
        T1.FullName = T2.FullName AND 
        T1.Address1 = T2.Address1 AND       
        T1.City = T2.City AND                     
        T1.[State] = T2.[State] AND                     
        T1.Zip5 = T2.Zip5 AND                     
        T1.MyPhone <> '' AND
        T1.Id = T2.Id AND -- This will flag the batch itself
        T1.Id = @BatchId AND
        T1.Id < T2.Id  -- This will leave the highest Id unflagged (latest record) 



--
-- 5. Duplicate Contact Flag. All Records
--      
IF @MyCampaign  = 1
UPDATE T1
  SET DeleteFlag = 1
  FROM myschema.Destination T1, myschema.Destination T2
  WHERE
        T1.MyPhone = T2.MyPhone AND                   
        T1.FullName = T2.FullName AND 
        T1.Address1 = T2.Address1 AND       
        T1.City = T2.City AND                     
        T1.[State] = T2.[State] AND                     
        T1.Zip5 = T2.Zip5 AND                     
        T1.MyPhone <> '' AND
        T1.Id = @BatchId AND
        T1.Id <> T2.Id -- Process against other batches



--
-- 6. Active Flag
--  

IF @MyCampaign <> 1
UPDATE T1
  SET DeleteFlag = 1
  FROM myschema.Destination T1, myschema.Destination T2
  JOIN myschema.Header H ON T2.Id = H.ID
  WHERE
        T1.MyPhone = T2.MyPhone AND                   
        T1.FullName = T2.FullName AND 
        T1.Address1 = T2.Address1 AND       
        T1.City = T2.City AND                     
        T1.[State] = T2.[State] AND                     
        T1.Zip5 = T2.Zip5 AND                     
        T1.MyPhone <> '' AND
        T1.Id = @BatchId AND
        T1.Id <> T2.Id AND -- Process against other batches
        H.ActiveBatch = 1 -- Only Active



--
-- 7. Delete DeleteFlag rows. Check for Id just in case
--  
IF @BatchId > 0
    DELETE FROM myschema.Destination
        WHERE        
            (DeleteFlag = 1) AND (Id = @BatchId)



--
-- 8. Update header with date last run
--                  
UPDATE myschema.Header
SET DateLastRun = GETDATE()
WHERE ID = @BatchId



END






GO

Спасибо, Christian

Ответы [ 6 ]

5 голосов
/ 30 июня 2011

Это часто происходит, когда вы используете параметры в процедуре и константы ad-hoc

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

Существуют и другие параметры, такие как параметры SET для хранимой процедуры или несоответствия типов данных ... но у нас нет дальнейшей информации.

3 голосов
/ 30 июня 2011

1 - Показать код.

2 - не видя код, я предполагаю, что у вас есть несколько параметров, которые вы передаете в сохраненный процесс, и у вас есть сниффинг параметроввыпуск.

2 голосов
/ 30 июня 2011

Это может быть связано с анализом параметров.Вы сравнили планы выполнения?Если вы возьмете бесплатный Проводник планов из http://www.sqlsentry.net/plan-explorer/sql-server-query-view.asp, вы сможете довольно четко увидеть, среди прочего, время выполнения и скомпилированные значения параметров для вашей хранимой процедуры.Иногда добавление WITH RECOMPILE в процедуру может помочь предотвратить срыв плохих планов (при потенциальной стоимости чуть более высокой загрузки ЦП при каждом выполнении процедуры).Но это не всегда лучший ответ - план, который в настоящее время кэшируется, может быть нетипичным и РЕКОМЕНДУЕТСЯ каждый раз, когда это излишне.Иногда ответ заключается в том, как определяются параметры - например, если вы передадите параметр, вы можете иногда победить перехват параметров, объявив локальную переменную и передав ей входной параметр и используя локальную переменную позже в коде.Иногда ответ включает оптимизацию для специальных настроек.Трудно сказать без подробностей.

Однако, исследование плана - хороший первый шаг.Если вы видите, что планы разные, отправьте нам сообщения о различиях, и мы поможем вам.

1 голос
/ 30 июня 2011

Вероятно, неверный план запроса для хранимой процедуры.Попробуйте сбросить и воссоздать хранимую процедуру.

1 голос
/ 30 июня 2011

Было бы полезно увидеть спрок и запрос, но я предполагаю, что в хранимой процедуре кешируется что-то «плохое»Попробуйте очистить кэш SQL (при условии использования SQL Server из ваших тегов).Смотри это

0 голосов
/ 30 июня 2011

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

Я согласен со всеми остальными - покажи код.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...