Using dataTable to sort by 3 separate columns - vb.net

I have a txt file I'm reading that looks something like this...
Acct |Name |Date |Country
11111,FirstLast,01/01/2015,United States
11111,FirstLast,,
11111,,01/01/2015,
22222,FirstLast,02/02/2015,Kuwait
22222,FirstLast,,
22222,,02/02/2015,
33333,FirstLast,02/02/2015,France
33333,FirstLast,,
33333,,02/02/2015,
I'm creating a dataTable to fill with this data, and am adding a column called "LINE" to keep track of how many lines of data each account has. I then do a dataView sorting to sort like this:
dataView.Sort = "Acct ASC, Line ASC, Country ASC"
My output sorts by the acct and line#, but not by the country. So I'm getting this back...
Acct |Name |Date |Country |Line
11111,FirstLast,01/01/2015,United States,1
11111,FirstLast,,,2
11111,,01/01/2015,,3
22222,FirstLast,02/02/2015,Kuwait,1
22222,FirstLast,,,2
22222,,02/02/2015,,3
33333,FirstLast,02/02/2015,France,1
33333,FirstLast,,,2
33333,,02/02/2015,,3
What I want to get back is this because the main thing is to keep the same acct# records together, while sorting by country ascending...
Acct |Name |Date |Country |Line
33333,FirstLast,02/02/2015,France,1
33333,FirstLast,,,2
33333,,02/02/2015,,3
22222,FirstLast,02/02/2015,Kuwait,1
22222,FirstLast,,,2
22222,,02/02/2015,,3
11111,FirstLast,01/01/2015,United States,1
11111,FirstLast,,,2
11111,,01/01/2015,,3
I then realized that obviously this isn't working because the account number takes precedence in the sortation.
The big issue is that I have to use .NET 2.0, is there some way I can group these and sort by the country field?

Ideally this type of sorting can be implemented in the display such as GridView/DataGridView by Grouping the fields by Acct and then sorting within the groups as Country Desc, Line Asc.
If you want it to be sorted within the DataTable then you can accomplish by adding another dummy column to the DataTable.
Add another column to the DataTable e.g. Grp1
Sort DataTable by Acct Asc and Country Desc (so that country name always comes first and blanks later)
Iterate DataTable in a for loop (NOT For Each)
3.1. If Country is empty then set country = country in previous row (skip row 0, assuming country in row 0 always has a value)
3.2. Within same loop (outside and after if block) set the value of Grp1 column to Country - Acct
Now sort your DataTable again by Grp1 and Line
Putting it all together:
dt1.Columns.Add("Grp1")
dt1.DefaultView.Sort = "Acct Asc, Country Desc"
For i = 0 to dt1.Rows - 1
If i > 0 AndAlso dt1.Rows(i).Item("Country").ToString.Length = 0 Then
dt1.Rows(i).Item("Country") = dt1.Rows(i - 1).Item("Country").ToString
End If
dt1.Rows(i).Item("Grp1") = dt1.Rows(i).Item("Country").ToString & " - " & dt1.Rows(i).Item("Acct").ToString
Next
dt1.DefaultView.Sort = "Grp1 Asc, Line Asc"

Related

Hiding Columns Conditionally While Using Page Breaks in SSRS

We would like to hide a column on a page when that column contains no data or 0 value/total for that page.
Here's some example data:
Department is a row group and Macs/PCs is a column group/Pivot.
| Department | Macs | PCs |
---------------------------
| Accounting | 0 | 10 |
| Creative | 10 | 0 |
We have a page break on department. For the Accounting page we would like to hide the Macs column and on the Creative page we would like to hide the PCs column.
I would do it with the help of a List. And group the list by Department and add the page break there.
Then add your matrix with a row group on Department and with a Column group on your product. Sum your count field.
Just make sure your dataset does not have any zeros.
For a simple dataset as:
select
*
from (
values
('Accounting','PC',10),
('Creative','PC',10),
('Accounting','Mac',11),
('Creative','Mac',20)
) x (Department, Product,Total)
And a ssrs report setup like this:
You should get the output you want. For more detailed help please supply a sample of your dataset.
Try adding a formula to the Visibility property of the column group. Something like:
iif(IsNothing(Fields!TotalMachines.Value) OR SUM(Fields!TotalMachines.Value) = 0, true, false)

Compare and synchronize DataTables

I need to Compare Two dataTables.
dataTable A contains current set of Data on clients machine.
dataTable B contains future updates to dataTable A.
dataTable A structure
ID | firstname | lastName
1 | "test" | "last"
2 | "whatever" | "someone"
3 | "hi" | "hello
dataTable B Structure
ID | firstname | lastName
1 | "updated" | "yes"
2 ->deleted
3 | "hi" | hello" ->unchanged
4 | "new" |record " ->row added
When I go dataTableA.merge(datatableB)
I basically just get dataTableA with dataTableB added rows
so for example
ID | firstname | lastName
1 | "test" | "last"
2 | "whatever" | "someone"
3 | "hi" | "hello
1 | "updated" | "yes"
3 | "hi" | hello" ->unchanged
4 | "new" |record " ->row added
It doesn't match on the IDs and get updated or deleted. I just want to compare two tables, update table A that should look exactly like table B. I'm not quite sure how to accomplish this properly.
Basically there is a SQL table in the clients machine that needs to get completed updated and sync exact to a datatable B that is being passed in. In theory i just want to take table B and basically update table A. So after I need to update the SQL table. I tried something like this.
Dim adapter = New SqlDataAdapter("select * from test_table2", connection)
Using (New SqlCommandBuilder(adapter))
adapter.Fill(dTable)
connection.Open()
adapter.Update(dTable)
End Using
Doesn't seem to work.
If the datatables are supposed to end up identical, instead of using
dataTableA.merge(datatableB)
Try
dataTableA=datatableB.Copy
Got this info from here
https://msdn.microsoft.com/en-us/library/system.data.datatable.copy(v=vs.110).aspx
Your both datatables are being created separately, independent of each other. In this context you can use following DataTable and LINQ operations such as
Intersect to find matching rows in both DataTables,
Except to find unique rows only in one table
ImportRow to copy specific rows of one DataTable to another.
Copy to copy an entire datatable into another
It could be like this
Dim dtA As DataTable 'Your original Table A
Dim dtB As DataTable 'Your modified Table B
Dim dtC As DataTable 'Unchanged Rows
Dim dtD As DataTable 'Final synchronized Table
Dim dtE As DataTable 'Deleted Rows
'Populate your Table A and Table B into dtA and dtB
'get unchanged rows into dtC (all rows - changed - deleted)
dtC = dtA.AsEnumerable.Intersect(dtB.AsEnumerable, DataRowComparer.Default).CopyToDataTable
'Copy all unchanged rows to final table
dtD = dtC.Copy
'Copy the structure to blank datatable
dtE = dtC.Clone
'process modified rows (changed + deleted) i.e. (all rows - unchanged rows)
For Each cdRow As DataRow In dtA.AsEnumerable.Except(dtC.AsEnumerable, DataRowComparer.Default).CopyToDataTable.Rows
'check if this row exists in modified rows
If dtB.Select("cid = " & cdRow.Item("cid").ToString).Count > 0 Then
'This is a modified row copy it to the final table or process it to database
dtD.ImportRow(dtB.Select("cid = " & cdRow.Item("cid").ToString).CopyToDataTable.Rows(0))
Else
'This is a deleted row copy it to the deleted records table or process it directly to the database
dtE.ImportRow(cdRow)
End If
Next
'Now dtE contains your deleted rows
'Finally dtD is your synchronized datatable
Assumptions and other details:
All DataTables should have same table structure
This code assumes that ID column is unique to identify new and old rows.
You will still have to process deleted rows (dtE) from original database.
DataTable dtD, dtE can be avoided, but just for the purpose of simplicity and better understanding of the concept these are included.
Instead of later processing all records in dtD and dtE in a separate loop you can directly update them to the database as mentioned in code above.
There may be memory, resources, processing/timing related issues in case you are processing large DataTables

Spitting long column values to managable size for presenting data neatly

Hi I was wondering if there is a way to split long column values in this case I am using SSRS to get the distinct values with the number of product ID against a category into a matrix/pivot table in SSRS. The problem lies with the amount of distinct category makes it a nightmare to make the report look pretty shall we say. Is there a dynamic way to split the columns in say groups of 10 to make the table look nicer and easy to read. I was thinking of using in operator then the list of values but that means managing the data every time a new category gets added. Is there a dynamic way to present the data in the best way possible? There are 135 distinct category values
Also I am open to suggestions to make the report to nicer if anyone has any thoughts. I am new to SSRS and trying to get to grips with its.
Here is an example of my problem
enter image description here
Are your column names coming back from the database under the SubCat field you note in the comments above? If so I imagine your dataset looks something like this
Subcat | Logno
---------+---------------
SubCatA | 34
SubCatB | 65
SubCatC | 120
SubCatD | 8
SubCatE | 19
You can edit this so that there is an index of each individual category being returned also, using the Row_Number() function. Add the field
ROW_NUMBER() OVER (ORDER BY SubCat ASC) AS ColID
To your query. This will result in the following.
Subcat | LogNo | ColID
-----------+--------------+----------
SubCatA | 34 | 1
SubCatB | 65 | 2
SubCatC | 120 | 3
SubCatD | 8 | 4
SubCatE | 19 | 5
Now there is a numeric identifier for each column you can perform some logic on it to arrange itself nicely on the page.
This solution involves a Tablix, nested inside a Matrix nested inside a Matrix as follows
First create a Matrix (Matrix1), and set it’s datasource to your dataset. Set the Row Group Properties to group on the following expression where ‘4’ is the number of columns you wish to display horizontally.
=CInt(Floor((Fields!ColID.Value - 1) / 4))
Then in the data section of the Matrix (bottom right corner) insert a rectangle and on this insert a new Matrix (Matrix 2). Remove the leftmost row. Set the column header to be the Column Name SubCat. This will automatically set the column grouping to be SubCat.
Finally, in the Data Section of Matrix 2 add a new Rectangle and Add a Tablix on it. Remove the Header Row, and set it to be one column wide only. Set the Data to be the information you wish to display, i.e. LogNo.
Finally, delete the Leftmost and Topmost rows/columns from Matrix 1 to make it look tidier (Note Delete Column Row only! Not associated groups!)
Then when the report is run it should look similar to the following. Note in my example SubCat = ColName, and LogNo = NumItems, and I have multiple values per SubCat.
Hopefully you find this helpful. If not, please ask for clarification.
Can you do something like this:
The following gives the steps (in two columns, down then across)

How do I limit multi-select options for one field based on data entered into another field when they both reference the IDs of another table?

I have two tables, we'll call them T1 and T2. T1 currently has nearly 600 records in it, one of which contains the ID number and another of which contains a title, so ID and TITLE:
T1
ID | TITLE
-----|----------
1 | Title ABC
... | ...
201 | Title XYZ
... | ...
411 | Title 123
T2 has an ID field, a Titles field, an Accepted Titles field, and a Rejected Titles field, so ID, TITLES, ACCEPTED TITLES, and REJECTED TITLES. The access form uses a multiple select ListBox to select one or more TITLES from T1, however many are required, but usually no more than ten. Once entries are made into the TITLES field of T2, which is Numeric for the record IDs corresponding to the titles selected from T1, I want a combo box for each of the ACCEPTED TITLES and REJECTED TITLES in T2 to be limited to showing only those titles that correspond to IDs entered into the TITLES field. So, if I have in the TITLES field of T2,
T2
ID | TITLES | ACCEPTED TITLES | REJECTED TITLES
---|---------------|------------------|----------------
1 | 1, 201, 411 | |
I want the dropdown for the ComboBox to show only the titles corresponding to those IDs entered into the TITLES field. So, taking the ACCEPTED TITLES field, it might look like this:
T2
ID | TITLES | ACCEPTED TITLES | REJECTED TITLES
---|---------------|--------------------|---------------
1 | 1, 201, 411 | | [ ] Title ABC \/|
| [ ] Title XYZ |
| [ ] Title 123 |
I'm thinking I should be able to build a SELECT WHERE IN (...) statement that I can use in the "Row Source" properties of ACCEPTED TITLES and REJECTED TITLES. Then the list would be as short as the items selected for TITLES rather than 600+ records long. This also completely eliminates the potential for erroneous input under ACCEPTED TITLES or REJECTED TITLES since those titles can only be selected from those entered under TITLES. But, I don't yet know how to build such a SELECT statement.
Any assistance will be appreciated. Thanks for your time.
I propose a slightly different design for T2 (TitleStatus).
Create Table [TitleStatus] ([TitleID] Number, [StatusID] Number);
Create Index TitleIDindex ON [TitleStatus] (TitleID) WITH PRIMARY;
Then another table to house the statuses. Something like
Create Table [Statuses] ([StatusID] Number, [StatusText] String);
Create Index StatusIDindex ON [Statuses] (StatusID) WITH PRIMARY;
(this table could be organized with one column, but either way you want to prevent statuses like 'Accepted' and 'Accepted Title' from creeping in. Then you'd have two records which are the same thing but you don't naturally know to look for both)
Then T2 (TitleStatus) will look like
TitleStatus
TITLEID | STATUSID
----------|----------
1 | 1
... | ...
201 | 1
... | ...
411 | 1
500 | 2
Where Statuses Looks like
StatusID | StatusText
1 | Accepted
2 | Rejected
You're inserts should be fairly straight forward form there.
You can get all the accepted titles like this
Select T1.Title, Status.StatusText
From T1
Inner join TitleStatus TS on TS.TitleID = T1.ID
Inner Join Statuses S on S.StatusID = TS.StatusID
Where S.StatusText = 'Accepted'
I found the solution. It's actually much simpler than I realized:
Public Function GetTitleIDs() As String
Dim IDData As ADODB.Recordset
Dim SubLookup As Variant
Dim SelectSubmissions As String
' Should pull the same records and in the same order as '
' those found in originating Listbox '
Set IDData = CurrentProject.Connection.Execute("SELECT [Poems].[ID], [Poems].[Title] FROM Poems ORDER BY [Title];")
SubLookup = IDData.GetRows
' Submissions is the name of my originating Listbox. The rest remains unaltered. '
Dim listrow As Integer
For listrow = 0 To Me.Submissions.ListCount - 1
If Me.Submissions.Selected(listrow) = True Then
SelectSubmissions = ", " & SubLookup(0, listrow) & SelectSubmissions
End If
Next
If Len(SelectSubmissions) > 0 Then
SelectSubmissions = Right(SelectSubmissions, Len(SelectSubmissions) - 1)
End If
GetTitleIDs = SelectSubmissions
End Function
Private Sub Form_Current()
' Needed to update existing Listbox entry or entries when record is loaded '
' in form. If record is new, Listbox(es) will simply contain no records. This '
' can source all records for the fields you want in order to show existing '
' Listbox entries without fail. '
Me.Declined.RowSource = "SELECT [Poems].[ID], [Poems].[Title], [Poems].[Year Completed], [Poems].[Blog Location] FROM Poems ORDER BY [Title];"
Me.Accepted.RowSource = "SELECT [Poems].[ID], [Poems].[Title], [Poems].[Year Completed], [Poems].[Blog Location] FROM Poems ORDER BY [Title];"
End Sub
Private Sub Declined_GotFocus()
' Presumably your subset selection list gets focus after '
' checking list entries in originating Listbox. '
Me.Declined.RowSource = "SELECT [Poems].[ID], [Poems].[Title], [Poems].[Year Completed], [Poems].[Blog Location] FROM Poems WHERE [ID] IN (" & GetTitleIDs & ") ORDER BY [Title];"
End Sub
Private Sub Accepted_GotFocus()
Me.Accepted.RowSource = "SELECT [Poems].[ID], [Poems].[Title], [Poems].[Year Completed], [Poems].[Blog Location] FROM Poems WHERE [ID] IN (" & GetTitleIDs & ") ORDER BY [Title];"
End Sub
This will show only options in Declined and Accepted that are checked under Submissions, limiting selection options to only those values of interest for these fields. It's perfect.
So long as the table and ordering is the same for both the records assigned to IDData and the ListBox you're checking against, the IDs will always line up correctly.

SQL count returning wrong values

Was asked the question in regards to a olympic database.
"For each competitor who was in more than one event, list their given-name, family-name, their number of events, and their best and worst places (and no other data).
List this information in in descending order of their number of events (i.e. most events first),
and (when competitors have the same number of events) then in ascending order of best place,
and (when the same best place) then in ascending order of worst place,
and (when the same worst place) then in ascending order of their family-name."
I have been trying to write the query to do this, however it does not return the correct count. It stops at a count of 2 and does not return the correct min or max values.
The Tables are:
Competitors Table
Competitornum | Givenname | Familyname | Gender | Dateofbirth | CountryCode |
Results Table has
Eventid | Competitornum | Place | Lane | Elapsedtime | Note |
Query
SELECT c.Givenname, Familyname, COUNT(r.Competitornum), MIN(r.Place), MAX(r.Place)
FROM Competitors c
JOIN Results r
ON c.Competitornum=r.Competitornum
Group by c.Givenname, familyname, place
Having Count(r.Competitornum) > 1
This is the query I have come up with so far and am not sure what I have wrong.
You don't want to include place in your GROUP BY clause.
And you will need an ORDER BY clause to meet all the requirements.
SELECT c.GivenName, c.FamilyName, COUNT(r.CompetitorNum) events,
MIN(r.Place) best_place, MAX(r.Place) worst_place
FROM Competitors c
JOIN Results r ON c.CompetitorNum = r.CompetitorNum
GROUP BY c.GivenName, c.FamilyName
HAVING COUNT(r.Competitornum) > 1
ORDER BY events DESC, best_place ASC, worst_place ASC, c.FamilyName ASC;
Try this, remove place from the GROUP BY
Group by c.Givenname, familyname
remove the place field from the group by clause and then try to run your query.