Как улучшить производительность хранимых процедур SQL Server? - PullRequest
2 голосов
/ 09 февраля 2012

Я вызываю хранимую процедуру через блок приложения DAL Enterprise Library и передаю DataTable, который, в свою очередь, «принимается» как пользовательский тип данных таблицы (@names как NamesTable) в моей процедуре. Процедура очень медленная после второго вызова, и я ищу другой способ ее реализации, чтобы производительность значительно возросла.

Таблицы Names / HistoricalNames огромны (100 миллионов записей), а данные, передаваемые в эти таблицы (через параметр набора данных / таблицы), составляют около 4 миллионов записей).

В основном то, что он делает (должен делать), следующее:

  1. Import @names (который является параметром DataTable / Table
    • Проверьте, содержит ли таблица Names или HistoricalNames любое из имен, содержащихся в новом параметре набора данных / таблицы, , если это так, пропустите весь импорт и верните 2
    • В противном случае вставьте все записи из @names в Names и верните 1 ;

Таблицы выглядят так:

create table Names
(
    id int IDENTITY(1,1) NOT NULL,
    name nvarchar(20),
    otherId uniqueidentifier
)

create table HistoricalNames
(
    id int IDENTITY(1,1) NOT NULL,
    name nvarchar(20),
    otherId uniqueidentifier
)

Табличный параметр (@names) выглядит следующим образом:

create table NameTable
(
    name nvarchar(20)
    otherId uniqueidentifier
)

Это процедура:

GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[sp_ImportNames]
    @names NameTable READONLY
AS
BEGIN       
    IF ((SELECT COUNT(cd.name) FROM Names as cd WHERE cd.name IN (SELECT c.name FROM @names as c)) > 0)
        BEGIN
            SELECT 2;
        END
    ELSE IF ((SELECT COUNT(cd.name) FROM HistoricalNames as cd WHERE cd.name IN (SELECT c.name FROM @names as c)) > 0)
        BEGIN
            SELECT 2;
        END
    ELSE
        BEGIN
            INSERT INTO Names (name, otherId) SELECT * FROM @names;
            SELECT 1;
        END
END


GO

Может ли это быть легко настроено на производительность? Любая помощь будет принята с благодарностью!

Ответы [ 5 ]

3 голосов
/ 09 февраля 2012

Параметр табличного значения почти наверняка является вашей проблемой.

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

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

Таким образом, вы получаете сканирование таблицы строк длиной 4 метра, которое никогда не требуется в реляционной базе данных.

Вы должны получить значительный прирост, вставив его в РЕАЛЬНУЮ таблицу в качестве промежуточной области с индексом, а затем выполнив свою операцию с этой таблицей вместо параметра.Также убедитесь, что у вас есть индексы и для других таблиц.

2 голосов
/ 09 февраля 2012

Может быть, что-то вроде этого:

GO

SET ANSI_NULLS ON
GO

SET QUOTED_IDENTIFIER ON
GO

ALTER PROCEDURE [dbo].[sp_ImportNames]
    @names NameTable READONLY
AS
BEGIN       
    IF EXISTS(SELECT NULL FROM Names as cd WHERE EXISTS(SELECT NULL FROM @names as c WHERE c.name=cd.name))
        BEGIN
            SELECT 2;
        END
    ELSE IF EXISTS(SELECT NULL FROM HistoricalNames as cd WHERE EXISTS(SELECT NULL FROM @names as c WHERE c.name=cd.name))
        BEGIN
            SELECT 2;
        END
    ELSE
        BEGIN
            INSERT INTO Names (name, otherId) SELECT * FROM @names;
            SELECT 1;
        END
END
1 голос
/ 09 февраля 2012

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

Я бы также проверил производительность существующих с помощью объединения:

if exists(select 1
from Names exist
inner join @names newNames on newNames.name = exist.name)
begin
  select 2;
end

Также обратите внимание, что обычно рекомендуется использовать явное использование имен столбцов для вставки для случая «без совпадения»:

insert into Names (name, otherId)
select name, otherId
from @names
1 голос
/ 09 февраля 2012

Хм.

  • Одно утверждение "ЕСЛИ НЕ СУЩЕСТВУЕТ" с двумя проверками.Вы рассчитываете полный счет каждый раз, но интересуетесь только указанием, существует ли один элемент, что можно сделать быстрее (отменить запрос, как только будет найдена одна строка).По этой причине существует предложение EXISTS.
1 голос
/ 09 февраля 2012

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

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