SQL Server: Запросите быстро, но медленный из процедуры

Хорошей идеей является использование «объектно-реляционного картографа», подобного Idiorm :

$user = ORM::for_table('user')
->where_equal('username', 'j4mie')
->find_one();

$user->first_name = 'Jamie';
$user->save();

$tweets = ORM::for_table('tweet')
    ->select('tweet.*')
    ->join('user', array(
        'user.id', '=', 'tweet.user_id'
    ))
    ->where_equal('user.username', 'j4mie')
    ->find_many();

foreach ($tweets as $tweet) {
    echo $tweet->text;
}

Он не только избавляет вас от SQL-инъекций, но и от синтаксических ошибок! Также поддерживает коллекции моделей с цепочкой методов для фильтрации или применения действий к нескольким результатам сразу и нескольких подключений.

246
задан Ian Boyd 27 November 2015 в 09:34
поделиться

9 ответов

Я нашел проблему, вот сценарий медленных и быстрых версий хранимой процедуры:

dbo.ViewOpener__RenamedForCruachan__Slow.PRC

SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS OFF 
GO

CREATE PROCEDURE dbo.ViewOpener_RenamedForCruachan_Slow
    @SessionGUID uniqueidentifier
AS

SELECT *
FROM Report_Opener_RenamedForCruachan
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank
GO

SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

dbo.ViewOpener__RenamedForCruachan__Fast.PRC

SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

CREATE PROCEDURE dbo.ViewOpener_RenamedForCruachan_Fast
    @SessionGUID uniqueidentifier 
AS

SELECT *
FROM Report_Opener_RenamedForCruachan
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank
GO

SET QUOTED_IDENTIFIER OFF 
GO
SET ANSI_NULLS ON 
GO

, Если Вы не определили различие, я не обвиняю Вас. Различие не находится в хранимой процедуре вообще. Различие, которое превращает быстрые 0,5 запроса стоимости в тот, который делает нетерпеливую шпульку 6 миллионов строк:

Медленный: SET ANSI_NULLS OFF

Быстро: SET ANSI_NULLS ON

<час>

Этот ответ также мог быть сделан иметь смысл, так как представление действительно имеет пункт соединения, в котором говорится:

(table.column IS NOT NULL)

, Таким образом, существуют [приблизительно 1 111] включенная с.

<час>

объяснение далее доказано путем возврата для Запросов Analizer и выполнения

SET ANSI_NULLS OFF

.

DECLARE @SessionGUID uniqueidentifier
SET @SessionGUID = 'BCBA333C-B6A1-4155-9833-C495F22EA908'

.

SELECT *
FROM Report_Opener_RenamedForCruachan
WHERE SessionGUID = @SessionGUID
ORDER BY CurrencyTypeOrder, Rank

И запрос является медленным.

<час>

, Таким образом, проблема не , потому что запрос выполняется из хранимой процедуры. Проблема состоит в том, что опция по умолчанию соединения Руководителя предприятия ANSI_NULLS off, а не ANSI_NULLS on, который является значением по умолчанию QA.

Microsoft подтверждает этот факт в [1 115] KB296769 (ОШИБКА: не Может использовать SQL Enterprise Manager для создания хранимых процедур, содержащих объекты связанного сервера). Обходное решение, включают ANSI_NULLS опция в диалоговом окне хранимой процедуры:

Set ANSI_NULLS ON
Go
Create Proc spXXXX as
....
129
ответ дан shA.t 23 November 2019 в 03:04
поделиться

Вы попытались восстановить статистику и/или индексы на таблице Report_Opener. Все повторно соответствование SP ничего не будет стоить, если статистика все еще покажет данные из того, когда база данных была первым inauguarated.

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

3
ответ дан AnthonyWJones 23 November 2019 в 03:04
поделиться

Хотя я обычно против него (хотя в этом случае кажется, что у Вас есть подлинная причина), Вы попытались обеспечить какие-либо подсказки запроса на версии SP запроса? Если SQL Server готовит различный план выполнения в тех двух экземплярах, можно ли использовать подсказку для сообщения его, какой индекс использовать, так, чтобы план соответствовал первому?

Для некоторых примеров, можно пойти сюда .

РЕДАКТИРОВАНИЕ: Если можно отправить план запросов здесь, возможно, мы можем определить некоторое различие между планами, которые это говорит.

114-СЕКУНДНЫЙ: Обновленный ссылка, чтобы быть SQL конкретный 2000. Необходимо будет прокрутить вниз пути, но существует секунда, названная "Подсказки Таблицы", это - то, что Вы ищете.

ТРЕТЬ: "Плохой" запрос, кажется, игнорирует [IX_Openers_SessionGUID] на таблице "Openers" - шанс, добавляющий, что ИНДЕКС подсказывает для принуждения, это для использования того индекса изменит вещи?

1
ответ дан SqlRyan 23 November 2019 в 03:04
поделиться

Это, вероятно, маловероятно, но, учитывая, что Ваше наблюдаемое поведение необычно, оно должно быть проверено, и никто еще не упомянул его.

Вы абсолютно уверенный, что все объекты принадлежат dbo, и у Вас нет жулика копиями принадлежавший себе или различному пользовательскому подарку также?

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

В примечании, что в тексте Вы выполняете некоторые вещи, не определяя владельца, например,

sp_recompile ViewOpener

, если, например, там, где две копии подарка viewOpener, принадлежавшего dbo и [некоторый другой пользователь] тогда, какой Вы на самом деле перекомпилировали, если Вы не определяете, зависит от обстоятельств. Так же с представлением Report_Opener - если там, где две копии (и они могли отличаться по спецификации или плану выполнения) тогда то, что используется, зависит от обстоятельств - и поскольку Вы не определяете владельца, совершенно возможно, что Ваш специальный запрос мог бы использовать один, и скомпилированная процедура могла бы использовать использование другой.

, Как я говорю, это, вероятно, маловероятно, но это возможно и должно быть проверено, потому что Ваши проблемы могли быть то, что Вы просто ищете ошибку в неправильном месте.

1
ответ дан Cruachan 23 November 2019 в 03:04
поделиться

У меня есть другая идея. Что, если Вы создаете эту основанную на таблице функцию:

CREATE FUNCTION tbfSelectFromView
(   
    -- Add the parameters for the function here
    @SessionGUID UNIQUEIDENTIFIER
)
RETURNS TABLE 
AS
RETURN 
(
    SELECT *
    FROM Report_Opener
    WHERE SessionGUID = @SessionGUID
    ORDER BY CurrencyTypeOrder, Rank
)
GO

И затем выбранный из него с помощью следующего утверждения (даже помещающий это в SP):

SELECT *
FROM tbfSelectFromView(@SessionGUID)

похоже на то, что происходит (который все уже прокомментировали), то, что SQL Server просто делает предположение где-нибудь, это неправильно, и возможно это вынудит его исправить предположение. Я очень не хочу добавить дополнительный шаг, но я не уверен, что еще могло бы вызывать его.

0
ответ дан SqlRyan 23 November 2019 в 03:04
поделиться

На этот раз вы нашли свою проблему. Если в следующий раз вам повезет меньше и вы не сможете это понять, вы можете использовать замораживание плана и перестать беспокоиться о неправильном плане выполнения.

4
ответ дан 23 November 2019 в 03:04
поделиться

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

Я нашел еще один ответ здесь «Обнюхивание параметров» , спасибо Omnibuzz. Сводится к использованию «локальных переменных» в запросах хранимых процедур, но прочтите оригинал, чтобы лучше понять, это отличная статья. например,

Медленный способ:

CREATE PROCEDURE GetOrderForCustomers(@CustID varchar(20))
AS
BEGIN
    SELECT * 
    FROM orders
    WHERE customerid = @CustID
END

Быстрый способ:

CREATE PROCEDURE GetOrderForCustomersWithoutPS(@CustID varchar(20))
AS
BEGIN
    DECLARE @LocCustID varchar(20)
    SET @LocCustID = @CustID

    SELECT * 
    FROM orders
    WHERE customerid = @LocCustID
END

Надеюсь, это поможет кому-то другому, это сократило мое время выполнения с 5+ минут до примерно 6-7 секунд.

385
ответ дан 23 November 2019 в 03:04
поделиться

Сделайте это для своей базы данных. У меня такая же проблема - она ​​отлично работает в одной базе данных, но когда я копирую эту базу данных в другую с помощью импорта SSIS (а не обычного восстановления), эта проблема возникает с большинством моих хранимых процедур. Итак, погуглив еще немного, я нашел блог Пинала Дэйва (который, кстати, я встречал большую часть его поста и очень помог мне, так что спасибо Пиналу Дэйву) .

Я выполняю следующий запрос на моя база данных, и это исправило мою проблему:

EXEC sp_MSforeachtable @command1="print '?' DBCC DBREINDEX ('?', ' ', 80)"
GO
EXEC sp_updatestats
GO 

Надеюсь, это поможет. Просто передаю помощь от других, которые помогли мне.

17
ответ дан 23 November 2019 в 03:04
поделиться

-- Вот решение:

create procedure GetOrderForCustomers(@CustID varchar(20))

as

begin

select * from orders

where customerid = ISNULL(@CustID, '')

end

-- Вот оно

0
ответ дан 23 November 2019 в 03:04
поделиться
Другие вопросы по тегам:

Похожие вопросы: