随笔20241113 数据分组与期次累加计算功能实现
在金融或数据分析系统中,经常需要对一段时间的数据进行分组和聚合,并按照一定的时间规则来进行累加统计。本篇文章将详细介绍如何利用 VB.NET 实现数据分组和期次累加计算。本文的代码包含以下主要功能:
- 将日期字符串转换为
DateTime
类型。 - 确定日期属于哪个期次(当前期或过去10期)。
- 对数据进行分组,并在每个分组内按照期次对数据进行累加计算。
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_CD
和 BRAND_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_CD
、PRODUCT_NM
、BRAND_CD
等基本信息字段,以及期次字段ZEN0KI
到ZEN10KI
。 -
数据分组:根据
PRODUCT_CD
和BRAND_CD
对原始数据进行分组。每个分组代表一个独特的产品和品牌组合。 -
填充基本信息:对于每个分组,从第一条数据中提取
PRODUCT_CD
、PRODUCT_NM
、BRAND_CD
和KBN_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)!