dynamic html table with column span and row span in vb.net - vb.net

If I have a value from sqlDataReader as below:
| YEAR1 | NAM1 | CRDT1 | SEMER1 | YEAR2 | NAM2 | CRDT2 | SEMER2 |
-----------------------------------------------------------------------
| 1 | Name1 | 1 | 1 | 1 | Name10 | 1 | 2 |
| 1 | Name2 | 4 | 1 | 1 | Name5 | 4 | 2 |
| 1 | Name3 | 2 | 1 | 1 | Name6 | 3 | 2 |
| 1 | Name4 | 7 | 1 | 1 | Name7 | 6 | 2 |
| (null) | (null) | (null) | (null) | 1 | Name8 | 1 | 2 |
| (null) | (null) | (null) | (null) | 1 | Name9 | 1 | 2 |
| 2 | Name11 | 3 | 1 | 2 | Name14 | 2 | 2 |
| 2 | Name12 | 6 | 1 | 2 | Name15 | 1 | 2 |
| 2 | Name13 | 4 | 1 | 2 | Name16 | 1 | 2 |
| (null) | (null) | (null) | (null) | 2 | Name17 | 1 | 2 |
| 3 | Name18 | 5 | 1 | 3 | Name18 | 5 | 2 |
| 3 | Name19 | 1 | 1 | 3 | Name19 | 1 | 2 |
| 3 | Name20 | 1 | 1 | 3 | Name20 | 1 | 2 |
I like to merge year column to become only one column. If any columns is null then it will merge into one column and If any the current rows and the next rows is null then it will merge.
I want to output like this:
| YEAR1 | NAM1 | CRDT1 | SEMER1 | YEAR2 | NAM2 | CRDT2 | SEMER2 |
-----------------------------------------------------------------------
| | Name1 | 1 | 1 | | Name10 | 1 | 2 |
| 1 | Name2 | 4 | 1 | 1 | Name5 | 4 | 2 |
| | Name3 | 2 | 1 | | Name6 | 3 | 2 |
| | Name4 | 7 | 1 | | Name7 | 6 | 2 |
| (null) | | Name8 | 1 | 2 |
| | | Name9 | 1 | 2 |
| | Name11 | 3 | 1 | | Name14 | 2 | 2 |
| 2 | Name12 | 6 | 1 | 2 | Name15 | 1 | 2 |
| | Name13 | 4 | 1 | | Name16 | 1 | 2 |
| (null) | | Name17 | 1 | 2 |
| | Name18 | 5 | 1 | | Name18 | 5 | 2 |
| 3 | Name19 | 1 | 1 | 3 | Name19 | 1 | 2 |
| | Name20 | 1 | 1 | | Name20 | 1 | 2 |
How can I create a html table which can output the result as above in vb.net?

Spin through the result set adding a new table row for each record. Use variables to keep track of when you need to add all of your cells (some with rowspans) or just the ones displayed on every row. Here's 90% of the code for you. You should be able to figure out the rest.
'Convert DataReader into DataTable.
Dim dtResults As New DataTable
dtResults.Load(drResults)
'You can define this in your .Aspx
Dim tblStudents As New Table
'These will help you with your table building logic.
Dim intCurrentYear1 As Integer = 0
Dim intRowSpan As Integer = 0
Dim bolDetermineRowSpan = True
For intRowCursor As Integer = 0 To (dtResults.Rows.Count - 1)
If bolDetermineRowSpan Then
'First get the current year (Nulls will be set to 0, but not displayed as such.)
If dtResults.Rows(intRowCursor).Item("Year1") Is DBNull.Value Then
intCurrentYear1 = 0
Else
intCurrentYear1 = CInt(dtResults.Rows(intRowCursor).Item("Year1"))
End If
If intCurrentYear1 > 0 Then
'Get the total number of records with this year, so we know how many cells to merge.
intRowSpan = (From d As DataRow In dtResults.Rows _
Where Not d.Item("Year1") Is DBNull.Value AndAlso _
CInt(d.Item("Year1")) = intCurrentYear1).Count()
Else
'Figure out how many null records until the next year, so we know how many to merge.
Dim bolNextYear As Boolean = False
Dim intTempCursor As Integer = intRowCursor + 1
intRowSpan = 1
Do Until bolNextYear = True OrElse intTempCursor = dtResults.Rows.Count
If Not dtResults.Rows(intTempCursor).Item("Year1") Is DBNull.Value Then
bolNextYear = True
End If
intTempCursor += 1
intRowSpan += 1
Loop
End If
End If
Dim tr As New TableRow
If intCurrentYear1 > 0 Then
If bolDetermineRowSpan = True Then
'Add all cells to this Table Row, using RowSpan property to merge desired fields.
Dim tdYear1 As New TableCell
tdYear1.RowSpan = intRowSpan
tdYear1.Text = intCurrentYear1
tdYear1.VerticalAlign = VerticalAlign.Middle
tr.Cells.Add(tdYear1)
Dim tdName1 As New TableCell
tdName1.Text = CStr(dtResults.Rows(intRowCursor).Item("NAM1"))
tr.Cells.Add(tdName1)
'Add the rest of your cells here (omitted).
'Update this variable to keep sound logic.
bolDetermineRowSpan = False
Else
'Do not add the Year1 Cell because of RowSpan/"Merging".
Dim tdName1 As New TableCell
tdName1.Text = CStr(dtResults.Rows(intRowCursor).Item("NAM1"))
tr.Cells.Add(tdName1)
'Do for rest of cells.
'Update this variable to keep sound logic.
bolDetermineRowSpan = False
End If
Else
'Same logic as other half of this If Statement block, just doing more
'cells with RowSpans. (These are the null records.)
If bolDetermineRowSpan = True Then
'Add all cells (with rowspans)
Else
'Add just cells that are displayed on every row.
End If
End If
'Add the row to the table.
tblStudents.Rows.Add(tr)
'Do we need to reset our boolean flag to determine row span?
If bolDetermineRowSpan = False AndAlso dtResults.Rows.Count < (intRowCursor + 1) AndAlso _
Not dtResults.Rows(intRowCursor + 1).Item("Year1") Is DBNull.Value AndAlso _
dtResults.Rows(intRowCursor + 1).Item("Year1") <> intCurrentYear1 Then
bolDetermineRowSpan = True
End If
Next

Related

How to assign duplicate increment in SQL?

While going through SQL columns, if we find text match "NEW" in Calc column, update the incrementing a count starting with 1 in Results column.
It should look like this on the output:
The following uses an id column to resolve the order issue. Replace that with your corresponding expression. This also addresses the requirement to start the display sequence with 1 and also show 0 for the 'NEW' rows.
The SQL (updated):
SELECT logs.*
, CASE WHEN text = 'NEW' THEN 0
ELSE
COALESCE(SUM(CASE WHEN text = 'NEW' THEN 1 END) OVER (PARTITION BY xrank ORDER BY id)+1, 1)
END AS display
FROM logs
ORDER BY id
The result:
+----+-------+------+---------+
| id | xrank | text | display |
+----+-------+------+---------+
| 1 | 1 | A | 1 |
| 2 | 1 | B | 1 |
| 3 | 1 | C | 1 |
| 4 | 1 | NEW | 0 |
| 5 | 1 | D | 2 |
| 6 | 1 | Q | 2 |
| 7 | 1 | B | 2 |
| 8 | 1 | NEW | 0 |
| 9 | 1 | D | 3 |
| 10 | 1 | Z | 3 |
| 11 | 2 | A | 1 |
| 12 | 2 | B | 1 |
| 13 | 2 | C | 1 |
| 14 | 2 | NEW | 0 |
| 15 | 2 | D | 2 |
| 16 | 2 | Q | 2 |
| 17 | 2 | B | 2 |
| 18 | 2 | NEW | 0 |
| 19 | 2 | D | 3 |
| 20 | 2 | Z | 3 |
+----+-------+------+---------+
You need a column that specifies the ordering for the table. With that, just use a cumulative sum:
select t.*,
1 + sum(case when Calc = 'NEW' then 1 else 0 end) over (partition by Rank_Id order by Seq) as display
from t;

dense_rank over boolean column

Good day. I have a permutated table with condition and I am running redshift DB. This is a table with events log and I splitted it into session start (bool = 1) and session continue (bool = 0) like this:
=======================
| ID | BOOL |
=======================
| 1 | 0 |
| 2 | 1 |
| 3 | 0 |
| 4 | 0 |
| 5 | 0 |
| 6 | 0 |
| 7 | 0 |
| 8 | 0 |
| 9 | 0 |
| 10 | 0 |
| 11 | 1 |
| 12 | 0 |
| 13 | 0 |
| 14 | 1 |
| 15 | 0 |
| 16 | 0 |
=======================
I need to create sesssion_id column with something like dense_rank:
================================
| ID | BOOL | D_RANK |
================================
| 1 | 0 | 1 |
| 2 | 1 | 2 |
| 3 | 0 | 2 |
| 4 | 0 | 2 |
| 5 | 0 | 2 |
| 6 | 0 | 2 |
| 7 | 0 | 2 |
| 8 | 0 | 2 |
| 9 | 0 | 2 |
| 10 | 0 | 2 |
| 11 | 1 | 3 |
| 12 | 0 | 3 |
| 13 | 0 | 3 |
| 14 | 1 | 4 |
| 15 | 0 | 4 |
| 16 | 0 | 4 |
================================
Is there any option to do this? Would appreciate any help.
Use a cumulative sum. Assuming that bool is the start of a new session:
select t.*,
sum(bool) over (order by id) as session_id
from t;
Note: This will start at 0. You can add 1 if you need.

SQL - Partition restarted based on a column value

I need to create a new column that restarts at every 0 value of Column Repeated Call of each Customer_ID:
+-------------+---------+----------------------+---------------+
| Customer_ID | Call_ID | Days Since Last Call | Repeated Call |
+-------------+---------+----------------------+---------------+
| 1 | 1 | Null | 0 |
| 1 | 2 | 45 | 0 |
| 1 | 3 | 0 | 1 |
| 1 | 4 | 0 | 1 |
| 1 | 5 | 0 | 1 |
| 1 | 6 | 48 | 0 |
| 1 | 7 | 1 | 1 |
| 2 | 8 | Null | 0 |
| 2 | 9 | 1 | 1 |
+-------------+---------+----------------------+---------------+
In to something like this:
+-------------+---------+----------------------+---------------+-------------+
| Customer_ID | Call_ID | Days Since Last Call | Repeated Call | Order_Group |
+-------------+---------+----------------------+---------------+-------------+
| 1 | 1 | Null | 0 | 1 |
| 1 | 2 | 45 | 0 | 2 |
| 1 | 3 | 0 | 1 | 2 |
| 1 | 4 | 0 | 1 | 2 |
| 1 | 5 | 0 | 1 | 2 |
| 1 | 6 | 48 | 0 | 3 |
| 1 | 7 | 1 | 1 | 3 |
| 2 | 8 | Null | 0 | 1 |
| 2 | 9 | 1 | 1 | 1 |
+-------------+---------+----------------------+---------------+-------------+
Appreciate your suggestion, thanks!
You can use SUM() window function:
select t.*,
sum(case when Repeated_Call = 0 then 1 else 0 end)
over (partition by Customer_ID order by Call_Id) Order_Group
from tablename t
See the demo (for MySql but it is standard SQL).
Results:
| Customer_ID | Call_ID | Days Since Last Call | Repeated_Call | Order_Group |
| ----------- | ------- | -------------------- | ------------- | ----------- |
| 1 | 1 | | 0 | 1 |
| 1 | 2 | 45 | 0 | 2 |
| 1 | 3 | 0 | 1 | 2 |
| 1 | 4 | 0 | 1 | 2 |
| 1 | 5 | 0 | 1 | 2 |
| 1 | 6 | 48 | 0 | 3 |
| 1 | 7 | 1 | 1 | 3 |
| 2 | 8 | | 0 | 1 |
| 2 | 9 | 1 | 1 | 1 |
You can calculation every 0 value in column Repeated Call (for each customer) using window analytic function COUNT with ROWS UNBOUNDED PRECEDING:
SELECT *,
COUNT(CASE WHEN Repeated Call=0 THEN 1 ELSE NULL END )OVER(PARTITION BY Customer_ID
ORDER BY Call_ID ROWS UNBOUNDED PRECEDING)Order_Gr FROM Table

Make a new column based from CASE and GROUP BY result

I have table like this called, table test:
+------------------------+--------+
| id_laporan_rekomendasi | status |
+------------------------+--------+
| 1 | 2 |
| 1 | 2 |
| 1 | 2 |
| 1 | 3 |
| 2 | 2 |
| 2 | 2 |
| 2 | 2 |
| 2 | 3 |
| 3 | 2 |
| 3 | 3 |
| 4 | 2 |
| 5 | 2 |
| 5 | 3 |
| 6 | 2 |
+------------------------+--------+
I want to group by id_laporan_rekomendasi and make a new column when in column status there is value 3. so if there is no value 3 in column status, then the value would be 0, but if there is value 3 than 1.
I expect the result would be like this
+------------------------+------+
| id_laporan_rekomendasi | test |
+------------------------+------+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 4 | 0 |
| 5 | 1 |
| 6 | 0 |
+------------------------+------+
I have tried this query
SELECT t1.id_laporan_rekomendasi,
COUNT(distinct case when t1.status = 3 then 1 else 0 end) as test
FROM test t1
GROUP BY t1.id_laporan_rekomendasi
But i got the result like below
+------------------------+------+
| id_laporan_rekomendasi | test |
+------------------------+------+
| 1 | 2 |
| 2 | 2 |
| 3 | 2 |
| 4 | 1 |
| 5 | 2 |
| 6 | 1 |
+------------------------+------+
Does anyone could help me with this table ?
You are close. In MariaDB, you can simplify this to:
SELECT t1.id_laporan_rekomendasi,
MAX( t1.status = 3 ) as test
FROM test t1
GROUP BY t1.id_laporan_rekomendasi;
MariaDB (and MySQL) treat boolean expressions as numbers, with "1" for true and "0" for false. So this does what you want.

"Transposing" a table to multiple columns

I have a table looks like this:
+---+---+---+---+---+---+
| | a | b | c | d | e |
+---+---+---+---+---+---+
| f | 1 | 3 | 3 | 2 | 2 |
| g | 3 | 1 | 3 | 2 | 1 |
| h | 3 | 3 | 1 | 3 | 3 |
| i | 2 | 2 | 3 | 1 | 2 |
| j | 2 | 1 | 3 | 2 | 1 |
+---+---+---+---+---+---+
And I want it to be like this:
+---+---+---+
| a | f | 1 |
| a | g | 3 |
| a | h | 3 |
| a | i | 2 |
| a | j | 2 |
| b | f | 3 |
| b | g | 1 |
| b | h | 3 |
| b | i | 2 |
| b | j | 1 |
| c | f | 3 |
| c | g | 3 |
| c | h | 1 |
| c | i | 3 |
| c | j | 3 |
| d | f | 2 |
| d | g | 2 |
| d | h | 3 |
| d | i | 1 |
| d | j | 2 |
| e | f | 2 |
| e | g | 1 |
| e | h | 3 |
| e | i | 2 |
| e | j | 1 |
+---+---+---+
I'm using this macro:
Sub ColumnCopy()
Sheets("test").Cells.Clear
Dim tRow As Long
Dim source As String
Dim target As String
source = "test1" 'Set your source sheet here
target = "test" 'Set the Target sheet name
'tRow = 2 'Define the start row of the target sheet
'Get Last Row and Column
lastRow = Sheets(source).Range("A" & Rows.Count).End(xlUp).Row
lastCol = Sheets(source).Cells(1, Columns.Count).End(xlToLeft).Column
tRow = 2
colBase = 2
Do While colBase < lastCol
For iRow = 2 To lastRow
Sheets(target).Cells(tRow, 1) = Sheets(source).Cells(1, colBase)
Sheets(target).Cells(tRow, 2) = Sheets(source).Cells(iRow, 1)
Sheets(target).Cells(tRow, 3) = Sheets(source).Cells(iRow, colBase)
tRow = tRow + 1
Next iRow
colBase = colBase + 1
Loop
End Sub
But i'm getting result missing the "column e":
+---+---+---+
| a | f | 1 |
| a | g | 3 |
| a | h | 3 |
| a | i | 2 |
| a | j | 2 |
| b | f | 3 |
| b | g | 1 |
| b | h | 3 |
| b | i | 2 |
| b | j | 1 |
| c | f | 3 |
| c | g | 3 |
| c | h | 1 |
| c | i | 3 |
| c | j | 3 |
| d | f | 2 |
| d | g | 2 |
| d | h | 3 |
| d | i | 1 |
| d | j | 2 |
+---+---+---+
I can't find what's causing this issue. I'm really new to excel macro.
Thanks for the help!
change this: Do While colBase < lastCol to this:
Do While colBase <= lastCol