1

我正在使用平面文件目标将我的数据导出到文本文件。我需要将输出文件大小限制为 1 MB。可能吗?

4

3 回答 3

1

在 SSIS 中没有任何开箱即用的东西可以做到这一点。但是,编写一个可以充当数据流目标并限制其输出文件大小的脚本转换相对简单。根据需要添加一些代码来创建多个文件(每个文件都小于指定的大小)来存储所有结果数据并不难。

例如,假设您的源查询是

SELECT 
    TABLE_CATALOG,
    TABLE_SCHEMA,
    TABLE_NAME,
    COLUMN_NAME,
    ORDINAL_POSITION,
    COLUMN_DEFAULT,
    IS_NULLABLE,
    DATA_TYPE,
    CHARACTER_MAXIMUM_LENGTH 
FROM INFORMATION_SCHEMA.COLUMNS

并且您正在尝试将其写入一个或多个 CSV 文件,而不超过每个文件的特定大小。

定义三个包级变量如下:

  • User::TargetFolder(String包含您要写入的文件夹名称)
  • User::TargetFileNamePattern(String带有输出文件的命名模式;例如SampleOutput{0}.csv
  • User::MaxFileLength(Int32包含每个文件的最大字符数)

像这样创建数据流:

数据流截图

并因此对脚本转换进行编码:

/* Microsoft SQL Server Integration Services Script Component
*  Write scripts using Microsoft Visual C# 2008.
*  ScriptMain is the entry point class of the script.*/

using System;
using System.Data;
using System.IO;
using System.Text;
using Microsoft.SqlServer.Dts.Pipeline.Wrapper;
using Microsoft.SqlServer.Dts.Runtime.Wrapper;

[Microsoft.SqlServer.Dts.Pipeline.SSISScriptComponentEntryPointAttribute]
public class ScriptMain : UserComponent
{
    private int _fileCounter;
    private int _bytesWritten;
    private TextWriter _tw;

    private TextWriter CurrentWriter
    {
        get
        {
            if (_tw == null)
            {
                string fileName = String.Format(this.Variables.TargetFileNamePattern, _fileCounter);
                string filePath = Path.Combine(this.Variables.TargetFolder, fileName);
                _tw = File.CreateText(filePath);
            }
            return _tw;
        }
    }

    public override void PreExecute()
    {
        base.PreExecute();
        _fileCounter = 1;
        _bytesWritten = 0;
        _tw = null;
    }

    public override void PostExecute()
    {
        base.PostExecute();
        if (_tw != null)
        {
            _tw.Flush();
            _tw.Close();
        }
    }

    public override void Input0_ProcessInputRow(Input0Buffer Row)
    {
        string thisLine = String.Format(
                "{0},{1},{2},{3},{4},{5},{6},{7},{8}",
                Row.TABLECATALOG,
                Row.TABLESCHEMA,
                Row.TABLENAME,
                Row.COLUMNNAME,
                Row.ORDINALPOSITION,
                Row.COLUMNDEFAULT_IsNull ? "NULL" : Row.COLUMNDEFAULT,
                Row.ISNULLABLE,
                Row.DATATYPE,
                Row.CHARACTERMAXIMUMLENGTH_IsNull ? "NULL" : Row.CHARACTERMAXIMUMLENGTH.ToString());
        if (_bytesWritten + thisLine.Length > this.Variables.MaxFileLength) 
        {
            _tw.Flush();
            _tw.Close();
            _tw = null;
            _fileCounter += 1;
            _bytesWritten = 0;
        }
        this.CurrentWriter.WriteLine(thisLine);
        _bytesWritten += thisLine.Length;
    }
}

对于源查询中的每一行,这将构建要写入的字符串,然后检查将该字符串添加到当前TextWriter是否会导致文件太大。如果是这种情况,则将当前文件刷新到磁盘并关闭;下一次调用将按顺序为下一个文件this.CurrentWriter创建一个新对象。TextWriter

于 2013-02-27T19:58:59.300 回答
0

如果数据将超过文件大小,您必须决定要省略哪些数据。这是只有您或您的客户才能决定的事情。

然后编写查询以仅返回所需的数据。

于 2013-02-27T14:18:26.433 回答
0

一些想法:

使用脚本转换,您将生成具有逻辑“获取下一行”方法的生成类 - 在该类上添加一个计数器,并在每一行上计算出该行数据的大小并添加它到柜台。如果计数器的行数超过 1mb,则不要将该行放入输出缓冲区。

在控制流上使用脚本组件,调用 .Net IO 方法来读取文件的 1mb 并只写回(这确实意味着您正在创建一个大文件然后缩小它)。

真的,SSIS 不会做好这两件事。如果是我,我会采用“SELECT TOP X”方法。

于 2013-02-27T16:48:40.953 回答