将SELECT查询拆分为多个Xml文件

时间:2017-02-09 15:31:06

标签: sql-server xml tsql

这是我的问题的简化版本。

使用以下SELECT语句,我将数据输出为Xml。

SET @result = ( 
    SELECT * FROM MyTable
    FOR XML path('receipts')
)

SELECT @result AS xmloutput

然后我将输出写入一个Xml文件,该文件使用文件名中的时间戳:

SET @sqlCommand =
     'bcp "EXEC '  +@db + '.dbo.MyStoreProcedure" queryout "' + @filePath + @fileName + ' "  -T -C 1252 -w'
EXEC master..xp_cmdshell @sqlCommand

在上面的代码段中:

  1. MyStoreProcedure基本上是第一个代码段中的代码,即SELECT结果。
  2. @filename的结构类似于YYYYMMDDHHMMSS_customers.xml
  3. 现在问题是,应该读取此Xml文件的程序限制为20000条记录。我应该做的是将结果拆分为单独的Xml文件。因此,如果原始SELECT查询提供25000条记录。它们应分成两个不重叠的文件:第一个包含20000,第二个包含剩余的5000。

    为了避免覆盖同一个文件(如果进程非常快),我们可以在每个批处理之间等待1秒钟,以便下一个文件获得一个新名称。

    如何实施此拆分?

    提前致谢。

2 个答案:

答案 0 :(得分:2)

function NTILE (Starting with SQL Server 2012)允许您分组您的数据。如果您的版本较低,则可以使用ROW_NUMBER整数除法的组合轻松模拟此项。

以下内容将表行与块号一起写入临时表。 WHILE在块之后占用大块。您可以使用chunk-number将其添加到文件名中。

function WAITFOR (again 2012+)允许自动暂停。

DECLARE @ApproxRowsPerChunk INT=10; --will float a little...

SELECT NTILE((SELECT Count(*) FROM sys.objects) / @ApproxRowsPerChunk) OVER(ORDER BY o.object_id) AS ChunkNumber
      ,* 
INTO #StagingTable
FROM sys.objects AS o;

DECLARE @nr INT=1;
DECLARE @maxNr INT=(SELECT MAX(ChunkNumber) FROM #StagingTable);

WHILE @nr<=@maxNr 
BEGIN
    SELECT * FROM #StagingTable WHERE ChunkNumber=@nr FOR XML PATH('test');
    SET @nr=@nr+1;
    WAITFOR DELAY '00:00:01';
END

提示

这将允许您将 1 of 17 之类的内容集成到XML中(如果需要,还可以集成到文件名中)。

答案 1 :(得分:1)

请查看以下内容是否有帮助。我基本上把你完成的myTable结果,分配row_number()并将它们划分为分区,然后根据批量大小遍历分区,在循环中执行批量复制。您可能还需要做一些有关动态设置文件名的工作(您可以在文件名构建器中使用@current_partition变量)。

我已经评论了我的代码:

-- Dummy data, represents your results set

IF OBJECT_ID('tempdb..#t1') IS NOT NULL
    DROP TABLE #t1

CREATE TABLE #t1 (
    initials VARCHAR(10)
    ,no_cars VARCHAR(10)
    )

INSERT INTO #t1
VALUES
('AA',1)
,('BB',1)
,('CC',1)
,('DD',1)
,('EE',1)
,('FF',1)
,('GG',1)
,('HH',1)
,('II',1)
,('JJ',1)
,('KK',1)

---- end of test data creation

-- Assign query partition size, in your case would be 20,000. Must be float (or other decimal).

DECLARE @partition_size FLOAT = 3;

IF OBJECT_ID('tempdb..#t2') IS NOT NULL
    DROP TABLE #t2;

-- Assign your results set a row number
WITH [cte1] AS (
SELECT
initials
,no_cars
,ROW_NUMBER() OVER (ORDER BY no_cars ASC) AS row_no
FROM #t1
)
-- Assign the query partition by running a ceiling command on the row number, store the results in a temp table
SELECT
initials
,no_cars
,CEILING(row_no/@partition_size) AS query_partition
INTO #t2
FROM cte1

--- Now, create a loop to go through each partition

-- Your business variables

DECLARE @result XML
DECLARE @sqlcommand NVARCHAR(4000)
DECLARE @db VARCHAR(50) = 'db'
DECLARE @filepath VARCHAR(50) = 'C:\temp'
DECLARE @filename VARCHAR(50) = 'dynamic2017010.xml'

-- Find highest partition
DECLARE @current_partition INT = 1
DECLARE @max_partition INT = (SELECT MAX(query_partition) FROM #t2)


WHILE @current_partition <= @max_partition
BEGIN

    SET @result = ( 
        SELECT initials
,no_cars FROM #t2
        WHERE query_partition = @current_partition
        FOR XML path('receipts')
    )
SELECT @result AS xmloutput

-- other code..?

    SET @sqlCommand =
         'bcp "EXEC '  +@db + '.dbo.MyStoreProcedure" queryout "' + @filePath + @fileName + ' "  -T -C 1252 -w'
    EXEC master..xp_cmdshell @sqlCommand

    SET @current_partition += 1

END