compute datatable with many filters - vb.net

data table with 3 columns ( Quantity - SOP_Type - Item_Number)
I want to compute sum of column Quantity for the item with number "309-032-SHU" and SOP_Type = 3
I tried this code and didn't work
Dim b As String
b = "309-032-SHU"
dim c as integer
c = 3
DGV1.Rows(0).Cells(0).Value = NAJRNDataSet.vwSalesLineItems.Compute("SUM(Quantity)", "Item_Number = " & b & " ", "SOP_Type = " & c & " ")

Since DataTable class does not implement IEnumerable it provides an extension method to get an enumerable. (.AsEnumerable) We can then use a linq syntax to get the Sum.
Private Sub OpCode()
Dim dt As New DataTable
'Fill the datatable
Dim b = "309-032-SHU"
Dim c = 3
Dim Total As Integer
Total = (From r As DataRow In dt.AsEnumerable
Where r.Field(Of String)("Item_Number") = b And r.Field(Of Integer)("SOP_Type") = c
Select r.Field(Of Integer)("Quantity")).Sum
DGV1.Rows(0).Cells(0).Value = Total
End Sub

Related

Search and replace inside string column in DataTable is slow?

I am fetching distinct words in a string column of a DataTable (.dt) and then replacing the unique values with another value, so essentially changing words to other words. Both approaches listed below work, however, for 90k records, the process is not very fast. Is there a way to speed up either approach?
The first approach, is as follows:
'fldNo is column number in dt
For Each Word As String In DistinctWordList
Dim myRow() As DataRow
myRow = dt.Select(MyColumnName & "='" & Word & "'")
For Each row In myRow
row(fldNo) = dicNewWords(Word)
Next
Next
A second LINQ-based approach is as follows, and is actually not very fast either:
Dim flds as new List(of String)
flds.Add(myColumnName)
For Each Word As String In DistinctWordsList
Dim rowData() As DataRow = dt.AsEnumerable().Where(Function(f) flds.Where(Function(el) f(el) IsNot DBNull.Value AndAlso f(el).ToString = Word).Count = flds.Count).ToArray
ReDim foundrecs(rowData.Count)
Cnt = 0
For Each row As DataRow In rowData
Dim Index As Integer = dt.Rows.IndexOf(row)
foundrecs(Cnt) = Index + 1 'row.RowId
Cnt += 1
Next
For i = 0 To Cnt
dt(foundrecs(i))(fldNo) = dicNewWords(Word)
Next
Next
So you have your dictionary of replacements:
Dim d as New Dictionary(Of String, String)
d("foo") = "bar"
d("baz") = "buf"
You can apply them to your table's ReplaceMe column:
Dim rep as String = Nothing
For Each r as DataRow In dt.Rows
If d.TryGetValue(r.Field(Of String)("ReplaceMe"), rep) Then r("ReplaceMe") = rep
Next r
On my machine it takes 340ms for 1 million replacements. I can cut that down to 260ms by using column number rather than name - If d.TryGetValue(r.Field(Of String)(0), rep) Then r(0) = rep
Timing:
'setup, fill a dict with string replacements like "1" -> "11", "7" -> "17"
Dim d As New Dictionary(Of String, String)
For i = 0 To 9
d(i.ToString()) = (i + 10).ToString()
Next
'put a million rows in a datatable, randomly assign dictionary keys as row values
Dim dt As New DataTable
dt.Columns.Add("ReplaceMe")
Dim r As New Random()
Dim k = d.Keys.ToArray()
For i = 1 To 1000000
dt.Rows.Add(k(r.Next(k.Length)))
Next
'what range of values do we have in our dt?
Dim minToMaxBefore = dt.Rows.Cast(Of DataRow).Min(Function(ro) ro.Field(Of String)("ReplaceMe")) & " - " & dt.Rows.Cast(Of DataRow).Max(Function(ro) ro.Field(Of String)("ReplaceMe"))
'it's a crappy way to time, but it'll prove the point
Dim start = DateTime.Now
Dim rep As String = Nothing
For Each ro As DataRow In dt.Rows
If d.TryGetValue(ro.Field(Of String)("ReplaceMe"), rep) Then ro("ReplaceMe") = rep
Next
Dim ennd = DateTime.Now
'what range of values do we have now
Dim minToMaxAfter = dt.Rows.Cast(Of DataRow).Min(Function(ro) ro.Field(Of String)("ReplaceMe")) & " - " & dt.Rows.Cast(Of DataRow).Max(Function(ro) ro.Field(Of String)("ReplaceMe"))
MessageBox.Show($"min to max before of {minToMaxBefore} became {minToMaxAfter} proving replacements occurred, it took {(ennd - start).TotalMilliseconds} ms for 1 million replacements")

Crystal Reports empty when printed during runtime(without viewer)

I'm developing a small POS system using VB.Net , MS Sql and Crystal Reports. I have no trouble viewing reports using Cry Rep Viewer. But when i try to print a bill during runtime the report becomes empty. Following is the sequence i'm executing within the procedure.
Generate bill no
Deduct qty from stocks
add the transaction from temp table to final sales table
print bill
delete temp table transactions
clear for next transaction
But my problem comes during the bill printing. Bill does not pickup the necessary records from the sales table. Following is my code for the end transaction. (I am using few of my own methods and functions which are from another class and im also using a fix bill no for testing)
I'm also attaching 2 bills. First 1 is the one viewed and exported from the Cry Report Viewer. The second one is the one with problem, and its empty even though the records exists in the table. Can some one kindly advice me on this matter?
Private Sub endCurrentTransaction()
Try
Dim sqlTempBill As String = "SELECT * FROM tb_transactions where cur_user='" & curUser & "'"
Dim sqlSales As String = "SELECT * FROM tb_sales where bill_no=''"
'Get Bill No
Dim newBillNo As String = generateBillNo()
'Deduct IT qty
Dim tempBill As DataTable
tempBill = myTbClass.myFunctionFetchTbData(sqlTempBill)
Dim i As Integer = 0
Dim curQty As Double = 0
Dim trQty As Double = 0
Dim updatedQty As Double = 0
For i = 0 To tempBill.Rows.Count - 1
curQty = 0
'Get the Current stock qty
Dim sqlgetCurQty As String = "SElECT cur_qty FROM tb_stock where it_batch_code='" & tempBill.Rows(i).Item("it_batch_code") & "'"
conn.Open()
Dim SqlCmdCurQty As New SqlCommand(sqlgetCurQty, conn)
Dim DataReadercurQty As SqlDataReader = SqlCmdCurQty.ExecuteReader
While DataReadercurQty.Read()
curQty = DataReadercurQty.Item(0)
End While
DataReadercurQty.Close()
conn.Close()
trQty = tempBill.Rows(i).Item("qty")
updatedQty = curQty - trQty
Dim sqlUpdateQtyString As String = "UPDATE tb_stock SET cur_qty=" & Math.Round((updatedQty), 3) & " Where it_batch_code='" & tempBill.Rows(i).Item("it_batch_code") & "'"
conn.Open()
Dim SqlCmdQty As New SqlCommand(sqlUpdateQtyString, conn)
SqlCmdQty.ExecuteNonQuery()
conn.Close()
'add to sales
Dim tempTbSales As DataTable
tempTbSales = myTbClass.myFunctionFetchTbData(sqlSales)
Dim dataRow As DataRow = tempTbSales.NewRow
dataRow("it_code") = Trim(tempBill.Rows(i).Item("it_code"))
dataRow("it_batch_code") = Trim(tempBill.Rows(i).Item("it_batch_code"))
dataRow("it_name") = Trim(tempBill.Rows(i).Item("it_name"))
dataRow("buy_price") = Math.Round(tempBill.Rows(i).Item("buy_price"), 3)
dataRow("sell_price") = Math.Round(tempBill.Rows(i).Item("sell_price"), 3)
dataRow("qty") = Math.Round(tempBill.Rows(i).Item("qty"), 3)
dataRow("gross_val") = Math.Round(tempBill.Rows(i).Item("gross_val"), 3)
dataRow("discount_val") = Math.Round(tempBill.Rows(i).Item("discount_val"), 3)
dataRow("net_val") = Math.Round(tempBill.Rows(i).Item("net_val"), 3)
dataRow("profit_profile") = Trim(tempBill.Rows(i).Item("profit_profile"))
dataRow("discount_profile") = Trim(tempBill.Rows(i).Item("discount_profile"))
dataRow("cus_name") = Trim(tempBill.Rows(i).Item("cus_name"))
dataRow("tr_type") = Trim(cmbTrans.Text)
dataRow("cr_card_type") = Trim(cmbCardType.Text)
dataRow("card_no") = Trim(txtCardNo.Text)
dataRow("bill_no") = newBillNo
dataRow("discount_profile") = Trim(tempBill.Rows(i).Item("cus_name"))
dataRow("cur_user") = curUser
dataRow("added_date") = curServerDateTime
tempTbSales.Rows.Add(dataRow)
Call myTbClass.MyMethodUpdateTable(sqlSales, tempTbSales)
Next
i = 0
'Print bill
Dim cash, balance As Double
Dim crepBill As New repBill
crepBill.SetDatabaseLogon("sa", dbPwd)
cash = Val(Me.txtCash.Text)
balance = Val(Me.txtBalance.Text)
crepBill.RecordSelectionFormula = "{TB_SALES.bill_no} ='" & "000015" & "'"
crepBill.DataDefinition.FormulaFields("txtCash").Text = Format(cash, "#0.00")
crepBill.DataDefinition.FormulaFields("txtBal").Text = Format(balance, "#0.00")
crepBill.PrintToPrinter(1, False, 0, 0)
crepBill.Dispose()
'delete temp bill table
For i = 0 To tempBill.Rows.Count - 1
tempBill.Rows(i).Delete()
Next
Call myTbClass.MyMethodUpdateTable(sqlTempBill, tempBill)
'reset front end
Call loadBill()
Call clearCurrentTransaction()
Call clearCurrentSubTotals()
Call clearCurCashBalance()
'Íncrease the bill no by 1
Call increaseBillNo()
txtItCode.Focus()
Catch ex As Exception
MsgBox("This error was generated at endCurrentTransaction()")
End Try
End Sub
I found out that report need to be refreshed before printing. Report will only take data if it is refreshed.
https://answers.sap.com/answers/13088178/view.html

I am having trouble with the code below. It is saying 'results' is an expression type '?'. which is not a collection type

I am having trouble with the code below. It is saying 'results' is an expression type '?'. which is not a collection type. And also my "=" sign between CInt(table1(ComboBox1.SelectedItem)) = CInt(table2(ComboBox2.SelectedItem)) is saying 'Equals' expected. Any help would be great, thanks! Not sure if table1 and table2 is the correct syntax either...
Dim tt As New DataTable()
tt = DtSet1.Tables(0)
Dim rr As New DataTable()
rr = DtSet2.Tables(0)
Dim DTA As New DataTable()
DTA.Columns.Add("Account", GetType(Integer))
DTA.Columns.Add("First", GetType(String))
DTA.Columns.Add("Last", GetType(String))
DTA.Columns.Add("Code", GetType(String))
Dim DTB As New DataTable()
DTB.Columns.Add("Code", GetType(String))
DTB.Columns.Add("Amount", GetType(Integer))
For g As Integer = 1 To 6
Dim row As DataRow = DTA.NewRow()
row("Account") = g
row("First") = 10 + g
row("Last") = 20 + g
row("Code") = 30 + g
DTA.Rows.Add(row)
row = DTB.NewRow()
row("Code") = 40 + g
row("Amount") = 50 + g
DTB.Rows.Add(row)
Next
Dim results = _
From table1 In DTA.AsEnumerable() Join table2 In DTB.AsEnumerable() _
On CInt(table1(ComboBox1.SelectedItem)) = CInt(table2(ComboBox2.SelectedItem)) _
New With { _
Key .Account = CInt(tt("Account")), _
Key .First = CInt(tt("First")), _
Key .Last = CInt(tt("Last")), _
Key .Offer = CInt(tt("Code")), _
Key .Amount = CInt(rr("Amount")) _
}
For Each item As var In results
Console.WriteLine([String].Format("Account = {0}, First = {1}, Last = {2}, Code = {3}, Amount = {4}", item.Account, item.First, item.Last, item.Code, item.Amount))
Next
Console.ReadLine()
The posted code doesn't compile - are you sure the actual version is the same?
Here it goes. The LINQ syntax is wrong. First off, in the Join clause you MUST use Equals keyword, not the = sign. Next the CInt() function is misplaced - it should be applied to the ComboBox1.SelectedItem in order to produce an integer, which will be treated as a column index. Finally you need to use the Select keyword to specify the select clause. Just before the New keyword. As a result you'll get an IEnumerable(Of <anonymous type>) in the results variable.
Later in the For ... Next loop declaration, loose the "As var". The item type will be inferred from the usage.
Plain LINQ queries are difficult. They require both practice and theoretical knowledge in order to be mastered. Fortunately there are LINQ extension methods. They achieve essentially the same, but in a different manner. If they are executed sequentially, the code becomes more readable and closes up toward the imperative approach.
E.g. the above LINQ query could be rewritten as follows:
Dim results = DTA.AsEnumerable().Join(DTB.AsEnumerable(),
Function(r1) r1(CInt(ComboBox1.SelectedItem)), ' The key selector for the LEFT row
Function(r2) r2(CInt(ComboBox2.SelectedItem)), ' The key selector for the RIGHT row
Function(tt, rr) New With {
Key .Account = CInt(tt("Account")), _
Key .First = CInt(tt("First")), _
Key .Last = CInt(tt("Last")), _
Key .Offer = CInt(tt("Code")), _
Key .Amount = CInt(rr("Amount")) _
} ' Result selector
)
Your question is rather vague with regard to where the data comes from.
I have made an example with two ComboBoxes and a Button on a form; it creates its own sample data:
Option Infer On
Option Strict On
Public Class Form1
' a method to show a datatable
Sub PrintDataTable(dt As DataTable)
Dim columnWidth = 10
Dim separator = New String("="c, columnWidth * dt.Columns.Count)
Console.WriteLine(separator)
Console.WriteLine(dt.TableName)
For Each n As DataColumn In dt.Columns
Console.Write(n.ColumnName.PadLeft(columnWidth))
Next
Console.WriteLine()
For Each r As DataRow In dt.Rows
For i = 0 To dt.Columns.Count - 1
Console.Write(r.Item(i).ToString().PadLeft(columnWidth, " "c))
Next
Console.WriteLine()
Next
Console.WriteLine(separator)
End Sub
Private Sub Button1_Click(sender As Object, e As EventArgs) Handles Button1.Click
' create some test data
Dim DTA As New DataTable("DTA")
DTA.Columns.Add("Account", GetType(Integer))
DTA.Columns.Add("First", GetType(String))
DTA.Columns.Add("Last", GetType(String))
DTA.Columns.Add("Code", GetType(String))
Dim DTB As New DataTable("DTB")
DTB.Columns.Add("Code", GetType(String))
DTB.Columns.Add("Amount", GetType(Integer))
' add values of test data where some values of "Code" are equal
For g As Integer = 1 To 6
Dim row As DataRow = DTA.NewRow()
row("Account") = g
row("First") = (10 + g).ToString()
row("Last") = (20 + g).ToString()
row("Code") = (30 + g).ToString()
DTA.Rows.Add(row)
row = DTB.NewRow()
row("Code") = (30 + CInt(g Mod 4)).ToString()
row("Amount") = (50 + g).ToString()
DTB.Rows.Add(row)
Next
' show the data for visual verification
PrintDataTable(DTA)
PrintDataTable(DTB)
' N.B. for the test data it is necessary to select "Code" in each ComboBox
Dim colToMatchA = CStr(ComboBox1.SelectedItem)
Dim colToMatchB = CStr(ComboBox2.SelectedItem)
Console.WriteLine($"A: {colToMatchA} B: {colToMatchB}")
' N.B. it is required that the column data have a .ToString() method which is
' unique for each possible value. Note that the number 0 will match the string "0" etc.
Dim result = From a In DTA
Join b In DTB
On a.Item(colToMatchA).ToString() Equals b.Item(colToMatchB).ToString()
Select New With {.Account = a("Account"), .CodeA = a("Code"), .Amount = b("Amount")}
For Each r In result
Console.WriteLine(r.ToString())
Next
Console.WriteLine("Done.")
End Sub
Private Sub Form1_Load(sender As Object, e As EventArgs) Handles MyBase.Load
Dim colsA = {"Account", "First", "Last", "Code"}
Dim colsB = {"Code", "Amount"}
ComboBox1.DataSource = colsA
ComboBox2.DataSource = colsB
End Sub
End Class
If you select "Code" in both Comboboxes, it gives this output:
========================================
DTA
Account First Last Code
1 11 21 31
2 12 22 32
3 13 23 33
4 14 24 34
5 15 25 35
6 16 26 36
========================================
====================
DTB
Code Amount
31 51
32 52
33 53
30 54
31 55
32 56
====================
A: Code B: Code
{ Account = 1, CodeA = 31, Amount = 51 }
{ Account = 1, CodeA = 31, Amount = 55 }
{ Account = 2, CodeA = 32, Amount = 52 }
{ Account = 2, CodeA = 32, Amount = 56 }
{ Account = 3, CodeA = 33, Amount = 53 }
Done.

Select distinct rows and sum columns in DataTable VB.Net 2005

i have a dataTable contains:
ID POCount POTotal
A 1 10
A 2 20
B 4 10
I want to get a result of a new data table as bellow:
ID POCount POTotal
A 3 30
B 4 10
How can i do this using a datatable?
My project is in VB.NET 2005 and i cannot use LINQ method.
What is the best way to do this?
I found a link that kinda near what i want. But it just skip the rows instead of summing up the columns when the id is similar.
http://www.dotnetfunda.com/forums/show/2603/how-to-remove-duplicate-records-from-a-datatable
LINQ is probably better - upgrade to later VS Express - its free!
Here is one approach using a class and a Dictionery
Public Class POSummary
Public Property ID As String
Public Property Count As Integer
Public Property Total As Integer
Sub New(POid As String, POcount As Integer, POtotal As Integer)
ID = POid
Count = POcount
Total = POtotal
End Sub
End Class
Private Sub Button12_Click(sender As Object, e As EventArgs) Handles Button12.Click
Dim pos As New List(Of POSummary)
Dim po As New POSummary("A", 1, 10)
pos.Add(po)
po = New POSummary("A", 2, 20)
pos.Add(po)
po = New POSummary("B", 4, 10)
pos.Add(po)
Debug.Print("--data--")
For Each p As POSummary In pos
Debug.Print(p.ID & " " & p.Count & " " & p.Total)
Next
Dim pd As New Dictionary(Of String, POSummary)
For Each p As POSummary In pos
If Not pd.ContainsKey(p.ID) Then
pd.Add(p.ID, p)
Else
pd(p.ID).Count += p.Count
pd(p.ID).Total += p.Total
End If
Next
Debug.Print("--totals--")
For Each kvp As KeyValuePair(Of String, POSummary) In pd
po = kvp.Value
Debug.Print(po.ID & " " & po.Count & " " & po.Total)
Next
Stop
End Sub

convert a datatable

I have a datatable like this
X,Y,Z
0,0,A
0,2,B
0,0,C
1,0,A
1,0,C
2,2,A
2,2,B
2,0,C
3,2,B
3,1,C
4,3,A
4,0,B
4,1,C
5,3,A
5,2,B
5,0,C
and I want to convert it to something like this:
X,A,B,C
0,0,2,0
1,0, ,0
2,2,2,0
3, ,2,1
4,3,0,1
5,3,2,0
I tried with dataset and linq but not I wasn't lucky.
My code for linq:
Dim q = (From c In dt _
Select c("Z") Distinct) 'I found out which categories I have in Z column (my example :A,B,C)
Dim ldt(q.Count) As DataTable
For i = 0 To q.Count - 1
Dim sfil As String = q(i).ToString
Dim r = (From c In dt _
Select c Where c("Z") = sfil)
ldt(i) = r.CopyToDataTable
Next
So now I have 3 tables (ldt(0) with values for A, ldt(1) with values for B, ldt(2) with values for C)
and I was thinking to do something like leftJoin but anything that I tried is fail.
Any solution or even a better idea?
Thanks
So a new example it would be:
I have this table:
id,Price,Item
0,0,Laptop
0,2,Tablet
0,0,Cellphone
1,0,Laptop
1,0,Tablet
2,2,Laptop
2,2,Cellphone
2,0,Tablet
3,2,Cellphone
3,1,Tablet
4,3,Laptop
4,0,Cellphone
4,1,Tablet
5,3,Laptop
5,2,Cellphone
5,0,Tablet
and I would like to convert it to this:
X,Laptop,Tablet,Cellphone
0,0,2,0
1,0, ,0
2,2,2,0
3, ,2,1
4,3,0,1
5,3,2,0
The values for each of the columns Laptop, Tablet, Cellphone are the Y values from the first table.
I hope it make more sense now.
I believe you can create a DataTable with column names corresponding to the item names. Then you group the previous DataTable by id and use each grouping to populate a row. Forgive me if I get anything wrong. I don't work with VB or DataTables that much.
Dim itemNames = (From c In dt _
Select c("Item") Distinct)
Dim newDt as DataTable = new DataTable()
Dim idColumn As DataColumn = new DataColumn()
idColumn.DataType = System.Type.GetType("System.Int32")
idColumn.ColumnName = "id"
idColumn.ReadOnly = True
idColumn.Unique = True
newDt.Columns.Add(idColumn)
For Each itemName As String In itemNames
Dim column As DataColumn = new DataColumn()
column.DataType = GetType(Nullable(Of Integer))
column.ColumnName = itemName
column.ReadOnly = True
column.Unique = False
newDt.Columns.Add(column)
Next
Dim groupingById = From row in dt
Group By Id = row("id")
Into RowsForId = Group
For Each grouping In groupingById
Dim row as DataRow = newDt.NewRow()
row("id") = grouping.Id
For Each rowForId in grouping.RowsForId
row(rowForId("Item")) = rowForId("Price")
Next
newDt.Rows.Add(row)
Next