自学内容网 自学内容网

随笔20241113 数据分组与期次累加计算功能实现

在金融或数据分析系统中,经常需要对一段时间的数据进行分组和聚合,并按照一定的时间规则来进行累加统计。本篇文章将详细介绍如何利用 VB.NET 实现数据分组和期次累加计算。本文的代码包含以下主要功能:

  1. 将日期字符串转换为 DateTime 类型。
  2. 确定日期属于哪个期次(当前期或过去10期)。
  3. 对数据进行分组,并在每个分组内按照期次对数据进行累加计算。

1. 日期转换函数:ConvertToDate

在数据处理中,日期通常以字符串格式存储。为了进行日期的比较和计算,我们首先需要将字符串转换为 DateTime 类型。ConvertToDate 函数负责将日期字符串(例如 "20241112")转换为 DateTime 类型。

代码实现:

' 将日期字符串(如20241112)转换为 DateTime
Function ConvertToDate(value As Object) As DateTime?
    Dim dateStr As String = value.ToString()
    ' 尝试将日期字符串解析为DateTime
    If DateTime.TryParseExact(dateStr, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, Nothing) Then
        Return DateTime.ParseExact(dateStr, "yyyyMMdd", CultureInfo.InvariantCulture)
    End If
    ' 如果解析失败,返回Nothing
    Return Nothing
End Function

功能说明:

  • 使用 DateTime.TryParseExact 按照指定的日期格式("yyyyMMdd")进行解析。如果解析成功,返回 DateTime 对象;如果解析失败,返回 Nothing
  • 这种设计可以确保数据的格式正确,并在数据格式不正确时避免抛出异常。

2. 期次判断函数:DeterminePeriod

在期次分析中,我们将每年的 7 月 1 日到次年的 6 月 30 日定义为一个期次。当前期定义为当年的 7 月 1 日到次年的 6 月 30 日,前一期则为上一个年度的同一时间段,以此类推。DeterminePeriod 函数用于判断给定的日期属于哪个期次。

代码实现:

' 根据传入的日期,判断它属于哪个期次(当前期或过去10期)
' 返回值为期次的编号,例如0表示当期,1表示前一期,以此类推
Function DeterminePeriod(dateValue As DateTime) As Integer
    Dim currentYear As Integer = DateTime.Now.Year
    ' 定义当期的起始日期和结束日期(每年7月1日到次年6月30日)
    Dim startCurrentPeriod As New DateTime(currentYear, 7, 1)
    Dim endCurrentPeriod As New DateTime(currentYear + 1, 6, 30)

    ' 遍历当前期和过去10个期次,检查传入的日期是否在某个期次范围内
    For i As Integer = 0 To 10
        Dim periodStart = startCurrentPeriod.AddYears(-i)
        Dim periodEnd = endCurrentPeriod.AddYears(-i)
        ' 如果日期在该期次范围内,则返回该期次编号
        If dateValue >= periodStart AndAlso dateValue <= periodEnd Then
            Return i ' 返回期次编号
        End If
    Next
    ' 如果日期不在任何期次范围内,返回-1表示不属于任何期次
    Return -1
End Function

功能说明:

  • 函数首先确定当前期的开始和结束日期,然后依次向前推算前 10 个期次。
  • 使用 For 循环判断传入日期是否在这些期次范围内。如果在某个期次范围内,则返回该期次的编号。
  • 返回值为整数,0 表示当前期,1 表示上一期,以此类推。

3. 数据分组和累加函数:ProcessData

ProcessData 函数是主处理函数,负责将数据按 PRODUCT_CDBRAND_CD 进行分组,并根据期次对数据进行累加。累加的具体字段由 calcType 参数决定。

代码实现:

' 主处理函数,用于根据条件对数据进行分组、累加并生成最终的DataTable
Function ProcessData(dataTable As DataTable, calcType As Integer) As DataTable
    ' 创建结果DataTable,并添加字段
    Dim resultTable As New DataTable()
    resultTable.Columns.Add("PRODUCT_CD", GetType(String))
    resultTable.Columns.Add("PRODUCT_NM", GetType(String))
    resultTable.Columns.Add("NM", GetType(String))
    resultTable.Columns.Add("BRAND_CD", GetType(String))
    resultTable.Columns.Add("KBN_NM", GetType(String))

    ' 添加期次字段 ZEN0KI 到 ZEN10KI
    For i As Integer = 0 To 10
        resultTable.Columns.Add("ZEN" & i & "KI", GetType(Integer))
    Next

    ' 对DataTable按 PRODUCT_CD 和 BRAND_CD 进行分组
    Dim groupedData = From row In dataTable.AsEnumerable()
                      Group row By key = Tuple.Create(row.Field(Of Integer)("PRODUCT_CD"), row.Field(Of Integer)("BRAND_CD")) Into Group

    ' 遍历每一组
    For Each group In groupedData
        ' 创建一个新的DataRow,用于存储该组的汇总数据
        Dim rowData As DataRow = resultTable.NewRow()
        Dim productCode = group.key.Item1
        Dim brandCode = group.key.Item2

        ' 填充该组的基本信息(产品编码、名称等)
        Dim firstRow As DataRow = group.Group.FirstOrDefault()
        rowData("PRODUCT_CD") = productCode
        rowData("PRODUCT_NM") = firstRow.Field(Of String)("PRODUCT_NM")
        rowData("NM") = firstRow.Field(Of String)("NM")
        rowData("BRAND_CD") = brandCode
        rowData("KBN_NM") = firstRow.Field(Of String)("KBN_NM")

        ' 创建数组 periodTotals 用于存储每个期次的累计值,初始化为0
        Dim periodTotals(10) As Integer

        ' 遍历当前组中的每一行,进行日期判断和累加
        For Each row As DataRow In group.Group
            ' 优先使用 MONTHLY_PROCESSING 作为日期,若为空则使用 SLIP_DT
            Dim monthlyProcessing = row("MONTHLY_PROCESSING")
            Dim slipDt = row("SLIP_DT")
            ' 转换日期字符串为DateTime
            Dim dateValue As DateTime? = If(monthlyProcessing IsNot DBNull.Value, ConvertToDate(monthlyProcessing), ConvertToDate(slipDt))

            ' 如果日期有效,则判断属于哪个期次
            If dateValue.HasValue Then
                Dim period = DeterminePeriod(dateValue.Value)
                ' 仅当日期属于某个期次时进行计算(期次为0到10)
                If period >= 0 AndAlso period <= 10 Then
                    ' 根据 calcType 判断计算内容(1表示 SALES_PRICE 总和,2表示 CAPACITY * SALES_QTY 总和)
                    Dim valueToAdd As Integer = If(calcType = 1, row.Field(Of Integer)("SALES_PRICE"), row.Field(Of Integer)("CAPACITY") * row.Field(Of Integer)("SALES_QTY"))
                    ' 将值累加到对应期次的总和中
                    periodTotals(period) += valueToAdd
                End If
            End If
        Next

        ' 将 periodTotals 数组中的累加值写入 DataRow 中
        For i As Integer = 0 To 10
            rowData("ZEN" & i & "KI") = periodTotals(i)
        Next

        ' 将当前汇总行添加到结果DataTable中
        resultTable.Rows.Add(rowData)
    Next

    ' 返回包含汇总结果的DataTable
    Return resultTable
End Function

功能说明:

  • 创建结果表结构:初始化一个新的 DataTable,并添加 PRODUCT_CDPRODUCT_NMBRAND_CD 等基本信息字段,以及期次字段 ZEN0KIZEN10KI

  • 数据分组:根据 PRODUCT_CDBRAND_CD 对原始数据进行分组。每个分组代表一个独特的产品和品牌组合。

  • 填充基本信息:对于每个分组,从第一条数据中提取 PRODUCT_CDPRODUCT_NMBRAND_CDKBN_NM 等基本信息填入结果行。

  • 期次累加计算:遍历分组中的每条数据,判断数据的日期属于哪个期次,然后根据 calcType 参数确定是累加 SALES_PRICE 还是 CAPACITY * SALES_QTY。累加结果存入对应期次的数组 periodTotals 中。

  • 写入累加结果:将 periodTotals 中每个期次的累加值写入结果表的对应字段。

  • **返回结果表

        最终全部代码


    ' 将日期字符串(如20241112)转换为 DateTime
    Function ConvertToDate(value As Object) As DateTime?
        Dim dateStr As String = value.ToString()
        ' 尝试将日期字符串解析为DateTime
        If DateTime.TryParseExact(dateStr, "yyyyMMdd", CultureInfo.InvariantCulture, DateTimeStyles.None, Nothing) Then
            Return DateTime.ParseExact(dateStr, "yyyyMMdd", CultureInfo.InvariantCulture)
        End If
        ' 如果解析失败,返回Nothing
        Return Nothing
    End Function

    ' 根据传入的日期,判断它属于哪个期次(当前期或过去10期)
    ' 返回值为期次的编号,例如0表示当期,1表示前一期,以此类推
    Function DeterminePeriod(dateValue As DateTime) As Integer
        Dim currentYear As Integer = DateTime.Now.Year
        ' 定义当期的起始日期和结束日期(每年7月1日到次年6月30日)
        Dim startCurrentPeriod As New DateTime(currentYear, 7, 1)
        Dim endCurrentPeriod As New DateTime(currentYear + 1, 6, 30)

        ' 遍历当前期和过去10个期次,检查传入的日期是否在某个期次范围内
        For i As Integer = 0 To 10
            Dim periodStart = startCurrentPeriod.AddYears(-i)
            Dim periodEnd = endCurrentPeriod.AddYears(-i)
            ' 如果日期在该期次范围内,则返回该期次编号
            If dateValue >= periodStart AndAlso dateValue <= periodEnd Then
                Return i ' 返回期次编号
            End If
        Next
        ' 如果日期不在任何期次范围内,返回-1表示不属于任何期次
        Return -1
    End Function

    ' 主处理函数,用于根据条件对数据进行分组、累加并生成最终的DataTable
    Function ProcessData(dataTable As DataTable, calcType As Integer) As DataTable
        ' 创建结果DataTable,并添加字段
        Dim resultTable As New DataTable()
        resultTable.Columns.Add("PRODUCT_CD", GetType(String))
        resultTable.Columns.Add("PRODUCT_NM", GetType(String))
        resultTable.Columns.Add("NM", GetType(String))
        resultTable.Columns.Add("BRAND_CD", GetType(String))
        resultTable.Columns.Add("KBN_NM", GetType(String))

        ' 添加期次字段 ZEN0KI 到 ZEN10KI
        For i As Integer = 0 To 10
            resultTable.Columns.Add("ZEN" & i & "KI", GetType(Integer))
        Next

        ' 对DataTable按 PRODUCT_CD 和 BRAND_CD 进行分组
        Dim groupedData = From row In dataTable.AsEnumerable()
                          Group row By key = Tuple.Create(row.Field(Of Integer)("PRODUCT_CD"), row.Field(Of Integer)("BRAND_CD")) Into Group

        ' 遍历每一组
        For Each group In groupedData
            ' 创建一个新的DataRow,用于存储该组的汇总数据
            Dim rowData As DataRow = resultTable.NewRow()
            Dim productCode = group.key.Item1
            Dim brandCode = group.key.Item2

            ' 填充该组的基本信息(产品编码、名称等)
            Dim firstRow As DataRow = group.Group.FirstOrDefault()
            rowData("PRODUCT_CD") = productCode
            rowData("PRODUCT_NM") = firstRow.Field(Of String)("PRODUCT_NM")
            rowData("NM") = firstRow.Field(Of String)("NM")
            rowData("BRAND_CD") = brandCode
            rowData("KBN_NM") = firstRow.Field(Of String)("KBN_NM")

            ' 创建数组 periodTotals 用于存储每个期次的累计值,初始化为0
            Dim periodTotals(10) As Integer

            ' 遍历当前组中的每一行,进行日期判断和累加
            For Each row As DataRow In group.Group
                ' 优先使用 MONTHLY_PROCESSING 作为日期,若为空则使用 SLIP_DT
                Dim monthlyProcessing = row("MONTHLY_PROCESSING")
                Dim slipDt = row("SLIP_DT")
                ' 转换日期字符串为DateTime
                Dim dateValue As DateTime? = If(monthlyProcessing IsNot DBNull.Value, ConvertToDate(monthlyProcessing), ConvertToDate(slipDt))

                ' 如果日期有效,则判断属于哪个期次
                If dateValue.HasValue Then
                    Dim period = DeterminePeriod(dateValue.Value)
                    ' 仅当日期属于某个期次时进行计算(期次为0到10)
                    If period >= 0 AndAlso period <= 10 Then
                        ' 根据 calcType 判断计算内容(1表示 SALES_PRICE 总和,2表示 CAPACITY * SALES_QTY 总和)
                        Dim valueToAdd As Integer = If(calcType = 1, row.Field(Of Integer)("SALES_PRICE"), row.Field(Of Integer)("CAPACITY") * row.Field(Of Integer)("SALES_QTY"))
                        ' 将值累加到对应期次的总和中
                        periodTotals(period) += valueToAdd
                    End If
                End If
            Next

            ' 将 periodTotals 数组中的累加值写入 DataRow 中
            For i As Integer = 0 To 10
                rowData("ZEN" & i & "KI") = periodTotals(i)
            Next

            ' 将当前汇总行添加到结果DataTable中
            resultTable.Rows.Add(rowData)
        Next

        ' 返回包含汇总结果的DataTable
        Return resultTable
    End Function

调用端

        ProcessData(dataTable, 1)
        ProcessData(dataTable, 2)

原文地址:https://blog.csdn.net/2301_79992621/article/details/143754659

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!