自动增加SSIS包的文件名

时间:2017-07-27 15:38:07

标签: sql ssis sql-server-2012

我是新来的。我有关于SSIS和动态文件名的问题。我发现了一篇文章可以通过附加日期来帮助增加文件名,但实际上我需要保持一个滚动的文件名计数器为001-999,并且每次创建文件时都要重新开始001。这是我评论过的文章:

https://www.red-gate.com/simple-talk/sql/ssis/passing-variables-to-and-from-an-ssis-task/

例如,我的第一个文件是“P12345001”,其中“001”是文件的第一个实例。下次SSIS包运行时,我希望文件名为“P12345002”,依此类推,直到它变为“P12345999”,下一个文件将返回“P12345001”。

这在SQL或SSIS中是否可行,或者我是否需要通过VB.NET或C#等语言编写脚本(我只有有限的经验)?

如果有帮助,我在MS SQL Server 2012上运行SSIS包。如果我已经包含所有相关信息,请告诉我。非常感谢提前!

1 个答案:

答案 0 :(得分:5)

SQL Work

虽然这在语法上对SQL Server是正确的,但您可以使用与您喜欢的任何RDBMS相同的概念,只需根据需要进行修改即可。

数据存储

要解决此问题,您需要存储以前运行的值的位置。 出现表格的键值类型足以满足您的需求。

-- Simple table structure
CREATE TABLE dbo.FileExecutionHistory
(
    BaseFileName varchar(50) NOT NULL
,   LastSequence int NOT NULL 
,   CONSTRAINT PK_dbo_FileExecutionHistory PRIMARY KEY
    (
        BaseFileName
    )
);

您将存储文件名(P12345)和最后使用的序列号(1)。

数据访问

在SSIS中,您将文件名传递给此查询。请注意?的使用 - 这将是基于序数的查询参数化的OLE DB提供程序的语法。

内部查询最多生成2行。用于此文件名的最后一个序列(如果找到)和0(未找到)。外部查询使用TOP(1)参数仅允许生成一行,并按降序值排序 - 从而确保我们只获取最大值。

我们使用模数%运算符和999将允许的值域限制为0到998.

但是,由于你需要1到999,我们然后在结果值中加1。

-- Access pattern
SELECT TOP(1)
    (D.LastSequence % 999) + 1  AS LastSequence
FROM
(
    SELECT
        FEH.LastSequence
    FROM
        dbo.FileExecutionHistory AS FEH
    WHERE
        FEH.BaseFileName = ?
    -- Handle the does not exist case
    UNION
    SELECT
        CAST(0 AS int)
) D
ORDER BY
    D.LastSequence DESC;

数据存储

同样,我们会看到?正在播放,但这次我们将它用于我们的文件名和序列值。

由于OLE DB提供程序是基于序数的,我们需要使用四个占位符并映射到"相同的"变量两次。这很傻,所以我先预先声明一个SQL Server变量并分配给它们。为了保持一致性,您也可以将其应用于数据访问模式。

Merge语句结果笨拙且不确定,因此我使用经过测试的真实更新/插入模式。我在表格中更新了该文件中的值,如果它不存在则该文件将不匹配。然后我尝试在我们的表中插入一个值,如果它还不存在的话。

最后两个语句中只有一个会执行一个动作,所以这很好。另外,我们有主键约束,可防止文件名重复,因此这是双加好的。

-- Storage pattern
DECLARE
    @BaseFileName varchar(50) = ?
,   @LastSequence int = ?;

UPDATE
    FEH
SET
    LastSequence = @LastSequence
FROM
    dbo.FileExecutionHistory AS FEH
WHERE
    FEH.BaseFileName = @BaseFileName;

INSERT INTO
    dbo.FileExecutionHistory
(
    BaseFileName
,   LastSequence
)
SELECT
    D.BaseFileName
,   D.LastSequence
FROM
(
    VALUES (@BaseFileName, @LastSequence)
) D (BaseFileName, LastSequence)
    LEFT OUTER JOIN
        dbo.FileExecutionHistory AS FEH
        ON FEH.BaseFileName = D.BaseFileName
WHERE
    FEH.BaseFileName IS NULL;

SSIS工作

将工作分解成小块并使用SSIS为您提供的内容。在这种方法中,我们将使用变量来完成大部分繁重的工作。

变量

您正在查看包含一些明显变量的包。一个变量用于保存序列号。一个变量用于保存基本文件名。我假设完整的文件名可能就像P12345001.txt一样,所以你也想跟踪那些东西。

  • User :: BaseFileName String - > P12345
  • User :: SequenceNumber Int - > 0
  • User :: FileExtension String - > .TXT
  • User :: CurrentFolder String - > C:\ ssisdata \输出

  • User :: SequencePad String> 001

  • User :: FullFileName String> P12345001.txt
  • User :: CurrentFileName String> C:\ ssisdata \输出\ P12345001.txt

前4个变量将是"正常"。最后3个将包含表达式。表达式是简单的公式,就像您在Excel中看到的一样。请注意,我为序列号指定了值0。在您的方案中不允许这样做,但确保拼图的所有部分都按预期工作将对您有所帮助。

SequencePad的表达式将使用一种经典的技术,在我们的值的左边填充N个字符,然后将正确的N个字符切出。对于像999这样的值,将没有净结果。对于1,我们最终会得到001.因为我们正在处理SequenceNumber的数值,所以我们必须先将它转换为字符串,然后再将+与三重零连接。

RIGHT("000" + (DT_WSTR, 3) @[User::SequenceNumber], 3)

FullFileName简单地将3个变量连接在一起。两个"硬编码"值和我们在上面构建的填充序列号。

@[User::BaseFileName] + @[User::SequencePad] + @[User::FileExtension]

最后,CurrentFileName的构建方式与FullFileName相同。我们要将更多变量连接在一起。所有这些小步骤的原因是,这是调试表达式的唯一方法。没有能力在它们上面设置断点。因此,使它们变小并将它们组合在一起。

唯一的"技巧"要注意这里斜杠\几乎也是几乎所有语言中的转义字符,所以如果你想使用\,你真的需要使用\\

@[User::CurrentFolder] + "\\" + @[User::FullFileName]

此时,您可以测试为前4个变量输入不同的值,并确保表达的值看起来符合预期。如果您不需要文件扩展名,只需将该值留空即可 - 表达式将正常工作。

平面文件连接管理器

根据文件P12345001.txt

的结构创建一个平面文件连接管理器

现在您已定义了变量,您将需要将此属性用作平面文件连接管理器的连接字符串属性的一部分。 See herehere用于设置属性。我将此称为Output

Ole DB连接管理器

您需要一个OLE DB连接管理器,指向我们创建表的SQL服务器和数据库(在顶部)。我假设它被称为Config

执行SQL 1

此任务的目的是将值分配给@ [User :: SequenceNumber]。将执行SQL任务拖到画布上,并按照我在this answer中显示的内容进行配置。对于我们的连接,我们将使用Config。 SQL语句是我在上面定义的数据访问查询。在结果集选项卡中,我们将指定User :: SequenceNumber

运行你的包裹,如果它没有爆炸,我们可以认为事情进展顺利。

数据流任务

在某个地方,不知何故,您正在使用计算的文件名,我假设您要查询某个表并将行推送到该文件中。您的数据流看起来像连接到平面文件目标的OLE DB源组件。 Biml

的例子是here

BimlExpress

执行SQL 2

现在我们的数据流已经完成,我们需要使用我们使用的序列号更新原始表。

这将是另一个执行SQL任务,使用上面的第三个查询,没有定义结果集。我们将变量@ [User :: FileName]和@ [User :: SequenceNumber](按此顺序)映射为参数0和1.

BIML

Business Intelligence Markup Lanaguage,Biml,可以被认为是商业智能的操作系统。它有一些部分可以处理您需要描述的所有方面以完成工作。在这里,我用它来描述具有自动增量文件名的SSIS包的样子。为了让你的工作

  1. 下载并安装{{3}}
  2. 打开一个SSIS项目并添加一个新的Biml文件。将下面的内容粘贴到其中
  3. 编辑第11行和第12行以指向计算机上的有效SQL Server和文件夹
  4. 右键单击BimlScript.biml并选择Generate SSIS Package
  5. 你的项目现在应该有一个名为so_45355289.dtsx的SSIS包 - 运行那个sucker
  6. 假设一切正常,您可以挖掘修改提供的Biml以满足您的需求(更改查询以实际提取您关心的数据并使平面文件格式匹配)。或者你可以修改现有的SSIS包,暂时忘掉Biml。最终你可能会关心它,因为它节省了大量时间。

    <Biml xmlns="http://schemas.varigence.com/biml.xsd">
        <FileFormats>
            <FlatFileFormat Name="FF Output">
                <Columns>
                    <Column Name="DatabaseName" Length="128" Delimiter="Tab" />
                    <Column Name="DatabaseId" DataType="Int32" Delimiter="CRLF" />
                </Columns>
            </FlatFileFormat>
        </FileFormats>
        <Connections>
            <OleDbConnection Name="Config" ConnectionString="Provider=SQLNCLI11;Server=localhost\DEV2016;Initial Catalog=tempdb;Integrated Security=SSPI;"  />
            <FlatFileConnection Name="Output" FileFormat="FF Output" FilePath="C:\ssisdata\output\P12345.txt"  />
        </Connections>
        <Packages>
            <Package Name="so_45355289" ConstraintMode="Linear">
                <Connections>
                    <Connection ConnectionName="Output">
                        <Expressions>
                            <Expression ExternalProperty="ConnectionString">@[User::CurrentFileName]</Expression>
                        </Expressions>
                    </Connection>
                </Connections>
                <Variables>
                    <Variable Name="BaseFileName" DataType="String">P12345</Variable>
                    <Variable Name="SequenceNumber" DataType="Int32">0</Variable>
                    <Variable Name="FileExtension" DataType="String">.txt</Variable>
                    <Variable Name="CurrentFolder" DataType="String">C:\ssisdata\Output</Variable>
                    <Variable Name="SequencePad" DataType="String" EvaluateAsExpression="true">RIGHT("000" + (DT_WSTR, 3) @[User::SequenceNumber], 3)</Variable>
                    <Variable Name="FullFileName" DataType="String" EvaluateAsExpression="true">@[User::BaseFileName] + @[User::SequencePad] + @[User::FileExtension]</Variable>
                    <Variable Name="CurrentFileName" DataType="String" EvaluateAsExpression="true">@[User::CurrentFolder] + "\\" + @[User::FullFileName]</Variable>
                </Variables>
                <Tasks>
                    <ExecuteSQL Name="SQL Get Current Sequence" ConnectionName="Config" ResultSet="SingleRow">
                        <DirectInput><![CDATA[SELECT TOP(1)
        (D.LastSequence % 999) + 1  AS LastSequence
    FROM
    (
        SELECT
            FEH.LastSequence
        FROM
            dbo.FileExecutionHistory AS FEH
        WHERE
            FEH.BaseFileName = ?
        -- Handle the does not exist case
        UNION
        SELECT
            CAST(0 AS int)
    ) D
    ORDER BY
        D.LastSequence DESC;]]></DirectInput>
                        <Parameters>
                            <Parameter Name="0" DataType="String" Length="128" VariableName="User.BaseFileName" />
                        </Parameters>
                        <Results>
                            <Result Name="0" VariableName="User.SequenceNumber" />
                        </Results>
                    </ExecuteSQL>
                    <Dataflow Name="DFT Generate data">
                        <Transformations>
                            <OleDbSource Name="OLESRC Query" ConnectionName="Config">
                                <DirectInput>SELECT name AS DatabaseName, database_id AS DatabaseId FROM sys.databases;</DirectInput>
                            </OleDbSource>
                            <FlatFileDestination Name="FFDST Output" ConnectionName="Output" Overwrite="true">
                            </FlatFileDestination>
                        </Transformations>
                    </Dataflow>
                    <ExecuteSQL Name="SQL Store Sequence" ConnectionName="Config">
                        <DirectInput><![CDATA[-- Storage pattern
    DECLARE
        @BaseFileName varchar(50) = ?
    ,   @LastSequence int = ?;
    
    UPDATE
        FEH
    SET
        LastSequence = @LastSequence
    FROM
        dbo.FileExecutionHistory AS FEH
    WHERE
        FEH.BaseFileName = @BaseFileName;
    
    INSERT INTO
        dbo.FileExecutionHistory
    (
        BaseFileName
    ,   LastSequence
    )
    SELECT
        D.BaseFileName
    ,   D.LastSequence
    FROM
    (
        VALUES (@BaseFileName, @LastSequence)
    ) D (BaseFileName, LastSequence)
        LEFT OUTER JOIN
            dbo.FileExecutionHistory AS FEH
            ON FEH.BaseFileName = D.BaseFileName
    WHERE
        FEH.BaseFileName IS NULL;]]></DirectInput>
                        <Parameters>
                            <Parameter Name="0" DataType="String" Length="128" VariableName="User.BaseFileName" />
                            <Parameter Name="1" DataType="Int32" VariableName="User.SequenceNumber" />
                        </Parameters>
                    </ExecuteSQL>
                </Tasks>
            </Package>
        </Packages>
    </Biml>