【问题标题】:Exporting SQL Server Table to multiple part files将 SQL Server 表导出到多个零件文件
【发布时间】:2013-07-12 00:26:51
【问题描述】:

我需要将一个相当大的 SQL Server 表 ~100GB 导出到一个 CSV 文件。但输出不是单个 csv 文件,理想情况下应该是多个文件,例如 10 个文件,每个 10GB。

我看到 BCP 有一个 batch_size 参数,但这仍然会将所有数据写入单个文件?是否有其他免费实用程序可以满足我的要求?可以在哪里以字节或行数指定文件的大小?

就上下文而言,这是为了让数据可以与 Hive/Hadoop 平台中的其他来源相结合,所以如果有更好的方法来导出数据,我愿意提供建议。

【问题讨论】:

  • 您使用的是哪个版本的 SQL Server?

标签: sql sql-server database hive bcp


【解决方案1】:

我会先导出文件,然后再向外分割。假设您在 Windows 机器上运行,有几个“免费软件”工具可以提供帮助。有关详细信息,请参阅超级用户上的 this other answer

【讨论】:

  • 在外部进行拆分/划分是否存在将一行数据拆分为两个文件的风险?如果您要提取然后摄取,那会很好,但是,我认为如果您想利用可扩展的导入,您的摄取将单独处理每个文件并单独提取和导入,因此在 zip 期间拆分可能会放置一行数据在两个文件中,从而破坏了您的可扩展摄取?
【解决方案2】:

我认为您可以将 SQL 2012 的分页函数 OFFSETFETCH 与 bcp 结合使用:

SELECT *
FROM Table
ORDER BY ID --Primary Key
OFFSET 100000001 ROWS
FETCH NEXT 100000000 ROWS ONLY

【讨论】:

    【解决方案3】:

    很遗憾,BCP 的 batch_size 参数不控制输出。

    我进行这种拆分的方式:

    1 - 简单但不可重复:创建一个命令文件 (.cmd),在表上针对特定行范围运行多个 BCPs。这可能需要在表上使用基于 IDENTITY(1,1) 的主键。

    bcp "SELECT * FROM MyTable WHERE Id BETWEEN 0 AND 10000000" queryout …  
    bcp "SELECT * FROM MyTable WHERE Id BETWEEN 10000000 AND 20000000" queryout …   
    

    2 - 简单且可重复,使用大量磁盘:BCP 将整个表放到一个文件中,并使用split 创建所需数量的新文件每个字节中的字节数(注意:按行分割是一个更好的主意 IMO)。使用“Cygwin”(GnuWin32 不再维护)安装split 和任何其他您想要的实用程序。

     bcp MyDb.MySchema.MyTable out C:\MyFile.csv -T -w  
     split -b 10737418240 C:\MyFile.csv C:\MySplitFile_  
    

    生成以下文件

     C:\MySplitFile_aaa
     C:\MySplitFile_aab
     …
    

    3 - 复杂但可重复,可能需要不安全的 T-SQL:使用 xp_cmdshell 函数在遍历表的存储过程中调用 BCP。

     DECLARE @loop AS INT;   
     --Use WHILE to loop as needed--   
     DECLARE @sql AS VARCHAR(MAX);   
     --Add code to generate dynamic SQL here--   
     DECLARE @bcp AS VARCHAR(MAX);   
     SELECT @bcp='BCP "'+@sql+'" queryout C:\MyFolder\MyFile_'+@loop+'.csv';   
    

    最后说明:如果您在数据中使用任何 NVARCHAR 字段,则需要使用 -w 标志并注意输出将采用 UTF-16LE 格式。我会强烈建议使用iconv(再次来自'Cygwin')将其转换为UTF-8,然后再尝试在Hadoop中对其进行任何操作。

    【讨论】:

      【解决方案4】:

      如果您有一个可排序的主键字段,您可以找到定义所需行边界的键,然后使用定义边界的 WHERE 选择记录。

      这类似于 Joe 提出的 #1,但您的键不必是连续的,也不必是数字的。下面是一个简单的例子:

      DECLARE @maxrowsperfile AS bigint = 1048576
      DECLARE boundaries CURSOR FOR
          SELECT the_sortable_key
          FROM
          (
              SELECT
                  the_sortable_key
                  , ROW_NUMBER() OVER(ORDER BY the_sortable_key) AS the_row_number
              FROM the_table
          ) AS t
          WHERE the_row_number % @maxrowsperfile = 0
      
      OPEN boundaries
      
      DECLARE @lowerbound AS [key type] = [value A]
      DECLARE @upperbound AS [key type] = [value A]
      
      FETCH NEXT FROM boundaries
      INTO @upperbound
      
      IF @lowerbound = @upperbound
          PRINT 'bcp "SELECT * FROM the_table" queryout file -w -T'
      ELSE
          DECLARE @filecount AS int = 1
          BEGIN
              WHILE @@FETCH_STATUS = 0
              BEGIN
                  PRINT 'bcp "SELECT * FROM the_table WHERE key > ' + CAST(@lowerbound AS varchar) + ' AND key <= ' + CAST(@upperbound AS varchar) + ' queryout file_' + CAST(@filecount AS varchar) + ' -w -T'
                  SET @filecount = @filecount + 1
                  SET @lowerbound = @upperbound
                  FETCH NEXT FROM boundaries
                  INTO @upperbound
              END
              PRINT 'bcp "SELECT * FROM table WHERE key > ' + CAST(@lowerbound AS varchar) + ' queryout file_' + CAST(@filecount AS varchar) + ' -w -T'
      
          END
      CLOSE boundaries
      DEALLOCATE boundaries
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-03-08
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多