"Transposing" a table to multiple columns - vba

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

Related

SQL how to find a multi column maximum in a group?

How can I write an SQL query (DB2) that will run on this table:
| A | B | C | V |
+---+---+---+----+
| | | | |
| 1 | 1 | 1 | k1 |
| | | | |
| 1 | 1 | 2 | k1 |
| | | | |
| 1 | 2 | 3 | k2 |
| | | | |
| 2 | 3 | 4 | k2 |
| | | | |
| 1 | 2 | 3 | k3 |
| | | | |
| 1 | 3 | 5 | k3 |
| | | | |
| 1 | 4 | 6 | k3 |
+---+---+---+----+
and produce this result
+---+---+---+----+
| A | B | C | V |
+---+---+---+----+
| | | | |
| 1 | 1 | 2 | k1 |
| | | | |
| 2 | 3 | 4 | k2 |
| | | | |
| 1 | 4 | 6 | k3 |
+---+---+---+----+
that is it will select rows based on a max of a "tuple" (A,B,C) in a group:
or for two rows R1, R2 :
if R1.A <> R2.A return Row where A = Max(R1.A,R2.A)
if R2.B <> R2.B return Row where B = Max(R1.B,R2.B)
return Row where C = Max(R1.C,R2.C)
I think row_number() does what you want -- if by "group" you mean V:
select t.*
from (select t.*,
row_number() over (partition by v order by a desc, b desc, c desc) as seqnum
from t
) t
where seqnum = 1;

Finding first occurence of multiples value in every group sort by date in SQL

I have a table with every operations that appends before an event group by another value.
There is only 3 operations: R, E, P
+ ----------+----------+-----------+------------------------+
| Rollcycle | Blocking | Operation | Order |
+ ----------+----------+-----------+------------------------+
| 1 | 3 | R | 4 |
| 1 | 3 | P | 3 |
| 1 | 3 | E | 2 |
| 1 | 3 | R | 1 |
| 1 | 2 | P | 3 |
| 1 | 2 | E | 2 |
| 1 | 2 | R | 1 |
| 1 | 1 | R | 1 |
| 2 | 1 | E | 2 |
| 2 | 1 | R | 1 |
+ ----------+----------+-----------+------------------------+
I want to know which operations occurs before every blocking group by Rollcycle.
I need to do this in access SQL.
Output
+ ----------+----------+---+---+---+
| Rollcycle | Blocking | R | E | P |
+ ----------+----------+---+---+---+
| 1 | 1 | 1 | 0 | 0 |
| 1 | 2 | 0 | 1 | 1 |
| 1 | 3 | 1 | 0 | 0 |
| 2 | 1 | 1 | 1 | 0 |
+ ----------+----------+---+---+---+
I could not find anything similar. It's maybe too specific.
Please help :)
EDIT: back to original table
If I followed you correctly, you can filter on records where order is greater or equal than blocking, and then do conditional aggregation:
select
rollcycle,
blocking,
max(iif(operation = 'R', 1, 0)) R,
max(iif(operation = 'E', 1, 0)) E,
max(iif(operation = 'P', 1, 0)) P
from mytable
where order >= blocking
group by rollcycle, blocking

Avoid repeated values in a join

I have two tables - a header and a matrix/details.
*Header Table* *Matrix / Details Table*
+----+--------+-----+ +----+--------+------+
| ID | Parent | Qty | | ID | Child | Qty |
+----+--------+-----+ +----+--------+------+
| 1 | A | 10 | | 1 | X | 100 |
| 2 | B | 20 | | 1 | Y | 1000 |
| 3 | C | 30 | | 2 | X | 200 |
+----+--------+-----+ | 2 | Y | 2000 |
| 3 | X | 30 |
| 3 | Y | 300 |
| 3 | Z | 3000 |
+----+--------+------+
I'm Joining these two tables based on ID.
I don't want the result to have duplicated values from header table.
I expect a result like following:
*Current Result* *Expected Result*
+----+--------+-----+-------+------+ +----+--------+-----+-------+------+
| ID | Parent | Qty | Child | Qty | | ID | Parent | Qty | Child | Qty |
+----+--------+-----+-------+------+ +----+--------+-----+-------+------+
| 1 | A | 10 | X | 100 | | 1 | A | 10 | X | 100 |
| 1 | A | 10 | Y | 1000 | | | | | Y | 1000 |
| 2 | B | 20 | X | 200 | | 2 | B | 20 | X | 200 |
| 2 | B | 20 | Y | 2000 | | | | | Y | 2000 |
| 3 | C | 30 | X | 30 | | 3 | C | 30 | X | 30 |
| 3 | C | 30 | Y | 300 | | | | | Y | 300 |
| 3 | C | 30 | Z | 3000 | | | | | Z | 3000 |
+----+--------+-----+-------+------+ +----+--------+-----+-------+------+
Is this possible? If not any, alternate solution available?
Thanks in advance...
If you are using SQL Server,Try with the below query.
;WITH CTE_1
AS
(SELECT *,ROW_NUMBER()OVER(PARTITION BY ID,Parent,Quantity ORDER BY ID ) RNO
FROM Header H
JOIN [Matrix / Details] M
ON H.ID=M.ID)
SELECT CASE WHEN RNO=1 THEN CAST(ID as VARCHAR(50)) ELSE '' END ID,
CASE WHEN RNO=1 THEN Parent ELSE '' END Parent,
CASE WHEN RNO=1 THEN cast(Quantity as VARCHAR(50)) ELSE '' END Quantity,
Child,Qty
FROM CTE_1
ORDER BY ID,Parent,Quantity

Map column data to matching rows

I have a sheet like this:
| A | B | C | D | E | F | G | H | ...
---------------------------------
| a | 1 | | b | 2 | | c | 7 |
---------------------------------
| b | 2 | | c | 8 | | b | 4 |
---------------------------------
| c |289| | a | 3 | | a |118|
---------------------------------
| d | 6 | | e | 3 | | e |888|
---------------------------------
| e | 8 | | d |111| | d |553|
---------------------------------
I want the sheet to become like this:
| A | B | C | D | E | F | G | H | ...
---------------------------------
| a | 1 | 3 |118| | | | |
---------------------------------
| b | 2 | 2 | 4 | | | | |
---------------------------------
| c |289| 8 | 7 | | | | |
---------------------------------
| d | 6 |111|553| | | | |
---------------------------------
| e | 8 | 3 |888| | | | |
---------------------------------
Col A, Col B and Col G have letters which are unique, and in the col next to it it has weights.
To make it even more clear,
| A | B |
---------
| a | 1 |
---------
| b | 2 |
---------
| c |289|
...
are the weights of a,b,c... in January
Similarly | D | E | are weights of a,b,c... in July and | G | H | are weights of a,b,c... in December
I need to put them side-by-side for comparison, the thing is they are NOT in order.
How do I approach this?
UPDATE
There are thousands of a,b,c, aa, bb, cc, aaa, avb, as, saf, sfa etc.. and some of them MAY be present in January (Col A) and not in July (Col D)
Something like this
code
Sub Squeeze()
[c1:c5] = Application.Index([E1:E5], Evaluate("IF(A1:A5<>"""",MATCH(A1:A5,D1:D5,0),A1:A5)"), 1)
[d1:d5] = Application.Index([H1:h5], Evaluate("IF(A1:A5<>"""",MATCH(A1:A5,G1:G5,0),A1:A5)"), 1)
[e1:h5].ClearContents
End Sub
Explanation of first line
Application.Index([E1:E5], Evaluate("IF(A1:A5<>"""",MATCH(A1:A5,D1:D5,0),A1:A5)"), 1)
The MATCH returns a VBA array matching the positions (5) of A1:A5 against D1:D5
INDEX then returns the corresponding values from E1:E5
So to use the key column of A1:A100 against M1:100 with values in N1:100
Application.Index([N1:N100], Evaluate("IF(A1:A100<>"""",MATCH(A1:A100,M1:M100,0),A1:A100)"), 1)
Extend as necessary: Sort D:E by D ascending, sort G:H by G ascending, delete G,F,D,C. If you want VBA, do this with Record Macro selected.

dynamic html table with column span and row span in 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