Цикл Цикл с внешним запросом - PullRequest
1 голос
/ 26 августа 2010

Я перебираю все свои базы данных и объединяю результаты в базу данных агрегатов.

В моем цикле я звоню

master.dbo.xp_cmdshell osql C:\whatever.SQL 

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

Могу ли я что-нибудь добавить в свой внешний скрипт SQL, чтобы он работал быстрее? Может быть, что-то зафиксировать и освободить записи перед следующим циклом? Или я должен добавить какую-то паузу после каждого цикла?

Я хочу использовать внешний файл SQL, потому что он содержит много операторов обновления, и он более управляем для меня.

Вот как я делаю цикл:

Update dbFoo.dbo.tblBar set Processed = 0
Go

WHILE EXISTS ( SELECT ID FROM dbFoo.dbo.tblBar WHERE Processed = 0)
    BEGIN

        SELECT @aRow = MIN(tblBar.ID) FROM dbFoo.dbo.tblBar
        SELECT @aFoo1 = Foo1 FROM dbFoo.dbo.tblBar WHERE ID = @aRow
        SELECT @aFoo2 = Foo2 FROM dbFoo.dbo.tblBar WHERE ID = @aRow
        SELECT @aFoo3 = Foo3 FROM dbFoo.dbo.tblWhatever WHERE Foo = @aFoo

        EXEC RunPreAgg @Foo1 = @aFoo1, @Foo2 = @aFoo2, @Foo3 = @aFoo3, @RetVal = @aRetVal OUTPUT
        SELECT returning = @aRetVal

        UPDATE dbFoo.dbo.tblBar SET Processed = 1 WHERE ID = @aRow
    END

Тогда хранимая процедура RunPreAgg в основном делает это:

if db_id('db' + @Foo1 + '_' + @Foo2) is not null  
    BEGIN
        --This bat file creates the SQL File
        select @sql = 'master.dbo.xp_cmdshell '''+@path+'wwwRunPreAgg.bat ' + @Foo1 + ' ' + @Foo2 + ' ' + @Foo3 + ''''
        exec( @sql )

        --execute
        select @sql = 'master.dbo.xp_cmdshell ''osql -E -o '+@path+'output\tmp'+@Foo1+'_'+@Foo2+'.txt -i '+@path+'tmp' + @Foo1 + '.SQL'''
        exec( @sql )

        --This erases the SQL File
        select @sql = 'master.dbo.xp_cmdshell '''+@path+'wwwCleanup.bat ' + @Foo1 + ' ' + @Foo2 + ''''
        exec( @sql )

        Set @retval = 'Done!'
    END
ELSE
    BEGIN
        Set @retval = 'Err: No DataBase'
    END 

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

1 Ответ

0 голосов
/ 26 августа 2010

Если производительность циклов вызывает у вас проблемы, вы можете попытаться уменьшить количество выборок. Обычно я не люблю курсоры, но ваш цикл может выиграть от одного. Вы можете выбрать все значения, которые вам нужны для цикла, в память, а затем выполнить цикл по этим значениям, не выполняя 3 или 4 выбора за цикл (конечно, если снижение производительности происходит внутри RunPreAgg SP, тогда это не поможет) :

DECLARE cFoos CURSOR FOR 
    SELECT tblBar.ID, tblBar.Foo1, tblBar.Foo2, tblWhatever.Foo3
    FROM dbFoo.dbo.tblBar
        INNER JOIN dbFoo.dbo.tblWhatever
            ON tblWhatever.Foo = tblBar.Foo
    WHERE tblBar.Processed = 0;

OPEN cFoos;
FETCH NEXT FROM cFoos INTO @aRow, @aFoo1, @aFoo2, @aFoo3;

WHILE @@FETCH_STATUS = 0
BEGIN
    EXEC RunPreAgg @Foo1 = @aFoo1, @Foo2 = @aFoo2, @Foo3 = @aFoo3, @RetVal = @aRetVal OUTPUT
    SELECT returning = @aRetVal

    UPDATE dbFoo.dbo.tblBar SET Processed = 1 WHERE ID = @aRow

   FETCH NEXT FROM cFoos INTO @aRow, @Foo1, @Foo2, @Foo3;
END

CLOSE cFoos;
DEALLOCATE cFoos;
...