как улучшить производительность SQL-запроса в моем случае - PullRequest
1 голос
/ 18 июня 2009

У меня есть таблица, схема очень проста, столбец идентификатора как уникальный первичный ключ (тип уникального идентификатора) и некоторые другие столбцы nvarchar. Моя текущая цель состоит в том, чтобы на 5000 входных данных мне нужно было рассчитать, какие из них уже содержатся в таблице, а какие нет. Входные данные являются строковыми, и у меня есть функция C #, которая преобразует строку в уникальный идентификатор (GUID). Моя логика заключается в том, что при наличии существующего идентификатора я рассматриваю строку как уже содержащуюся в таблице.

У меня такой вопрос: если мне нужно выяснить, какие из 5000 входных строк уже содержатся в БД, а какие нет, каков наиболее эффективный способ?

Кстати: моя текущая реализация заключается в том, чтобы преобразовать строку в GUID с использованием кода C #, а затем вызвать / реализовать процедуру сохранения, которая запрашивает, существует ли идентификатор в базе данных и возвращает ли он код C #.

Моя рабочая среда: VSTS 2008 + SQL Server 2008 + C # 3.5.

Ответы [ 7 ]

3 голосов
/ 18 июня 2009

Шаг 1. Убедитесь, что у вас есть проблема, которую нужно решить. Пять тысяч вставок - это не много для вставки по одной во многих контекстах.

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

3 голосов
/ 18 июня 2009

Моим первым инстинктом было бы закачать ваши 5000 входов во временную таблицу X с одним столбцом, возможно, проиндексировать ее, а затем использовать:

SELECT X.thecol
FROM X
JOIN ExistingTable USING (thecol)

, чтобы получить те, которые присутствуют, и (если необходимы оба набора)

SELECT X.thecol
FROM X
LEFT JOIN ExistingTable USING (thecol)
WHERE ExistingTable.thecol IS NULL

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

Редактировать: по запросу, вот несколько хороших документов и учебных пособий по временным таблицам в SQL Server. Билл Грациано имеет простое введение, охватывающее временные таблицы, табличные переменные и глобальные временные таблицы. Randy Dyess и SQL Master обсуждают проблемы производительности за и против них (но помните, что если у вас возникают проблемы с производительностью, вы действительно хотите сравнить альтернативы, не просто перейти на теоретические соображения! -).

MSDN содержит статьи по tempdb (где хранятся временные таблицы) и , оптимизирующим его производительность.

2 голосов
/ 18 июня 2009

Что вам нужно делать с теми записями, которые существуют или не существуют в вашей таблице ??

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

Ваше утверждение будет выглядеть примерно так:

MERGE INTO 
    (your target table) AS t
USING 
    (your source table, e.g. a temporary table) AS s
ON t.ID = s.ID
WHEN NOT MATCHED THEN  -- new rows does not exist in base table
  ....(do whatever you need to do)
WHEN MATCHED THEN      -- row exists in base table
  ... (do whatever else you need to do)
;

Чтобы сделать это действительно быстро, я бы загружал "новые" записи, например, из файл TXT или CSV во временную таблицу на сервере SQL с помощью BULK INSERT:

BULK INSERT YourTemporaryTable
FROM 'c:\temp\yourimportfile.csv'
WITH 
(
    FIELDTERMINATOR =',',
    ROWTERMINATOR =' |\n'
)

BULK INSERT в сочетании с MERGE должен дать вам наилучшую производительность на этой планете: -)

Марк

PS: вот заметка от TechNet о производительности MERGE и почему она быстрее, чем отдельные заявления:

В SQL Server 2008 вы можете выполнять несколько операций на языке манипулирования данными (DML) в одном операторе, используя оператор MERGE. Например, вам может потребоваться синхронизировать две таблицы, вставляя, обновляя или удаляя строки в одной таблице на основе различий, обнаруженных в другой таблице. Как правило, это выполняется путем выполнения хранимой процедуры или пакета, который содержит отдельные операторы INSERT, UPDATE и DELETE. Однако это означает, что данные в исходной и целевой таблицах оцениваются и обрабатываются несколько раз; хотя бы один раз для каждого утверждения. Используя оператор MERGE, вы можете заменить отдельные операторы DML одним оператором. Это может повысить производительность запросов, поскольку операции выполняются в одном операторе, что сводит к минимуму количество обработок данных в исходной и целевой таблицах. Однако прирост производительности зависит от наличия правильных индексов, объединений и других факторов. В этом разделе приведены рекомендации, которые помогут вам достичь оптимальной производительности при использовании оператора MERGE.

1 голос
/ 18 июня 2009

Поскольку вы используете Sql server 2008, вы можете использовать табличные параметры. Это способ предоставления таблицы в качестве параметра для хранимой процедуры.

Используя ADO.NET, вы можете легко предварительно заполнить DataTable и передать его как SqlParameter. Шаги, которые необходимо выполнить:

Создание пользовательского типа Sql

CREATE TYPE MyType AS TABLE
(
UniqueId INT NOT NULL,
Column NVARCHAR(255) NOT NULL
) 

Создать хранимую процедуру, которая принимает тип

CREATE PROCEDURE spInsertMyType
@Data MyType READONLY
AS 
xxxx

Звоните с помощью C #

SqlCommand insertCommand = new SqlCommand(
   "spInsertMyType", connection);
 insertCommand.CommandType = CommandType.StoredProcedure;
 SqlParameter tvpParam = 
    insertCommand.Parameters.AddWithValue(
    "@Data", dataReader);
 tvpParam.SqlDbType = SqlDbType.Structured;

Ссылки: Табличные параметры в Sql 2008

1 голос
/ 18 июня 2009

Определенно не делайте этого один за другим.

Мое предпочтительное решение - создать хранимую процедуру с одним параметром, который может принимать XML, в следующем формате:

<ROOT>
  <MyObject ID="60EAD98F-8A6C-4C22-AF75-000000000000">
  <MyObject ID="60EAD98F-8A6C-4C22-AF75-000000000001">
  ....
</ROOT>

Затем в процедуре с аргументом типа NCHAR (MAX) вы конвертируете его в XML, после чего используете его как таблицу с одним столбцом (давайте назовем его @FilterTable). Процедура магазина выглядит так:

CREATE PROCEDURE dbo.sp_MultipleParams(@FilterXML NVARCHAR(MAX))
AS BEGIN
    SET NOCOUNT ON

    DECLARE @x XML
    SELECT @x = CONVERT(XML, @FilterXML)

    -- temporary table (must have it, because cannot join on XML statement)
    DECLARE @FilterTable TABLE (
         "ID" UNIQUEIDENTIFIER
    )

    -- insert into temporary table
    -- @important: XML iS CaSe-SenSiTiv
    INSERT      @FilterTable
    SELECT      x.value('@ID', 'UNIQUEIDENTIFIER')
    FROM        @x.nodes('/ROOT/MyObject') AS R(x)

    SELECT      o.ID,
                SIGN(SUM(CASE WHEN t.ID IS NULL THEN 0 ELSE 1 END)) AS FoundInDB
    FROM        @FilterTable o
    LEFT JOIN   dbo.MyTable t
            ON  o.ID = t.ID
    GROUP BY    o.ID

END
GO

Вы запускаете его как:

EXEC sp_MultipleParams '<ROOT><MyObject ID="60EAD98F-8A6C-4C22-AF75-000000000000"/><MyObject ID="60EAD98F-8A6C-4C22-AF75-000000000002"/></ROOT>'

И ваши результаты выглядят так:

ID                                   FoundInDB
------------------------------------ -----------
60EAD98F-8A6C-4C22-AF75-000000000000 1
60EAD98F-8A6C-4C22-AF75-000000000002 0
1 голос
/ 18 июня 2009

Если вам нужна простота, поскольку 5000 записей не очень много, то из C # просто используйте цикл для генерации оператора вставки для каждой из строк, которые вы хотите добавить в таблицу. Заверните вставку в блок TRY CATCH. Отправьте их всем на сервер одним выстрелом так:

BEGIN TRY
INSERT INTO table (theCol, field2, field3)
SELECT theGuid, value2, value3
END TRY BEGIN CATCH END CATCH

BEGIN TRY
INSERT INTO table (theCol, field2, field3)
SELECT theGuid, value2, value3
END TRY BEGIN CATCH END CATCH

BEGIN TRY
INSERT INTO table (theCol, field2, field3)
SELECT theGuid, value2, value3
END TRY BEGIN CATCH END CATCH

если в вашем строковом GUID определен уникальный индекс или первичный ключ, то дубликаты вставок завершатся неудачно. Проверка заранее, чтобы увидеть, не существует ли запись, просто дублирует работу, которую SQL все равно будет выполнять.

Если производительность действительно важна, подумайте о загрузке 5000 GUIDS на свою местную станцию ​​и проведении всего анализа на месте. Чтение 5000 GUIDS должно занимать гораздо меньше 1 секунды. Это проще, чем массовый импорт во временную таблицу (это единственный способ получить производительность из временной таблицы) и выполнение обновления с использованием соединения с временной таблицей.

1 голос
/ 18 июня 2009

Постарайтесь обеспечить выполнение только одного запроса, т. Е. Если ваше решение состоит из 5000 запросов к базе данных, это, вероятно, будет самым большим потребителем ресурсов для операции.

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

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