从excel导入数据并合并

时间:2015-04-05 21:35:45

标签: vb.net excel

我正在使用以下代码从excel文件中读取并将某些列添加到listview中。导入后我将其导出为CSV(代码未显示)。

我的问题是excel文件是一个摘要文件,它按事务显示数据,导致数千行。我想基于EPoS线执行相当于SUMIF的excel,并在可能的情况下合并信息?

以下数据样本......

Sample Info

    Public Structure ExcelRows
    Dim Unit As String
    Dim Outlet As String
    Dim EPoS As String
    Dim Quantity As String
    Dim Value As String
    Dim DateSale As String

End Structure
Public ExcelRowList As List(Of ExcelRows) = New List(Of ExcelRows)

Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load

End Sub

Public Function GetInfo() As Boolean
    Dim Completed As Boolean = False
    Dim MyExcel As New Excel.Application
    Dim enUK As New CultureInfo("en-GB")
    Dim DOS As String = "01/04/15"
    MyExcel.Workbooks.Open("C:\Dropbox\Tills\taRunAction1.xlsx")

    MyExcel.Sheets("Report").Activate()
    MyExcel.Range("A10").Activate()

    Dim ThisRow As New ExcelRows

    Do
        If MyExcel.ActiveCell.Value > Nothing Or MyExcel.ActiveCell.Text > Nothing Then

            ThisRow.Unit = MyExcel.ActiveCell.Value
            MyExcel.ActiveCell.Offset(0, 1).Activate()

            ThisRow.Outlet = MyExcel.ActiveCell.Value
            MyExcel.ActiveCell.Offset(0, 1).Activate()

            ThisRow.DateSale = MyExcel.ActiveCell.Value

            MyExcel.ActiveCell.Offset(0, 2).Activate()

            ThisRow.EPoS = MyExcel.ActiveCell.Value
            MyExcel.ActiveCell.Offset(0, 1).Activate()

            ThisRow.Quantity = MyExcel.ActiveCell.Value
            MyExcel.ActiveCell.Offset(0, 1).Activate()

            ThisRow.Value = MyExcel.ActiveCell.Value

            ExcelRowList.Add(ThisRow)
            MyExcel.ActiveCell.Offset(1, -6).Activate()

        Else
            Completed = True
            Exit Do
        End If

    Loop
    MyExcel.Workbooks.Close()
    MyExcel = Nothing

    Return Completed

End Function

Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click

    If GetInfo() = True Then

        For Each xItem In ExcelRowList

            Dim lViewItem As ListViewItem

            lViewItem = ListView1.Items.Add(xItem.Unit)
            lViewItem.SubItems.AddRange(New String() {xItem.Outlet, xItem.EPoS, xItem.Quantity, xItem.Value, xItem.DateSale})

        Next

    End If
End Sub

2 个答案:

答案 0 :(得分:0)

执行此操作的一种方法是使用DataTable:

Private dt As DataTable
...
dt = New DataTable
dt.Columns.Add("EPOS", GetType(Integer))
dt.Columns.Add("Unit", GetType(String))
dt.Columns.Add("Quantity", GetType(Integer))
dt.Columns.Add("Value", GetType(Decimal))
dt.Columns.Add("Total", GetType(Decimal))
dt.Columns.Add("Date", GetType(DateTime))

Dim keys As DataColumn() = {dt.Columns("EPOS")}
dt.PrimaryKey = keys

目前尚不清楚" VALUE"是单价或销售金额,因为所有单位都是1.我不确定我会一遍又一遍地重复SaleDate等数据,关键是DataTable可以替换 XLRows结构。 / p>

一个关键是您希望使用正确的类型数据,以便您可以相乘和添加。 XLRow结构正确输入 - 一切都是字符串而不是日期,十进制,整数等。

一种。要转换的XLRow类

为此,将XLRow结构转换为类。它的作用是从XL获取字符串数据并转换为Typed数据。接下来,DataTable用于收集汇总数据。

为此,上面的PrimaryKey定义至关重要。它会阻止你添加第二个(或第1000个)百事可乐"项目,并允许您找到该摘要项目。也许最重要的是,你可以摆脱ListView并使用DataGridView

dgv.DataSource = dt

enter image description here

使用一行代码行,DGV将创建列并显示DataTable中的所有行。与ListView不同,它会在DataTable中的基础数据发生更改时自行更新。迭代数据以在循环内汇总:

' get the row for this EPOS code
Dim dr As DataRow = dt.Rows.Find(xl.EPOS)

If dr IsNot Nothing Then
    ' we already have this item, increment Quan, TotalSales:
    dr("Quantity") += xl.Quantity
    dr("Total") += (xl.Quantity * xl.Value)
Else
    ' new transaction item, add it:
    dt.Rows.Add(xl.EPOS, xl.Unit, xl.Quantity, 
           xl.Value, (xl.Quantity * xl.Value), xl.DateSale)
End If

这非常简洁,因为当您从XL读取数据时,它会添加到摘要中。在执行汇总之前,无需将所有详细数据(XLS行)导入集合或数据表中。


B中。使用Linq而不是XLRow结构

为此省略DataTable中的主键。在这种情况下,DataTable将收集原始XLS数据而不是XLRow结构。 转换为数字以进行汇总。接下来,使用linq来总结数据;也许进入另一个DataTable。例如:

示例数据:

dt.Rows.Add(10001, "Ginger Ale", 1, 2.25, #4/5/2015#)
dt.Rows.Add(34582, "Pepsi", 3, 6.0, #4/5/2015#)
dt.Rows.Add(10002, "Chips", 1, 3.25, #4/5/2015#)
dt.Rows.Add(34582, "Pepsi", 1, 2.0, #4/5/2015#)
dt.Rows.Add(78301, "Roast Duck", 1, 15.25, #4/5/2015#)
dt.Rows.Add(34582, "Pepsi", 1, 2.0, #4/5/2015#)
dt.Rows.Add(34582, "Pepsi", 1, 2.0, #4/5/2015#)
dt.Rows.Add(10002, "Chips", 1, 3.25, #4/5/2015#)
dt.Rows.Add(34582, "Pepsi", 1, 2.0, #4/5/2015#)

获取摘要信息:

' group the data by EPOS code
Dim drs = From row In dt.AsEnumerable()
          Group row By ID = row.Field(Of Integer)("EPOS") Into Group
          Select Group

Dim TotSales As Decimal
Dim TotUnits As Integer

' each DRS is a collection of all the items with the same EPOS code
Dim dr As DataRow()

Console.WriteLine("EPOS     Item    Lines   Units   Total Sales")
' get the total sales in each group
For n As Integer = 0 To drs.Count - 1
    dr = drs(n)         ' the current EPOC group

    TotUnits = dr.Sum(Function(t) t.Field(Of Integer)("Quantity"))

    ' Sales could just be TotUnits * dr(0)("Value")
    ' sample data makes it unclear if Value is the UNITPRICE or SALEAMOUNT
    ' This assumes it is SALEAMOUNT such that 2 Pepsi = 4.00
    TotSales = dr.Sum(Function(t) t.Field(Of Decimal)("Value"))

    ' ToDo: do something interesting with the totals
    Console.WriteLine("{0}      {1}      {2}         {3}     {4}",
                    dr(0)("EPOS"),
                    dr(0)("Unit").ToString,
                    dr.Length.ToString("D2"),
                    TotUnits.ToString,
                    TotSales.ToString("C2"))
Next

输出:

EPOS     Item          Lines   Units   Total Sales  
10001     Ginger Ale     01       1     $2.25
34582     Pepsi          05       7     $14.00
10002     Chips          02       2     $6.50
78301     Roast Duck     01       1     $15.25

关键是,一旦在有用的结构中输入了类型数据,它的行为就像SUMIF一样。结果显示该数据有5个百事可乐条目,7个总单位和7 * 2 = 14。

我认为循环版本更易于管理和调试,而且由于在读取XLS行时随时构建了摘要,所以它更加经济。

答案 1 :(得分:0)

@ Plutonix的回答是一个好主意,我完全同意他关于将ExcelRows结构/类字段从字符串更改为适当的数字类型的意见。

另一种可能性是使用LINQ对ExcelRowList中的数据进行分组。类似于以下内容

Dim results = From r In ExcelRowList
              Group By r.EPoS
              Into Group, Sum(r.Value * r.Quantity)

请注意,这不会起作用,因为r.Value和r.Quantity是字符串,因此您无法将它们相乘。因此,您应该更改结构中的字段类型,然后在循环遍历单元格时,将单元格值转换为正确的类型。如果演员表失败,您将不得不决定该怎么做。

Group By on MSDN