You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

SSIS脚本任务中基于Test File Name字段去重并保留最早记录

SSIS脚本任务C#处理CSV去重:按Test File Name保留排序后第一条记录

针对你的需求,结合你熟悉SQL的背景,下面是一套在SSIS脚本任务中可用的C#实现方案,核心思路和SQL里「先排序再取每组TOP 1」的逻辑一致:

核心思路

  1. 用TextFieldParser读取CSV(比直接读行更可靠,能处理带引号的字段)
  2. 将每行数据映射为实体类,方便后续Linq操作(对应SQL里的表结构)
  3. 按Date Time字段排序(对应SQL的ORDER BY Date Time)
  4. 按Test File Name分组,取每组第一条记录(对应SQL的ROW_NUMBER() OVER(PARTITION BY Test File Name ORDER BY Date Time) = 1)
  5. 将结果写入新CSV文件

完整代码实现

在SSIS中添加脚本任务,选择C#作为脚本语言,替换Main方法内的代码如下:

using System;
using System.IO;
using System.Linq;
using Microsoft.VisualBasic.FileIO;
using System.Globalization;

namespace ST_XXXXXXXXX // 保留脚本任务自动生成的命名空间即可
{
    public class ScriptMain : UserComponent
    {
        public override void Main()
        {
            // 替换为你的源CSV路径和目标CSV路径,也可以用SSIS变量传递(见下方说明)
            string sourceCsvPath = @"C:\YourSourceFile.csv";
            string targetCsvPath = @"C:\YourTargetFile.csv";

            // 定义CSV行的实体类,根据你的实际列名/类型调整
            public class CsvRecord
            {
                public string TestFileName { get; set; }
                public DateTime DateTime { get; set; }
                // 添加CSV中的其他列示例:
                // public string DeviceID { get; set; }
                // public decimal ResultValue { get; set; }
            }

            try
            {
                var csvRecords = new System.Collections.Generic.List<CsvRecord>();
                using (TextFieldParser parser = new TextFieldParser(sourceCsvPath))
                {
                    parser.TextFieldType = FieldType.Delimited;
                    parser.SetDelimiters(","); // 若CSV用其他分隔符(如制表符\t),此处修改
                    parser.HasFieldsEnclosedInQuotes = true; // 处理带引号的字段

                    // 读取并跳过表头(如需复用表头可保存后写入目标文件)
                    string[] headers = parser.ReadFields();

                    // 逐行读取解析数据
                    while (!parser.EndOfData)
                    {
                        string[] fields = parser.ReadFields();
                        if (fields == null || fields.Length < 2) continue; // 跳过无效行

                        // 解析日期:格式需与CSV中Date Time字段完全匹配,如"MM/dd/yyyy hh:mm tt"
                        if (DateTime.TryParseExact(fields[1], "yyyy-MM-dd HH:mm:ss", CultureInfo.InvariantCulture, DateTimeStyles.None, out DateTime dateTime))
                        {
                            csvRecords.Add(new CsvRecord
                            {
                                TestFileName = fields[0], // 假设Test File Name是第1列(索引0)
                                DateTime = dateTime,
                                // 映射其他列示例:
                                // DeviceID = fields[2],
                                // ResultValue = decimal.Parse(fields[3])
                            });
                        }
                    }
                }

                // 先按Date Time升序排序,再按TestFileName分组取第一条(取最新则用OrderByDescending)
                var uniqueRecords = csvRecords
                    .OrderBy(r => r.DateTime)
                    .GroupBy(r => r.TestFileName)
                    .Select(g => g.First())
                    .ToList();

                // 写入新CSV文件
                using (StreamWriter writer = new StreamWriter(targetCsvPath))
                {
                    // 写入表头(根据你的实际列名调整)
                    writer.WriteLine("Test File Name,Date Time,DeviceID,ResultValue");

                    foreach (var record in uniqueRecords)
                    {
                        // 处理带逗号的字段,用引号包裹避免格式错误
                        string line = $"\"{record.TestFileName}\",{record.DateTime.ToString("yyyy-MM-dd HH:mm:ss")},\"{record.DeviceID}\",{record.ResultValue}";
                        writer.WriteLine(line);
                    }
                }

                Dts.TaskResult = (int)ScriptResults.Success;
            }
            catch (Exception ex)
            {
                // 将错误信息输出到SSIS日志
                Dts.Events.FireError(0, "CSV处理失败", ex.Message + "\n" + ex.StackTrace, string.Empty, 0);
                Dts.TaskResult = (int)ScriptResults.Failure;
            }
        }
    }
}

关键注意事项

  1. 实体类适配:根据CSV的实际列名和数据类型,修改CsvRecord类的属性,确保一一对应。
  2. 日期格式匹配:TryParseExact中的日期格式必须和CSV里Date Time字段的格式完全一致,否则会解析失败。
  3. SSIS变量传递:如果不想硬编码路径,可在脚本任务的「ReadOnlyVariables」中添加路径变量,通过Dts.Variables["User::SourceCsvPath"].Value.ToString()获取值。
  4. 分组排序顺序:必须先排序再分组,否则分组后的记录顺序无法保证,这也是你之前GroupBy方法失败的核心原因——SQL里的PARTITION BY是基于排序后的结果,C#中也要遵循「先排序、再分组取首条」的逻辑。

内容的提问来源于stack exchange,提问作者Amanda

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 02:33:10