Schedule planing procedure - sqlite

My family owns a medium sized transport company and when i came in the business 3 years ago we had no software to manage all the transports we had to do. With 20 drivers this was a problem, so i sat down, learned the basics of VBA and made an app trough excel to manage/dispatch the different trips by email to our different drivers. It "works" for now but we are planing a future expansion so i started learning Xojo (im on a mac, closest thing to VBA)
We receive a Excel file to tell us which trips we have to do one day ahead (we transport people). Basically, its a sheet with all the different customers. I import this sheet in a "week file" to use the data afterwards trough different macros. There is lot of irrelevant information in this sheet but the column we will be interested too are the Type, Number and Hour.
So basically, i have to take all my rows (100+), group them by type and number, then order them by hour.
Heres a quick example of what my sheet looks like when sorted (the different colours are different drivers):
I think my procedure to get this result is not really that good. I loop trough all the rows in a data sheet (which is hidden) with a If statement checking if its a new type or trip number, save the time and row reference (first row, last row) in an array, then loop trough the array to export the ranges on the display sheet. Keep in mind that i wrote this 3 weeks after learning that VBA existed. It "works" but id like to have a better process.
I will be using SQLite to store all the information in the application im starting to write. Id like to have suggestion as to how i could sort all my data faster using SQL. Im looking for a procedure, i can figure out a way to code it.
Heres a sample of the code i made.
For RowSearch = 2 To RowCount
If Sheets(DataSheetName).Cells(RowSearch, 2).Value <> Sheets(DataSheetName).Cells(RowSearch - 1, 2).Value _
Or Sheets(DataSheetName).Cells(RowSearch, 3).Value <> Sheets(DataSheetName).Cells(RowSearch - 1, 3).Value Then
Blocks(TripCount, 1) = Position
Blocks(TripCount, 2) = RowSearch - 1
Blocks(TripCount, 3) = Format(Sheets(DataSheetName).Cells(Position, 4).Value, "hh:mm")
TripCount = TripCount + 1
Position = RowSearch
End If
Next RowSearch
Blocks(TripCount, 1) = Position
Blocks(TripCount, 2) = RowSearch - 1
Blocks(TripCount, 3) = Format(Sheets(DataSheetName).Cells(Position, 4).Value, "hh:mm")
'Sorts the blocks by time, loops trought the trips row range to sort the trips by time and type and writes the blocks
RowSelect = 1
For BlockSearch = 1 To TripCount
TempHour = "99:99"
For RowOrder = 1 To TripCount
If Blocks(RowOrder, 3) <= TempHour Then
TempHour = Blocks(RowOrder, 3)
Trips(BlockSearch, 1) = Blocks(RowOrder, 1)
Trips(BlockSearch, 2) = Blocks(RowOrder, 2)
RowChange = RowOrder
End If
Next RowOrder
RowRange = Trips(BlockSearch, 2) - Trips(BlockSearch, 1) + 1
FieldValue = Sheets(DataSheetName).Range("A" & Trips(BlockSearch, 1) & ":" & "R" & Trips(BlockSearch, 2))
Sheets(SheetName).Range("A" & RowSelect & ":" & "R" & RowSelect + RowRange - 1) = FieldValue
Sheets(SheetName).Rows(RowSelect + RowRange).Insert Shift = xlDown
RowSelect = RowSelect + RowRange + 1
Blocks(RowChange, 3) = "99:99"
Next BlockSearch

In SQL, "grouping" is an operation that not only partitions the rows into groups, but also aggregates all a group's rows to create a single output row for each group.
In your example, the rows are simply sorted by type, number, and hour, which would require a query like this:
SELECT *
FROM MyTable
ORDER BY Type, Number, Hour

Related

How to execute a complex sql statement and get the results in an array?

I would like to execute a fairly complex SQL statement using SQLite.swift and get the result preferably in an array to use as a data source for a tableview. The statement looks like this:
SELECT defindex, AVG(price) FROM prices WHERE quality = 5 AND price_index != 0 GROUP BY defindex ORDER BY AVG(price) DESC
I was studying the SQLite.swift documentation to ind out how to do it properly, but I couldn't find a way. I could call prepare on the database and iterate through the Statement object, but that wouldn't be optimal performance wise.
Any help would be appreciated.
Most sequences in Swift can be unpacked into an array by simply wrapping the sequence itself in an array:
let stmt = db.prepare(
"SELECT defindex, AVG(price) FROM prices " +
"WHERE quality = 5 AND price_index != 0 " +
"GROUP BY defindex " +
"ORDER BY AVG(price) DESC"
)
let rows = Array(stmt)
Building a data source from this should be relatively straightforward at this point.
If you use the type-safe API, it would look like this:
let query = prices.select(defindex, average(price))
.filter(quality == 5 && price_index != 0)
.group(defindex)
.order(average(price).desc)
let rows = Array(query)

Multiple Where Parameters on EntityDataSource?

I have a gridview that uses an entity datasource to populate itself. Depending on what the user has access to see, I want the gridview to implement a where clause. At the lowest level of access, the user can only see themselves. In order to do this I implement the line of code:
EmployeeEntityDataSource.Where = "it.Person_ID = " + selectQuery.ToString()
This successfully reduces the data in the gridview to the one appropriate user. If the user has the next step in access, they should be able to see themselves plus all the employees that work for them. I have sucessfully created a list of employees Person_IDs and I'm trying to filter my gridview so that if the Person_ID column in the gridview matches one of the Person_IDs in my list it should show up.
I have tried the following bits of code:
1.
For Each employeeID In employeeList
If count2 <> count Then
whereString += "it.Person_ID = " + employeeID.ToString() + " OR "
count2 += 1
Else
whereString += "it.Person_ID = " + employeeID.ToString()
End If
Next
EmployeeEntityDataSource.Where = whereString
Essentially thought I could create a giant where statement with a bunch of ORs but this did not work
2.
EmployeeEntityDataSource.WhereParameters.Add(employeeList)
EmployeeEntityDataSource.Where = "it.Person_ID = #employeeList"
The error I get here says a List(of T) cannot be converted WebControl.Parameter
How do I properly create a WHERE statement that will compare the it.Person_ID of the gridview to each element in my list called employeeList?
I think an In statement should accomplish what you need. Something like this -
string employeeIDs = string.Join(",", employeeList.ToList());
EmployeeEntityDataSource.Where = String.Format("it.Person_ID IN ({0})", employeeIDs);
Or you may have to iterate through your list to create the employeeIDs string, depending on the types we're dealing with here.
Apparently I lied When I said my first bit of code did not work. The issue was that I was not generating the massive OR statement correctly.
From what I remember the statement I was originally generating was it.Column = ID1 OR ID2 OR ID3 and so on.
If the statement created is it.Column = ID1 OR it.Column = ID2 OR it.Column = ID3 and so on, this creates a statement that works properly.
The code that is working in my current project is:
For Each employee In employeeList
If count2 <> count Then
whereString += "it.Person_ID = " + employee.ToString() + " OR "
count2 += 1
Else
whereString += "it.Person_ID = " + employee.ToString()
End If
Next
EmployeeEntityDataSource.Where = whereString

count the unique values in one column in EXCEL 2010 or R with 1 million rows

After searching the forum, I did not find a good solution for this question. If I missed it, please tell me.
I need to count the unique values in one column in EXCEL 2010.
The worksheet has 1 million rows and 10 columns. All cell values are string or numbers.
I used the solution at Count unique values in a column in Excel
=SUMPRODUCT((A2:A1000000<>"")/COUNTIF(A2:A100000,A2:A1000000&""))
But, it runs so long time that the EXCEL is almost frozen. And, it generates 25 processes in Win 7.
Are there more efficient ways to do it?
Also, in the column, all values have for format of
AX_Y
here, A is a character, X is an integer, Y is an integer from 1 to 10.
For example, A5389579_10
I need to cut off the part after (including) undersocre. for the example,
A5389579
This is what I need to count as unique values in all cells in one column.
For example, A5389579_10
A1543848_6
A5389579_8
Here, the unique value has 2 after removing the part after underscore.
How to do it in EXCEL VBA and R (if no efficient solution for EXCEL)?
If you want to do this by VBA, you can take advantage of the Collection object. Since collections can only contain unique values, trying to add all of your input data to a collection will result in an array of unique values. The code below takes all the variables in a selected range and then outputs an array with distinct values to an other sheet (in this case a sheet named Output).
Sub ReturnDistinct()
Dim Cell As Range
Dim i As Integer
Dim DistCol As New Collection
Dim DistArr()
Dim OutSht As Worksheet
Dim LookupVal As String
Set OutSht = ActiveWorkbook.Sheets("Output") '<~~ Define sheet to putput array
If TypeName(Selection) <> "Range" Then Exit Sub
'Add all distinct values to collection
For Each Cell In Selection
If InStr(Cell.Value, "_") > 0 Then
LookupVal = Mid(Cell.Value, 1, InStr(Cell.Value, "_") - 1)
Else
LookupVal = Cell.Value
End If
On Error Resume Next
DistCol.Add LookupVal, CStr(LookupVal)
On Error GoTo 0
Next Cell
'Write collection to array
ReDim DistArr(1 To DistCol.Count, 1 To 1)
For i = 1 To DistCol.Count Step 1
DistArr(i, 1) = DistCol.Item(i)
Next i
'Outputs distinct values
OutSht.Range("A1:A" & UBound(DistArr)).Value = DistArr
End Sub
Note that since this code writes all the distinct values to a single column in the OutSht-sheet, this will return an error if there are more than 1,048,576 distinct values in your dataset. In that case you would have to split the data to be filled into multiple output columns.
For your specific request to count, use the below in a formula like =COUNTA(GetUniques(LEFT("A1:A100000",FIND("_","A1:A100000")-1)) entered as an array formula with Ctrl+Shift+Enter.
It also accepts multiple ranges / values (e.g. GetUniques("A1:A10","B2:E4"))
Function GetUniques(ParamArray args())
Dim arg, ele, arr, i As Long
Dim c As Collection
Set c = New Collection
For Each arg In args
If TypeOf arg Is Range Then
If arg.Count = 1 Then
arr = array(arg.value)
Else
arr = arg.Value
End If
ElseIf VarType(arg) > vbArray Then
arr = arg
Else
arr = Array(arg)
End If
For Each ele In arr
On Error Resume Next
c.Add ele, VarType(ele) & "|" & CStr(ele)
On Error GoTo 0
Next ele
Next arg
If c.Count > 0 Then
ReDim arr(0 To c.Count - 1)
For i = 0 To UBound(arr)
arr(i) = c(i + 1)
Next i
Set c = Nothing
GetUniques = arr
End If
End Function
edit: added a performance optimisation for ranges (loads them at once into an array - much faster than enumerating through a range)
In R:
# sample data
df <- data.frame(x=1:1000000,
y=sample(1e6:(1e7-1),1e6,replace=T))
df$y <- paste0("A",df$y,"_",sample(1:10,1e6,replace=T))
# this does the work...
length(unique(sub("_[0-9]+","",df$y)))
# [1] 946442
# and it's fast...
system.time(length(unique(sub("_[0-9]+","",df$y))))
# user system elapsed
# 2.01 0.00 2.02
In excel 2010... in the next column add (if original data was in A:A add in B1)
= 1/COUNTIF(A:A,A1) and copy down col B to the bottom of your data. Depending on your PC it may chug away calculating for a long time, but it will work. Then copy col B & paste values over itself.
Then SUM col B

Linq to SQL: Group By and Sum()

I'm very new to linq so this should be pretty easy to answer, but I've had a hard time finding the answer.
I have the following LINQ statement, which performs a simple linq query and assigns the resulting values labels on an asp.net web form:
Dim db As New MeetingManagerDataContext
Dim q = From s In db.vwRoomAvailabilities _
Where s.MeetingID = lblMeetingID.Text _
Select s.AllRequestedSingles, s.AllRequestedDoubles, s.AllBookedSingles, s.AllBookedDoubles, SinglesNeeded = s.AllRequestedSingles - s.AllBookedDoubles, DoublesNeeded = s.AllRequestedDoubles - s.AllBookedDoubles
lblSinglesRequested.Text = "Singles Requested: " & q.FirstOrDefault.AllRequestedSingles
lblSinglesBooked.Text = "Singles Booked: " & q.FirstOrDefault().AllBookedSingles
lblSinglesNeeded.Text = "Singles Needed: " & q.FirstOrDefault().SinglesNeeded
lblDoublesRequested.Text = "Doubles Requested: " & q.FirstOrDefault().AllRequestedDoubles
lblDoublesBooked.Text = "Doubles Booked: " & q.FirstOrDefault().AllBookedDoubles
lblDoublesNeeded.Text = "Doubles Needed: " & q.FirstOrDefault().DoublesNeeded
Originally, there was going to be only a single row result and you can see I'm using FirstOrDefault() to grab that single value which works great. But the design has changed, and multiple rows can now be returned by the query. I need to now Group By the MeetingID above, and SUM each of the selected columns (i.e. s.AllRequestedDoubles).
I've found lots of grouping and summing samples but none seem to fit this scenario very well.
Can you help me modify the above LINQ to Sum the resulting values instead of just showing the first row result values?
Try this
From s In db.vwRoomAvailabilities
Where s.MeetingID = lblMeetingID.Text
Group by s.MeetingID
into SumAllRequestedDoubles = sum(s.AllRequestedDoubles),
SumAggregate2 = sum(s.SomeField2),
SumAggregate3 = sum(s.SomeField3)
Select SumAllRequestedDoubles, SumAggregate2, SumAggregate3
That will get you started for performing a SUM on that single column.
You'll need to project each SUM'd column into a new aliased column (like i did above).
Also, as you're new to LINQ-SQL, check out LinqPad - it will rock your world.

dataset and non existing rows

Please help out newbie.
I am reading mothly sales statistics for last two years from stored procedure, display it on asp.net site and it works just fine.
Problem is with products that are not sold often I need to figure out which months do not have any sales. In that case I need to put zero in table cell and move to next row in dataset.
For...Each does not do the trick in case where there isn't data for every month.
Question is, how to move to next sqlrow and how to test when all rows heve been read?
sqlSelect = "EXECUTE dealer_sales_statistics #productID = '" & strProdID.Value & "'"
Dim sqlConn As New SqlConnection(sqlConnStr)
Dim sqlRow As DataRow
sqlConn.Open()
Dim sqlAdapt As New SqlDataAdapter(sqlSelect,sqlConn)
Dim sqlDataSet As New DataSet()
sqlAdapt.Fill(sqlDataSet, "sales_statistics")
Do Until sqlRow.EOF
If intCounter < 12 Then
' arrMonth contains last 24 months, e.g. "12_2009" to "1_2008"'
' stored procedure can return values for any month between that range'
' amount of returned values (DataSet sqlRows) can vary from 0 to 24'
If arrMonth(intCounter) = sqlRow("month") & "_" & sqlRow("year") Then
strLine_1 &= "<td>" & CInt(sqlRow("qty")) & "</td>"
arrSumma_1 = arrSumma_1 + CInt(sqlRow("qty"))
sqlRow.MoveNext
Else
strLine_1 &= "<td class='cell'>0</td>"
End If
Else
'using intCouter and same code to separate sales in 12 month periods'
If arrMonth(intCounter) = sqlRow("month") & "_" & sqlRow("year") Then
strLine_2 &= "<td>" & CInt(sqlRow("qty")) & "</td>"
arrSumma_2 = arrSumma_2 + CInt(sqlRow("qty"))
sqlRow.MoveNext
Else
strLine_2 &= "<td>0</td>"
End If
End If
intCounter = intCounter + 1
Loop
I think that you are focusing on the wrong area by trying to do this in your code. I can think of a likely solution there but it is really messy. Instead, focus on making sure that the sets returned by the stored proc are complete so you can iterate them without worry about missing months. That is, the stored procedure is probably returning sets made up only of months where there were sales (e.g. due to an inner join) - and you need to change this so it returns all months.
So, instead of posting the VB code, I'd suggest that you post the stored proc to get help in resolving the issue.
As a general guideline, I'd approach this by creating a dummy table with the months of the year listed (along with their month numbers to perform the join). Then, fold that table in with the query using a left outer join to ensure that all months are represented. Also, when selecting the final sales figures, make sure that there are no null values (for months where there were no sales) by using an "IsNull(Val, 0) as Val" to substitute a zero.
Again, this is just general guidance, we'd need to see the actual sproc to really help.
Here is how I did solve this with SQL. I create dynamically temp table that holds last 24 months and another temp table with sales data 0 to 24 months. Maybe this will help somebody with similar problem. (code below is in sql server as stored procedure). Thank you for help Mark!
DECLARE #strTemp_months TABLE
(
sorting INT,
months INT,
years INT
)
DECLARE #mnth INT
SET #mnth = 0
WHILE (#mnth < 24)
BEGIN
INSERT #strTemp_months
SELECT CASE WHEN YEAR(GETDATE()) = YEAR(DATEADD( m , -#mnth , GETDATE())) THEN 1 ELSE 2 END AS sorting,
MONTH(DATEADD( m , -#mnth , GETDATE())), YEAR(DATEADD( m , -#mnth , GETDATE()))
SET #mnth = #mnth + 1
END
DECLARE #productID VARCHAR(12)
SET #productID = '1234567890'
DECLARE #strTemp_statistics TABLE
(
sorting INT,
months INT,
years INT,
productno VARCHAR(35),
salesqty DECIMAL(9,2)
)
INSERT #strTemp_statistics
SELECT CASE WHEN YEAR(transaction_date) = YEAR(GETDATE()) THEN 1 ELSE 2 END AS sorting,
MONTH(transaction_date) AS months, YEAR(transaction_date) AS years, product_number AS productno,
SUM(qty) AS salesqty
FROM sales_events
WHERE product_number = #productID
-- including all transactions from last 24 full months until today
AND transaction_date >= CAST(YEAR(DATEADD( m , -23 , GETDATE())) AS CHAR(4)) + '-' + CAST(MONTH(DATEADD( m , -23 , GETDATE())) AS VARCHAR(2)) + '-01'
GROUP BY MONTH(transaction_date), YEAR(transaction_date), product_number
SELECT m.sorting, m.months, m.years, COALESCE(productno, 'No Sales') AS productno, COALESCE(kpl, 0) AS salesqty
FROM #strTemp_months m LEFT OUTER JOIN #strTemp_statistics s
ON m.months = s.months AND m.years = s.years
ORDER BY 1, 2 DESC

Resources