Cross table VB.NET & SQL Server & Linq - sql

I have a table like this:
MAName feldtext
------------------
karl fieldtext1
karl fieldtext2
karl fieldtext1
karl fieldtext3
karl fieldtext4
karl fieldtext2
karl fieldtext5
karl fieldtext3
karl fieldtext3
susi fieldtext1
susi fieldtext4
john fieldtext2
john fieldtext5
john fieldtext5
and I need:
MAName fieldtext1 fieldtext2 fieldtext3 fieldtext4 fieldtext5 FehlerJeMA
karl 2 2 3 1 1 9
susi 1 0 0 1 0 2
john 0 1 0 0 2 3
The columns fieldtext can go from fieldtext1 to fieldtextn, it's dynamic, depending on query.
I was looking here for solutions and found, so my approach:
Dim dt2 As New DataTable
Dim nn As Integer = 0
Dim Zeile As DataRow
dt2.Columns.Add("MAName")
' fieldtext distinct
Dim query2 = (From dr In (From d In newTable2.AsEnumerable Select New With {.feldtext1 = d("feldtext")}) Select dr.feldtext1 Distinct)
For Each Feldtext In query2
dt2.Columns.Add(Feldtext)
Next
column = New DataColumn()
column.DataType = System.Type.GetType("System.Int32")
column.ColumnName = "FehlerJeMA"
dt2.Columns.Add(column)
' MAName distinct
Dim query3 = (From dr In (From d In newTable2.AsEnumerable Select New With {.MAName2 = d("MAName")}) Select dr.MAName2.ToString.ToLower Distinct)
For Each Mitarbeiter In query3
Zeile = dt2.NewRow()
Zeile(0) = Mitarbeiter.ToString.ToLower
MA2 = Mitarbeiter.ToString.ToLower
nn = 1
For Each colName2 In query2
Fehler2 = colName2
Dim AnzahlFehler As String = (From row In newTable2.Rows Select row Where row("MAName").ToString.ToLower = MA2 And row("feldtext") = Fehler2).Count
If AnzahlFehler = 0 Then
AnzahlFehler = ""
End If
Zeile(nn) = AnzahlFehler
nn += 1
If AnzahlFehler <> "" Then
FehlerJeMA += CInt(AnzahlFehler)
End If
Next
Zeile(nn) = FehlerJeMA
dt2.Rows.Add(Zeile)
Next
This works, but is very slow...
It could be the case that in my table has more than 10.000 rows...
So my question is: what is fastest approach to get the result?
Is it some kind of cross table with linq? Other approaches?

In C# you will be able to use the code, try to translate it for your problem:
var pivotData = data.GroupBy(x => new {x.MAName, x.feldtext}, (key, group) => new { MAName = key.Column1, feldtext = key.Column2, count = group.Count() });

Related

VBA code - Group by matching columns using SQL

Input:
G F V S P M
10 1 1 1 1 a
10 1 1 1 1 b
10 1 2 1 1 c
10 2 1 1 1 c
11 1 1 1 1 d
11 1 1 2 1 d
11 1 1 2 1 e
Output should be:
G F V S P M
10 1 1 1 1 a, b
10 1 2 1 1 c
10 2 1 1 1 c
11 1 1 1 1 d, e
11 1 1 2 1 d, e
Public Function Test()
Dim sqlCON As New ADODB.Connection
Dim sqlREC As New ADODB.Recordset
Dim sqlSTR As String
Dim sqlSTR2 As String
Dim newWB As Workbook
Set newWB = ActiveWorkbook
With sqlCON
.Provider = "Microsoft.ACE.OLEDB.12.0;"
.ConnectionString = "Data Source='" & newWB.FullName & "';Extended Properties=""Excel 12.0 Xml;HDR=Yes;IMEX=1"";"
.Open
End With
sqlSTR = "SELECT G, F, V, S, P, M " & _
"FROM [Sheet1$];"
Set sqlREC = sqlCON.Execute(sqlSTR)
If sqlREC.BOF = False And sqlREC.EOF = False Then
getAD = sqlREC.GetRows
Else
getAD = Empty
End If
Set sqlREC = Nothing
sqlCON.Close
In the SELECT section of the code, I have already tried FOR XML PATH, STRING_AGG and GROUP_CONCAT, but had no success, it seems these functions are not supported in VBA.
The "ID" of the row is a combination of all the columns, where the leading column is V, for each V (combined with the other columns) we have a combination of M.
Does someone have a clue on how I can get the desired output?

datatable sum column and concatenate rows using LINQ and group by on multiple columns

I Have a datatable with following records
ID NAME VALUE CONTENT
1 AAA 10 SYS, LKE
2 BBB 20 NOM
1 AAA 15 BST
3 CCC 30 DSR
2 BBB 05 EFG
I want to write a VB.NET/LINQ query to have a output like below table: -
ID NAME SUM CONTENT (as CSV)
1 AAA 25 SYS, LKE, BST
2 BBB 25 NOM, EFG
3 CCC 30 DSR
Please provide me LINQ query to get the desired result. Thanks.
I have tried concatenation using below query
Dim grouped = From row In dtTgt.AsEnumerable() _
Group row By New With {row.Field(Of Int16)("ID"), row.Field(Of String)("Name")} _
Into grp() _
Select ID, Name, CONTENT= String.Join(",", From i In grp Select i.Field(Of String)("CONTENT"))
This query will give you the expected output:-
Dim result = From row In dt.AsEnumerable()
Group row By _group = New With {Key .Id = row.Field(Of Integer)("Id"),
Key .Name = row.Field(Of String)("Name")} Into g = Group
Select New With {Key .Id = _group.Id, Key .Name = _group.Name,
Key .Sum = g.Sum(Function(x) x.Field(Of Integer)("Value")),
Key .Content = String.Join(",", g.Select(Function(x) x.Field(Of String)("Content")))}
Thanks for your answers.
However, I have managed to get the desired result using simple code (Without LINQ): -
Dim dt2 As New DataTable
dt2 = dt.Clone()
For Each dRow As DataRow In dt.Rows
Dim iID As Integer = dRow("ID")
Dim sName As String = dRow("Name")
Dim sContt As String = dRow("Content")
Dim iValue As Integer = dRow("Value")
Dim rwTgt() As DataRow = dt2.Select("ID=" & iID)
If rwTgt.Length > 0 Then
rwTgt(0)("Value") += iValue
rwTgt(0)("Content") += ", " & sContt
Else
rw = dt2.NewRow()
rw("ID") = iID
rw("Name") = sName
rw("Value") = iValue
rw("Content") = sContt
dt2.Rows.Add(rw)
End If
Next

Display only certain rows in datatable

So I have a dataset like below
Col1 Col2 Col3 Col4
Apple 1 10 Orange
Apple 2 20 Orange
Apple 3 30 Orange
Apple 1 10 Pear
Apple 2 20 Pear
Apple 3 30 Pear
Orange 1 10 grapes
Orange 2 20 grapes
ORange 1 10 kiwi
Berries 1 10 apple
Berries 1 20 Kiwi
I just need something like
Col1 Col2 Col3 Col4
Apple 1 10 Orange
Apple 2 20 Orange
Apple 3 30 Orange
ORange 1 10 Grapes
Orange 2 20 Grapes
Berries 1 10 Apple
So basically it is the col1 and col4, if col4 changes for col1 as I read through the rows, I should'nt display that
Could someone please help me
After first's suggestion
For i As Integer = 0 To dtResults.Rows.Count - 1
Dim firstItem As String = dtResults.Rows(i)("col1").ToString()
Dim firstToB As String = dtResults.Rows(i)("col4").ToString()
dtResults.DefaultView.RowFilter = "col4= '" + firstToB + "'"
Dim tempTable As DataTable = dtResults.DefaultView.ToTable()
Dim Total As Integer = 0
For Each dr As DataRow In tempTable.Rows
'Dim firstItem As String = dr("col1").ToString()
'If (dr("col1") = firstItem) AndAlso (firstToBin = dr("col4")) Then
If item Is Nothing OrElse item <> dr("col1") Then
If Not item Is Nothing Then
dgv.Rows.Add()
End If
itemnum = dr("col1")
Else
itemnum = ""
desc = ""
size = ""
Total += dr("col3")
End If
item = dr("col1")
dgv.Rows.Add(dr('',dr('',dr('')...)
count += 1
'End If
Next
Next
You will need to filter through your DataTable's rows. You can use the DefaultView in order to do this.
For example:
Dim ds1 As New DataSet1 'Create an instance of your DataSet1
ds1.yourDataTable.DefaultView.RowFilter = "Col4='Orange'"
If your DataSet's DataTables are created in the Visual Studio's designer, you can specify the DataType (string, integer, boolean) for each column. By default the DataType is set to a string.
You can do this in two ways
Filtering and Sorting using DataViews
Filtering and Sorting using DataTables
So to achieve as mentioned in the question here is what I did. I can always make changes in Stored procedure but I should be able to see those rows also in dataset for some other purpose. And so to bind the Grid I took row's item like below loop through
For i As Integer = 0 To dtResults.Rows.Count - 1
Dim firstItem As String = dtResults.Rows(i)("col1").ToString()
If acceptitem Is Nothing OrElse acceptitem <> firstItem Then
Dim firstToB As String = dtResults.Rows(i)("col4").ToString()
dtResults.DefaultView.RowFilter = "col4 = '" + firstToB + "' AND col1 = '" + firstItem + "'"
Dim tempTable As DataTable = dtResults.DefaultView.ToTable()
Dim Total As Integer = 0 ' dtResults.Rows(i)("col3").ToString()
For Each dr As DataRow In tempTable.Rows
acceptitem = dr("col1").ToString()
If item Is Nothing OrElse item <> dr("col1") Then
If Not item Is Nothing Then
dgv.Rows.Add()
End If
itemnum = dr("col1")
Total += Convert.ToInt16(dr("col3").ToString())
Else
itemnum = ""
Total += Convert.ToInt16(dr("col3").ToString())
End If
item = dr("col1")
dgv.Rows.Add(dr('',dr('',dr('').......)
count += 1
Next
End If
Next
Thanks so much for all the other suggestions...

SQL: How to query the distinct values for all columns

For example,
The table has 3 rows and 3 columns:
Name Age Gender
Peter 25 M
John 29 M
Alex 25 M
And I want to query the table and get
Name Age Gender
Peter 25 M
John 29
Alex
The method I have tried:
SELECT DISTINCT Name,Age,Gender FROM table
The output is still
Name Age Gender
Peter 25 M
John 29 M
Alex 25 M
How to achieve the table that there is no redundant entries for every field? Thanks.
Thanks for the help from all of you, especially the help from donPablo.
Here's my VBA code to achieve that. Since I am totally new to VBA, the code might not be very clean and efficient. But at least it works.
Option Compare Database
Sub ReadDistinctValue()
Dim d As Database
Dim rs As Recordset
Dim FN As Field, Age As Field, Sex As Field
Set d = CurrentDb()
Set rs = d.OpenRecordset("Table1")
Set FN = rs.Fields("FN")
Set Age = rs.Fields("Age")
Set Sex = rs.Fields("Sex")
d.Execute "CREATE TABLE Table4 (FN Text,Age Text,Sex Text)"
While Not rs.EOF
If CheckFN(FN) = False Then
Call WriteFN(FN)
End If
If CheckAge(Age) = False Then
Call WriteAge(Age)
End If
If CheckSex(Sex) = False Then
Call WriteSex(Sex)
End If
rs.MoveNext
Wend
rs.Close
End Sub
Function CheckFN(FN As Field) As Boolean
Dim d As Database
Dim rs_new As Recordset
Dim FN_new As Field
Set d = CurrentDb()
Set rs_new = d.OpenRecordset("Table4")
Set FN_new = rs_new.Fields("FN")
CheckFN = False
Do While Not rs_new.EOF
If FN_new = FN Then
CheckFN = True
Exit Do
End If
rs_new.MoveNext
Loop
rs_new.Close
End Function
Function WriteFN(FN As Field)
Dim d As Database
Dim rs_new As Recordset
Dim FN_new As Field
Set d = CurrentDb()
Set rs_new = d.OpenRecordset("Table4")
Set FN_new = rs_new.Fields("FN")
If Not rs_new.EOF Then
rs_new.MoveFirst
End If
Do While True
If rs_new.EOF Then
rs_new.AddNew
FN_new = FN
rs_new.Update
Exit Do
End If
If IsNull(FN_new.Value) Then
rs_new.Edit
FN_new = FN
rs_new.Update
Exit Do
End If
rs_new.MoveNext
Loop
rs_new.Close
End Function
Function CheckAge(Age As Field) As Boolean
Dim d As Database
Dim rs_new As Recordset
Dim Age_new As Field
Set d = CurrentDb()
Set rs_new = d.OpenRecordset("Table4")
Set Age_new = rs_new.Fields("Age")
CheckAge = False
Do While Not rs_new.EOF
If Age_new = Age Then
CheckAge = True
Exit Do
End If
rs_new.MoveNext
Loop
rs_new.Close
End Function
Function WriteAge(Age As Field)
Dim d As Database
Dim rs_new As Recordset
Dim Age_new As Field
Set d = CurrentDb()
Set rs_new = d.OpenRecordset("Table4")
Set Age_new = rs_new.Fields("Age")
If Not rs_new.EOF Then
rs_new.MoveFirst
End If
Do While True
If rs_new.EOF Then
rs_new.AddNew
Age_new = Age
rs_new.Update
Exit Do
End If
If IsNull(Age_new.Value) Then
rs_new.Edit
Age_new = Age
rs_new.Update
Exit Do
End If
rs_new.MoveNext
Loop
rs_new.Close
End Function
Function CheckSex(Sex As Field) As Boolean
Dim d As Database
Dim rs_new As Recordset
Dim Sex_new As Field
Set d = CurrentDb()
Set rs_new = d.OpenRecordset("Table4")
Set Sex_new = rs_new.Fields("Sex")
CheckSex = False
Do While Not rs_new.EOF
If Sex_new = Sex Then
CheckSex = True
Exit Do
End If
rs_new.MoveNext
Loop
rs_new.Close
End Function
Function WriteSex(Sex As Field)
Dim d As Database
Dim rs_new As Recordset
Dim Sex_new As Field
Set d = CurrentDb()
Set rs_new = d.OpenRecordset("Table4")
Set Sex_new = rs_new.Fields("Sex")
If Not rs_new.EOF Then
rs_new.MoveFirst
End If
Do While True
If rs_new.EOF Then
rs_new.AddNew
Sex_new = Sex
rs_new.Update
Exit Do
End If
If IsNull(Sex_new.Value) Then
rs_new.Edit
Sex_new = Sex
rs_new.Update
Exit Do
End If
rs_new.MoveNext
Loop
rs_new.Close
End Function
By naming the three columns, you are retrieving distinct combinations of that set of values.
If you want lists of distinct values, name each individually in a select.
SELECT DISTINCT Name FROM table
SELECT DISTINCT Age FROM table
SELECT DISTINCT Gender FROM table
If you are trying to get them to display as you have in your example, that will have to be accomplished by some GUI functionality. SQL database engines are not good at display trickery, just handling data.
I have expanded the table values a little, just to see what would happen --
FN Age Sex
Alice 28 F
Ben 19 M
Charles 33 M
Doug 23 M
Elaine 21 F
Frank 25 M
Gwen 28 F
Helen 33 F
Alice 17 F
Ben 21 F
Then I developed a single query for FN, and later generalized to all three fields --
The clue is to sequence # each FN/AGE/SEX and then join on that seq#--
SELECT
AB.fn,
CD.age,
EF.sex
FROM
((SELECT A.fn, Count(B.fn) AS CNTfn
FROM
(SELECT DISTINCT fn FROM table1) AS A,
(SELECT DISTINCT fn FROM table1) AS B
WHERE B.fn <= A.fn
GROUP BY A.fn) AS AB
LEFT JOIN
(SELECT C.age, Count(D.age) AS CNTage
FROM
(SELECT DISTINCT age FROM table1) AS C,
(SELECT DISTINCT age FROM table1) AS D
WHERE D.age <= C.age
GROUP BY C.age) AS CD
ON AB.cntfn = CD.cntage)
LEFT JOIN
(SELECT E.sex, Count(F.sex) AS CNTsex
FROM
(SELECT DISTINCT sex FROM table1) AS E,
(SELECT DISTINCT sex FROM table1) AS F
WHERE F.sex <= E.sex
GROUP BY E.sex) AS EF
ON AB.cntfn = EF.CNTsex;
This gives the results desired --
FN AGE SEX
Alice 17 F
Ben 19 M
Charles 21
Doug 23
Elaine 25
Frank 28
Gwen 33
Helen
I changed the Sex in my sample table, and added to the following as the first sequencing of the un-Distinct whole table and changed the ON... to XZ.cntall ...
(SELECT X.FN & X.AGE & X.SEX, Count(*) AS CNTall
FROM
(SELECT DISTINCT FN, AGE, SEX FROM table1) AS X,
(SELECT DISTINCT FN, AGE, SEX FROM table1) AS Z
WHERE Z.FN & Z.AGE & Z.SEX <= X.FN & X.AGE & X.SEX
GROUP BY X.FN, X.AGE, X.SEX) as XZ
and now get these results
fn age sex
Alice 17 M
Ben 19 N
Charles 21 O
Doug 23 P
Elaine 25 Q
Frank 28 R
Gwen 33 W
Helen X
Y
Z
There is probably an SQL solution for this. I am constantly amazed at what can be done. However, my answer is that this is a perfect application for VBA.

How to remove all duplicates in a data table in vb.net?

Consider my data table
ID Name
1 AAA
2 BBB
3 CCC
1 AAA
4 DDD
Final Output is
2 BBB
3 CCC
4 DDD
How can i remove the rows in the data table using Vb.Net
Any help is appreciated.
Following works if you only want the distinct rows(skip those with same ID and Name):
Dim distinctRows = From r In tbl
Group By Distinct = New With {Key .ID = CInt(r("ID")), Key .Name = CStr(r("Name"))} Into Group
Where Group.Count = 1
Select Distinct
' Create a new DataTable containing only the unique rows '
Dim tblDistinct = (From r In tbl
Join distinctRow In tblDistinct
On distinctRow.ID Equals CInt(r("ID")) _
And distinctRow.Name Equals CStr(r("Name"))
Select r).CopyToDataTable
If you want to remove the dups from the original table:
Dim tblDups = From r In tbl
Group By Dups = New With {Key .ID = CInt(r("ID")), Key .Name = CStr(r("Name"))} Into Group
Where Group.Count > 1
Select Dups
Dim dupRowList = (From r In tbl
Join dupRow In tblDups
On dupRow.ID Equals CInt(r("ID")) _
And dupRow.Name Equals CStr(r("Name"))
Select r).ToList()
For Each dup In dupRowList
tbl.Rows.Remove(dup)
Next
Here is your sample-data:
Dim tbl As New DataTable
tbl.Columns.Add(New DataColumn("ID", GetType(Int32)))
tbl.Columns.Add(New DataColumn("Name", GetType(String)))
Dim row = tbl.NewRow
row("ID") = 1
row("Name") = "AAA"
tbl.Rows.Add(row)
row = tbl.NewRow
row("ID") = 2
row("Name") = "BBB"
tbl.Rows.Add(row)
row = tbl.NewRow
row("ID") = 3
row("Name") = "CCC"
tbl.Rows.Add(row)
row = tbl.NewRow
row("ID") = 1
row("Name") = "AAA"
tbl.Rows.Add(row)
row = tbl.NewRow
row("ID") = 4
row("Name") = "DDD"
tbl.Rows.Add(row)
You can use the DefaultView.ToTable method of a DataTable to do the filtering like this:
Public Sub RemoveDuplicateRows(ByRef rDataTable As DataTable)
Dim pNewDataTable As DataTable
Dim pCurrentRowCopy As DataRow
Dim pColumnList As New List(Of String)
Dim pColumn As DataColumn
'Build column list
For Each pColumn In rDataTable.Columns
pColumnList.Add(pColumn.ColumnName)
Next
'Filter by all columns
pNewDataTable = rDataTable.DefaultView.ToTable(True, pColumnList.ToArray)
rDataTable = rDataTable.Clone
'Import rows into original table structure
For Each pCurrentRowCopy In pNewDataTable.Rows
rDataTable.ImportRow(pCurrentRowCopy)
Next
End Sub
Assuming you want to check all the columns, this should remove the duplicates from the DataTable (DT):
DT = DT.DefaultView.ToTable(True, Array.ConvertAll((From v In DT.Columns Select v.ColumnName).ToArray(), Function(x) x.ToString()))
Unless I overlooked it, this doesn't seem to be in the documentation (DataView.ToTable Method), but this also appears to do the same thing:
DT = DT.DefaultView.ToTable(True)