использование ssis для выполнения операций с высокой производительностью - PullRequest
0 голосов
/ 02 ноября 2010

Я пытаюсь выполнить операцию по созданию пользовательской сети на основе записей сведений о вызовах в моей таблице CDR.

Для простоты скажем, у меня есть таблица CDR:

CDRid
UserAId
UserBId

есть более 100 миллионов записей, поэтому таблица довольно большая.

Я установил таблицу user2user:

UserAId
UserBId
NumberOfConnections

затем, используя curos, я перебираю каждую строку в таблице, затем делаю оператор выбора:

если в таблице user2user есть запись с UserAId = UserAId из записи CDR и UserBId = UserBId из записи CDR, то увеличьте NumberOfConnections.

в противном случае вставить такую ​​строку, которая NumebrOfConnections = 1.

Довольно простая задача, и она работает, как я сказал, используя курсор, но она очень плоха по производительности (примерное время на моем компьютере ~ 60 часов).

Я слышал о Sql Server Integration Services, что он имеет более высокую производительность, когда речь идет о таких больших таблицах.

Проблема в том, что я понятия не имею, как настроить пакет служб SSIS для создания такой задачи.

Если кто-нибудь знает, как мне помочь, какие-нибудь хорошие ресурсы и т. Д., Я был бы очень благодарен.

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

спасибо за помощь,

приписка

Это сценарий, который я написал, и его выполнение занимает примерно 40 - 50 часов.

DECLARE CDR_cursor CURSOR FOR 
SELECT CDRId, SubscriberAId, BNumber 
FROM dbo.CDR

OPEN CDR_cursor;

FETCH NEXT FROM CDR_cursor 
INTO @CdrId, @SubscriberAId, @BNumber;

WHILE @@FETCH_STATUS = 0

BEGIN

- здесь я проверяю, есть ли пользователь с этим номером (потому что в CDR у меня есть только SubscriberAId - и BNumber, так что мне нужно проверить, какой это пользователь (у меня есть только пользователи из --network, чтобы каждый раз, когда я не мог найти этого пользователя, я добавлял одного, который является сетью outtide)

SELECT @UserBId = (Select UserID from dbo.Number where Number = @BNumber)
    IF (@UserBId is NULL)
    BEGIN
        INSERT INTO dbo.[User] (ID, Marked, InNetwork)
        VALUES (@OutUserId, 0, 0);

        INSERT into dbo.[Number](Number, UserId) values (@BNumber, @OutUserId);
        INSERT INTO dbo.User2User

        VALUES (@SubscriberAId, @OutUserId, 1)
        SET @OutUserId = @OutUserId - 1;
    END

    else

    BEGIN
        UPDATE dbo.User2User
        SET NumberOfConnections = NumberOfConnections + 1
        WHERE User1ID = @SubscriberAId AND User2ID = @UserBId
        -- Insert the row if the UPDATE statement failed.   
        if(@@ROWCOUNT = 0)
        BEGIN
            INSERT INTO dbo.User2User
            VALUES (@SubscriberAId, @UserBId, 1)
        END 
    END

    SET @Counter = @Counter + 1;

    if((@Counter % 100000) = 0)
    BEGIN
        PRINT Cast (@Counter as NVarchar(12));
    END
    FETCH NEXT FROM CDR_cursor 
    INTO @CdrId, @SubscriberAId, @BNumber;
END

CLOSE CDR_cursor;
DEALLOCATE CDR_cursor;

Ответы [ 4 ]

1 голос
/ 03 ноября 2010

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

Прекратите использовать предложение values ​​и используйте вместо него вставку с соединениями. То же самое с обновлениями. Если вам нужна дополнительная сложность, кейс-стаденет, вероятно, даст вам все, что вам нужно.

В общем, перестаньте думать о построчной обработке. Если вы можете написать выбор для курсора, вы можете написать оператор на основе набора для выполнения работы в 99,9% случаев.

Вам все еще может потребоваться курсор с такой большой таблицей, который будет обрабатывать пакеты данных (например, 1000 записей за раз), а не один, запускаемый последовательно по ряду.

1 голос
/ 02 ноября 2010

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

Лучшим решением было бы написать INSERT и UPDATE.заявление, которое даст вам то, что вы хотите.Благодаря этому вы сможете лучше использовать индексы в базе данных.Они будут выглядеть примерно так:

WITH SummaryCDR AS (UserAId, UserBId, Conns) AS
(
SELECT UserAId, UserBId, COUNT(1) FROM CDR
GROUP BY UserAId, UserBId)    
UPDATE user2user
SET NumberOfConnections = NumberOfConnections + SummaryCDR.Conns
FROM SummaryCDR
WHERE SummaryCDR.UserAId = user2user.UserAId
AND SummaryCDR.UserBId = user2user.UserBId

INSERT INTO user2user (UserAId, UserBId, NumberOfConnections)
SELECT CDR.UserAId, CDR.UserBId, Count(1)
FROM CDR
LEFT OUTER JOIN user2user
ON user2user.UserAId = CDR.UserAId
AND user2user.UserBId = CDR.UserBId
WHERE user2user.UserAId IS NULL

GROUP BY CDR.UserAId, CDR.UserBId

(Примечание: у меня нет времени на тестирование этого кода, вам придется отлаживать его самостоятельно)

1 голос
/ 02 ноября 2010

Не могли бы вы разбить условное обновление / вставку на два отдельных оператора и избавиться от курсора?

Сделайте INSERT для всех строк NULL и UPDATE для всех строк NOT NULL.

1 голос
/ 02 ноября 2010

это то, что тебе нужно?

select 
UserAId, UserBId, count(CDRid) as count_connections
from cdr
group by UserAId, UserBId
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...