2011-01-18 4 views
2

Я пытаюсь подготовить некоторые данные для удаления третьим лицом, и, к сожалению, они могут обрабатывать данные только в партиях из 2000 записей. У меня есть 100 тыс. Записей и, возможно, придется разделить и экспортировать эти данные еще несколько раз, поэтому я хотел бы как-то автоматизировать процесс.Разбить запрос SQL Server 2008 пакетами

Есть ли простой способ сделать это с помощью SQL Server 2008? Я не выполняю сложный запрос - он не слишком далеко от SELECT PKID FROM Sometable ORDER BY PKID - и, хотя я могу, возможно, сделать это с помощью курсора, я хотел бы знать, есть ли лучший способ.

ответ

4
SET NOCOUNT ON; 

CREATE TABLE [dbo].[SyncAudit] (PkId INT, BatchNumber INT) 

DECLARE @batchsize INT 
    ,@rowcount INT 
    ,@batchcount INT 
    ,@rootdir VARCHAR(2048) 
    ,@saveas VARCHAR(2048) 
    ,@query VARCHAR(2048) 
    ,@bcpquery VARCHAR(2048) 
    ,@bcpconn VARCHAR(64) 
    ,@bcpdelim VARCHAR(2) 

SET  @rootdir = '\\SERVER1\SHARE1\FOLDER\' 
SET  @batchsize = 2000 
SET  @bcpdelim = '|' 
SET  @bcpconn = '-T' -- Trusted 
--SET  @bcpconn = '-U <username> -P <password>' -- SQL authentication 

SELECT @rowcount = COUNT(1), 
    @batchcount = CEILING(COUNT(1)/@batchsize) FROM <@TableName, string, 'SomeTable'> 

SELECT [BatchSize] = @BatchSize, [BatchCount] = @Batchcount 

INSERT INTO SyncAudit 
SELECT 
<@TableKey, string, 'PKField'> 
,groupnum = NTILE(@batchcount) OVER (ORDER BY <@TableKey, string, 'PKField'>) 
FROM 
<@TableName, string, 'SomeTable'> 

WHILE (@batchcount > 0) 
BEGIN 

SET @saveas = @rootdir + 'batchnumber-' + cast(@batchcount as varchar) + '.txt' 
SET @query = ' SELECT [<@TableName, string, 'SomeTable'>].* 
       FROM [' + db_name() + '].[dbo].[<@TableName, string, 'SomeTable'>] 
       JOIN [' + db_name() + '].[dbo].[SyncAudit] 
          ON [<@TableName, string, 'SomeTable'>].<@TableKey, string, 'PKField'> = [SyncAudit].PkId 
          AND [SyncAudit].BatchNumber = ' + cast(@batchcount as varchar) + '' 

SET @bcpquery = 'bcp "' + replace(@query, char(10), '') + '" QUERYOUT "' + @saveas + '" -c -t^' + @bcpdelim + ' ' + @bcpconn + ' -S ' + @@servername 
EXEC master..xp_cmdshell @bcpquery 

--EXEC (@query) 


SET @batchcount = @batchcount -1 
END 


DROP TABLE [dbo].[SyncAudit] -- or leave for reference 
+0

Очень интересно - тот факт, что он экспортирует напрямую в файл, особенно приятен. У меня еще не было возможности попробовать это, но я сделаю это сегодня днем. tyvm! –

4

Я думаю, вы можете использовать ROW_NUMBER, а затем использовать BETWEEN, чтобы указать диапазон строк, который вам нравится. В качестве альтернативы вы могли бы использовать PKID, если бы знали, что не было пробелов, или не интересовались пробелами.

, например.

SELECT ... 
FROM 
    (SELECT ... 
     ROW_NUMBER() OVER(ORDER BY PKID) as RowNum 
    FROM Sometable e 
    ) t 
WHERE RowNum BETWEEN @startRowIndex AND (@startRowIndex + @maximumRows) - 1 

Это часто используется для поиска подкатегория. 4GuysFromRolla имеют хороший article on it

+0

Да, это направление, в котором я собирался войти, в отсутствие более элегантного решения. Однако у меня нет большого опыта работы с ROW_NUMBER(), поэтому ваш пример по-прежнему весьма полезен. :) –

+1

Будет ли row_number() постепенно замедляться дальше в диапазоне, который он получает? Esp ближе к концу 100k записей – RichardTheKiwi

2

Вы могли бы работать из диапазонов в то время @@ ROWCOUNT цикла целевой строки требуется. Он может работать лучше, чем ROW_NUMBER(), который должен был бы продолжать нумерацию с самого начала.

declare @startid int 
declare @endid int 

-- get one range, these are efficient as they go over the PKID key by range 
select top(1) @startid = pkid from sometable order by pkid -- 1 key visited 
select top(2000) @endid = pkid from sometable order by pkid -- 2000 keys visited 
-- note: top 2000 may end up with the 514th id if that is the last one 

while @@ROWCOUNT > 0 
begin 
    insert otherdb.dbo.backupcopy 
    select * from sometable 
    where pkid between @startid and @endid 

    select top(1) @startid = pkid from sometable 
    WHERE pkid > @endid -- binary locate 
    order by pkid 

    select top(2000) @endid = pkid from sometable 
    WHERE pkid > @endid -- binary locate, then forward range lookup, max 2000 keys 
    order by pkid 
end 
+0

Это выглядит великолепно - я могу попробовать комбинировать его с сообщением Адама, чтобы экспортировать вывод непосредственно в текстовые файлы. Однако я не понимаю следующий комментарий: «note: top 2000 может закончиться с 514-м идентификатором, если это последний». Могли бы вы объяснить? –

+0

Даже если вы запрашиваете верх (2000), если есть только 514 записей (слева), то последняя запись - 514-я. Назначение в multirow select работает, назначая значение последней строки, поэтому @ endid = pkid присваивается идентификатору 514-й строки (последняя), если нет 2000 строк. – RichardTheKiwi

+0

ohh, я вижу - вы просто имеете в виду, что вы имеете дело с случаями, когда последняя партия не содержит ровно 2000 строк, не так ли? –

0

Я закончил с использованием комбинации подходов, предусмотренных cyberkiwi и Адама. Мне не нужно было использовать ROW_NUMBER только потому, что вместо этого я использовал столбец IDENTITY в виде данных table.

Вот отредактированная версия кода, который я использовал, - он работал как шарм. Еще раз спасибо всем за помощь!

use Testing 
GO 

SET NOCOUNT ON 

declare 
    @now datetime = GETDATE(), 
    @batchsize int = 2000, 
    @bcpTargetDir varchar(500) = '\\SomeServer\Upload\', 
    @csvQueryServer varchar(500) = '.\SQLExpress', 

    @rowcount integer, 
    @nowstring varchar(100), 
    @batch_id int, 
    @startid int, 
    @endid int, 
    @oidCSV varchar(max), 
    @csvQuery varchar(max), 
    @bcpFilename varchar(200), 
    @bcpQuery varchar(1000) 

declare @tblBatchRanges table (
    batch_id integer NOT NULL IDENTITY(1,1) PRIMARY KEY, 
    oid_start integer NOT NULL, 
    oid_end integer NOT NULL, 
    csvQuery varchar(max) 
) 

-- Create a unique timestamp-based string, which will be used to name the exported files. 
select @nowstring = CONVERT(varchar, @now, 112) + '-' + REPLACE(CONVERT(varchar, @now, 114), ':', '') 


-- 
select top(1) @startid = oid from Testing..MyObjectIds order by oid 
select top(@batchsize) @endid = oid from Testing..MyObjectIds order by oid 
select @rowcount = @@ROWCOUNT 

while (@rowcount > 0) begin 
    -- Create a CSV of all object IDs in the batch, using the STUFF() function (http://goo.gl/EyE8L). 
    select @csvQuery = 'select stuff((select distinct '','' + CAST(oid as varchar) from Testing..MyObjectIds where oid between ' + CAST(@startid as varchar) + ' and ' + CAST(@endid as varchar) + ' order by '','' + CAST(oid as varchar) for xml path('''')),1,1,'''')' 


    -- Log the info and get the batch ID. 
    insert into @tblBatchRanges (oid_start, oid_end, csvQuery) 
     values (@startid, @endid, @oidCSV, @csvQuery) 

    select @batch_id = @@IDENTITY 


    -- Advance @startid and @endid so that they point to the next batch 
    select top(1) @startid = oid 
     from Testing..MyObjectIds 
     where oid > @endid 
     order by oid 

    select top(@batchsize) @endid = oid 
     from Testing..MyObjectIds 
     where oid > @endid 
     order by oid 

    select @rowcount = @@ROWCOUNT 


    -- Export the current batch to a file. 
    select @bcpFilename = 'MyExport-' + @nowstring + '-' + cast(@batch_id as varchar) + '.txt' 
    select @bcpQuery = 'bcp "' + @csvQuery + '" QUERYOUT "' + @bcpTargetDir + @bcpFilename + '" -S ' + @csvQueryServer + ' -T -c' 
    exec master..xp_cmdshell @bcpquery 
end 

SET NOCOUNT OFF 


--Check all of the logged info. 
select oid_start, oid_end, csvQuery from @tblBatchRanges 
Смежные вопросы