My code looks at a table with three different Ids and a qty. It sums all rows where the three ids are the same and pastes it sheet2. The original table is always sorted with the three Id columns sorted in alphabetical order.
Original Table
+--------+------+---------+-----+
| Name | City | Country | Qty |
+--------+------+---------+-----+
| Jack | A | US | 15 |
| Jack | A | US | 16 |
| Kevin | A | US | 12 |
| Kevin | A | US | 11 |
| Jack | B | US | 13 |
| Jack | B | US | 10 |
+--------+------+---------+-----+
Output Table
+--------+------+---------+-----+
| Name | City | Country | Qty |
+--------+------+---------+-----+
| Jack | A | US | 31 |
| Kevin | A | US | 23 |
| Jack | B | US | 23 |
+--------+------+---------+-----+
Code
For i = 2 To totalrow 'totalrow is the total number of rows
name = sheet1.Cells(i, 1)
city = sheet1.Cells(i, 2)
country = sheet1.Cells(i, 3)
qty = 0
Do
qty = qty + sheet1.Cells(i, 4)
i = i + 1
Loop While wsDB.Cells(i, 1) = name And wsDB.Cells(i, 2) = city And wsDB.Cells(i, 3) = country
sheet2.Cells(process_row, 1).Value = name
sheet2.Cells(process_row, 2).Value = city
sheet2.Cells(process_row, 3).Value = country
sheet2.Cells(process_row, 4).Value = qty
process_row = process_row + 1
i = i - 1
Next I
I timed how long the code took to run and it consistently takes 30 seconds.
I then removed the section pasting the updated values into sheet2 and timed it (code below). That consistently took less than 1 second.
For i = 2 To totalrow
bl = wsDB.Cells(i, 1)
cpty = wsDB.Cells(i, 2)
cptyname = wsDB.Cells(i, 3)
cusip = wsDB.Cells(i, 4)
qty = 0
Do
qty = qty + wsDB.Cells(i, 6)
i = i + 1
Loop While wsDB.Cells(i, 1) = bl And wsDB.Cells(i, 2) = cpty And wsDB.Cells(i, 4) = cusip
process_row = process_row + 1
i = i - 1
Next i
It appears that inserting cell values requires the most time. Are there any methods in which I can speed this up?
Related
I have a source table that has a few different prices for each product (depending on the order quantity). Those prices are listed vertically, so each product could have more than one row to display its prices.
Example:
ID | Quantity | Price
--------------------------
001 | 5 | 100
001 | 15 | 90
001 | 50 | 80
002 | 10 | 20
002 | 20 | 15
002 | 30 | 10
002 | 40 | 5
The other table I have is the result table in which there is only one row for each product, but there are five columns that each could contain the quantity and price for each row of the source table.
Example:
ID | Quantity_1 | Price_1 | Quantity_2 | Price_2 | Quantity_3 | Price_3 | Quantity_4 | Price_4 | Quantity_5 | Price_5
---------------------------------------------------------------------------------------------------------------------------
001 | | | | | | | | | |
002 | | | | | | | | | |
Result:
ID | Quantity_1 | Price_1 | Quantity_2 | Price_2 | Quantity_3 | Price_3 | Quantity_4 | Price_4 | Quantity_5 | Price_5
---------------------------------------------------------------------------------------------------------------------------
001 | 5 | 100 | 15 | 90 | 50 | 80 | | | |
002 | 10 | 20 | 20 | 15 | 30 | 10 | 40 | 5 | |
Here is my Python/SQL solution for this (I'm fully aware that this could not work in any way, but this was the only way for me to show you my interpretation of a solution to this problem):
For Each result_ID In result_table.ID:
Subselect = (SELECT * FROM source_table WHERE source_table.ID = result_ID ORDER BY source_table.Quantity) # the Subselect should only contain rows where the IDs are the same
For n in Range(0, len(Subselect)): # n (index) should start from 0 to last row - 1
price_column_name = 'Price_' & (n + 1)
quantity_column_name = 'Quantity_' & (n + 1)
(UPDATE result_table
SET result_table.price_column_name = Subselect[n].Price, # this should be the price of the n-th row in Subselect
result_table.quantity_column_name = Subselect[n].Quantity # this should be the quantity of the n-th row in Subselect
WHERE result_table.ID = Subselect[n].ID)
I honestly have no idea how to do this with only SQL or VBA (those are the only languages I'd be able to use -> MS-Access).
This is a pain in MS Access. If you can enumerate the values, you can pivot them.
If we assume that price is unique (or quantity or both), then you can generate such a column:
select id,
max(iif(seqnum = 1, quantity, null)) as quantity_1,
max(iif(seqnum = 1, price, null)) as price_1,
. . .
from (select st.*,
(select count(*)
from source_table st2
where st2.id = st.id and st2.price >= st.price
) as seqnum
from source_table st
) st
group by id;
I should note that another solution would use data frames in Python. If you want to take that route, ask another question and tag it with the appropriate Python tags. This question is clearly a SQL question.
In SQL Server, I have one table with following data (tblUserSettings):
| CountryID | CityID | UserType | Value1 | Value2 | Value3 |
| 9 | 3 | 1 | 5 | 5 | 5 |
| 9 | 3 | 2 | NULL | NULL | NULL |
| 9 | 3 | 3 | 5 | 5 | 5 |
| 9 | 3 | 4 | 5 | 5 | 5 |
| 9 | 20 | 1 | 5 | 5 | 5 |
| 9 | 20 | 2 | NULL | NULL | NULL |
| 9 | 20 | 3 | 5 | 5 | 5 |
| 9 | 20 | 4 | 0 | 0 | 0 |
I need to compare all the values for all UserTypes from CityID = 20 with all the values for corresponding UserTypes from CityID = 3. The CountryID = 9. The columns to compare are: Value1, Value2, Value3.
I just need to know if all of them are matched to each other or not. I tried to do something as follows:
SELECT CASE WHEN ISNULL(t1.Value1, 0) = ISNULL(t2.Value1, 0) THEN 1 ELSE 0 END AS Match1,
CASE WHEN ISNULL(t1.Value2, 0) = ISNULL(t2.Value2, 0) THEN 1 ELSE 0 END AS Match2,
CASE WHEN ISNULL(t1.Value3, 0) = ISNULL(t2.Value3, 0) THEN 1 ELSE 0 END AS Match3
FROM tblUserSettings t1
INNER JOIN tblUserSettings t2 ON t1.CountryID = t2.CountryID
AND t1.UserType = t2.UserType
AND t1.CityID = 3
AND t2.CityID = 20
WHERE t1.CountryID = 9
And it gives me following result which I have to process further to define if everything matches or not.
| Match1 | Match2 | Match3 |
| 1 | 1 | 1 |
| 1 | 1 | 1 |
| 1 | 1 | 1 |
| 0 | 0 | 0 |
Can I do this in a way to have only one column and row in output - just receive either 1 for all the matches or 0 if at least one doesn't match?
If you are looking to get only one column with 1 when all the values match and 0 if atleast one doesn't, use,
SELECT
CASE WHEN ISNULL(t1.Value1, 0) = ISNULL(t2.Value1, 0)
AND ISNULL(t1.Value2, 0) = ISNULL(t2.Value2, 0)
AND ISNULL(t1.Value3, 0) = ISNULL(t2.Value3, 0)
THEN 1 ELSE 0 END AS Match
FROM tblUserSettings t1
INNER JOIN tblUserSettings t2 ON t1.CountryID = t2.CountryID
AND t1.UserType = t2.UserType
AND t1.CityID = 3
AND t2.CityID = 20
WHERE t1.CountryID = 9
If you are looking to compare all cities rather than just two you should be able to do this by grouping rather than joining.
Something like:
SELECT
CASE WHEN
max(Value1)-min(Value1) = 0
AND max(Value2)-min(Value2) = 0
AND max(Value3)-min(Value3) = 0
THEN 1 ELSE 0 AS Match
FROM tblUserSettings
GROUP BY CountryID,UserType
I have the following LINQ code which has a syntax error and i've no idea how to fix it
Dim query =
From row In mainDatatable.AsEnumerable
'the syntax Error is in the following line
Group row By new { row.Field(Of Double)("cprice") , row.Field(Of Integer)("ccategory")}
Into ProductGroups
'**************************
Dim grpNumber = 1
For Each grp In query
For Each row In grp.ProductGroups
row.SetField("gnum", grpNumber)
Next
grpNumber += 1
Next
End Sub
so what am i doing wrong?
Here is how the table looks like and i want to group by the columns cprice and ccategory
+---------------+---------------+-----------+------+
| Product Name | cprice | ccategory | gnum |
+---------------+---------------+-----------+------+
| Skirt Red | 99 | 1 | |
| Jeans Blue | 49 | 2 | |
| Jeans Black | 49 | 2 | |
| Skirt Blue | 99 | 1 | |
| T-shirt White | 20 | 2 | |
| T-shirt Green | 20 | 2 | |
| Jeans Grey | 49 | 2 | |
+---------------+---------------+-----------+------+
Try this:
new with { Key.Price = row.Field(Of Double)("cprice") ,
Key.Category = row.Field(Of Integer)("ccategory")
Uisng the Key modifier, this creates an anonymous type that implements equality based on equality of its members.
I am trying to initialize a multidimensional array. Here is my syntax; this does not create errors but it does not store all values either. Although it correctly prints out all records in this snippet,
dFirstWeek = CDate(FirstWeek)
dFirstWeek = DateAdd(DateInterval.WeekOfYear, -1, dFirstWeek)
Dim dFirstDay As Date
Dim arrWeekYear(5000, 1) As Date
Dim i As Integer = 0
Dim j As Integer = 0
dFirstDay = dFirstDay.AddDays(1)
While dFirstWeek <= dLastWeek
dFirstDay = dFirstWeek
dFirstWeek = dFirstWeek.AddDays(7)
While dFirstDay < dFirstWeek
arrWeekYear(i, j) = (dFirstWeek)
arrWeekYear(i, j + 1) = (dFirstDay)
Response.Write(arrWeekYear(i, j).ToString("d"))
Response.Write(" ;")
Response.Write(arrWeekYear(i, j + 1).ToString("d"))
Response.Write("<br>")
dFirstDay = dFirstDay.AddDays(1)
j = 0
End While
i = i + 1
End While
later in this code, I try to reprint this array as follows:
i = 0
j = 0
Dim k As Integer = 0
'Response.Write(arrWeekYear.GetLength(0))
While k < arrWeekYear.GetLength(0) - 2
Response.Write(arrWeekYear(i, j).ToString("d"))
Response.Write(" ;")
Response.Write(arrWeekYear(i, j + 1).ToString("d"))
Response.Write("<br>")
j = 0
i = i + 1
k = k + 1
End While
but this time, only one "j" record appears per "i" record. Why is this? And then after these few records, many dates resembling null dates appear: "1/1/0001 "
So why do all records appear in upper section, but not from lower? Did I insert values wrongly into this array? And it does not have to have fixed number of rows, just a fixed number of columns.
Dim arrWeekYear(5000, 1) As Date
That’s a multi-dimensional array all right, but the second dimension only has a size of 2! And you access it via j, which is always 0 in your code. That makes no sense. Is that really what you want? I suggest you use a data structure inside your array instead of a multi-dimensional array.
In fact, you almost never want to use a multi-dimensional array. At all. Complex objects are almost always more appropriate. The only real exception is when you actually want to store a mathematical matrix.
Increment i inside the inner loop, rather than inside the outer loop.
And even simpler code (requires visual studio 2010):
Dim baseDate As Datetime = Convert.ToDatetime(FirstWeek).AddDays(-7)
For Each item In Enumerable.Range(0, (dLastWeek - baseDate).TotalDays / 7) _
.Select(Function(i) New DateTime() _
{baseDate.AddDays(i*7), baseDate.AddDays(i*7 + 7) })
Response.Write(string.Format("{0:d} ;{1:d}<br/>", item(0), item(1)))
Next item
And since Response.Write() is frowned upon in asp.net, you can take this a step further and assign the Enumerable.Range() call as the datasource for an asp:repeater control, if you're using webforms rather than mvc.
In the first piece you fill the array writing 7 times a value in the same array line.
In a test (from 01/01/2011 to 03/03/2011) the first 21 assignments are:
i | j | value | j+1 | value
0 | 0 | 01/01/2011 | 1 | 25/12/2010
0 | 0 | 01/01/2011 | 1 | 26/12/2010
0 | 0 | 01/01/2011 | 1 | 27/12/2010
0 | 0 | 01/01/2011 | 1 | 28/12/2010
0 | 0 | 01/01/2011 | 1 | 29/12/2010
0 | 0 | 01/01/2011 | 1 | 30/12/2010
0 | 0 | 01/01/2011 | 1 | 31/12/2010 final value in array (0,0) and (0,1)
1 | 0 | 08/01/2011 | 1 | 01/01/2011
1 | 0 | 08/01/2011 | 1 | 02/01/2011
1 | 0 | 08/01/2011 | 1 | 03/01/2011
1 | 0 | 08/01/2011 | 1 | 04/01/2011
1 | 0 | 08/01/2011 | 1 | 05/01/2011
1 | 0 | 08/01/2011 | 1 | 06/01/2011
1 | 0 | 08/01/2011 | 1 | 07/01/2011 final value in array (1,0) and 1,1)
2 | 0 | 15/01/2011 | 1 | 08/01/2011
2 | 0 | 15/01/2011 | 1 | 09/01/2011
2 | 0 | 15/01/2011 | 1 | 10/01/2011
2 | 0 | 15/01/2011 | 1 | 11/01/2011
2 | 0 | 15/01/2011 | 1 | 12/01/2011
2 | 0 | 15/01/2011 | 1 | 13/01/2011
2 | 0 | 15/01/2011 | 1 | 14/01/2011 final value in array (2,0) and (2,1)
etc.
In the 2nd part you get the values written :
i | j | value | j+1 | value
0 | 0 | 01/01/2011 | 1 | 31/12/2010
1 | 0 | 08/01/2011 | 1 | 07/01/2011
2 | 0 | 15/01/2011 | 1 | 14/01/2011
3 | 0 | 22/01/2011 | 1 | 21/01/2011
4 | 0 | 29/01/2011 | 1 | 28/01/2011
5 | 0 | 05/02/2011 | 1 | 04/02/2011
6 | 0 | 12/02/2011 | 1 | 11/02/2011
7 | 0 | 19/02/2011 | 1 | 18/02/2011
8 | 0 | 26/02/2011 | 1 | 25/02/2011
9 | 0 | 05/03/2011 | 1 | 04/03/2011
10 | 0 | 01/01/0001 | 1 | 01/01/0001
and this is the value in the rest of the array.
I think you forgot to increment some value in part 1?!?
I have a spreadsheet with multiple duplicate rows. I need this data sorted in order of their frequency, so I setup a COUNTIF and sorted them. Now I would like to remove the duplicate rows, but keep the COUNTIF at the same value and have Excel add to it if other duplicates are added to the spreadsheet in the future.
How can I go about doing this?
EDIT:
This may make it a little more complex, but below is an example of the data as it is now (the last number denotes the number of occurances; with my COUNTIF):
123 | Yes | 2,901 | 4,243 | 5555 | John | 4
123 | Yes | 2,919 | 4,528 | 5555 | John | 4
123 | Yes | 2,901 | 4,243 | 5555 | John | 4
123 | Yes | 4,243 | 4,128 | 5555 | John | 4
111 | 80 | 2,919 | 4,452 | 2222 | Joe | 2
111 | 80 | 11,00 | 2,901 | 2222 | Joe | 2
And this is how I need it to be:
123 | Yes | 2,901 | 4,243 | 5555 | John | 4
111 | 80 | 2,919 | 4,452 | 2222 | Joe | 2
Notice that despite some of the values not being an exact match the first cells (column A) are the same.
Hope that makes it a little more clear.
In case anyone is interested, below is the solution to my problem.
Sub Count()
Dim sID As String
Dim sOldID As String
Dim lLastRow As Long
Dim lrow As Long
Dim lcount As Long
lLastRow = ActiveSheet.Cells(Rows.Count, 1).End(xlUp).Row
lrow = 2
sID = ActiveSheet.Cells(lrow, 1).Value
sOldID = ActiveSheet.Cells(1, 1).Value
lcount = 1
Do While Len(sID) <> 0
If sID <> sOldID Then
ActiveSheet.Cells(lrow - 1, 19).Value = lcount
sOldID = sID
lcount = 1
lrow = lrow + 1
Else
lcount = lcount + 1
ActiveSheet.Rows(lrow).Select
Selection.Delete Shift:=xlUp
End If
sID = ActiveSheet.Cells(lrow, 1).Value
Loop
ActiveSheet.Cells(lrow - 1, 19).Value = lcount
End Sub