Stored procedure which has four input parameters - vba

I have a stored procedure which has 4 input parameters.
#Scenario, #StockID, #EOD, #EOD_PREV.
Based on the #Scenario (total 9 Scenarios), different SQL scripts are executed.
My requirement is:
I need to use VB Macros to display results in each worksheet, I need to use one ActiveX-Command button to run all the Scenarios.
Each Scenario in a separate Worksheet.
So far, I have managed to create individual connections for each sheet and Active X Command Button for each sheet.
Below is the query that I used.
Private Sub CommandButton1_Click()
Dim Scenario As String
Dim STOCK_ID As String
Dim EOD As String
Dim EOD_PREV As String
Scenario = Sheets("Stocks").Range("B3").Value
STOCK_ID = Sheets("Stocks").Range("B8").Value
EOD = Sheets("Stocks").Range("B6").Value
EOD_PREV = Sheets("Stocks").Range("B7").Value
With ActiveWorkbook.Connections("9").OLEDBConnection
.CommandText = "EXEC dbo.usp_Inventory_Reconcilliation '" & Scenario & "','" &
STOCK_ID & "', '" & EOD & "' ,'" & EOD_PREV & "'"
ActiveWorkbook.RefreshAll
End With
End Sub
Can somebody help me out with this situation.

Get the ActiveWorkbook.RefreshAll out of the With, and maybe it'll be easier to use connections without VBA and just use it for your refresh button.

Related

Define a function in Excel VBA where it runs SQL query (selecting the given from function columns and rows)

I am fairly new to SQL and VBA. I want to create a function and then make it an add-in, where I will be able to select a range of ids and a range of column names and retrieve the data from an excel file (ADODB.Connection).
I have found some instructions here (https://excel-macro.tutorialhorizon.com/vba-excel-read-excel-workbook-as-database-using-odbc-source/?fbclid=IwAR3uaiUOCwDVNhM_gMbsPdHWFPodxL0EsIcTAeRh6Qu1GzLRjB4eW8QR9is); I have replicated and adjusted the code for my case. But I can't find a way to:
make it a public function.
adjust the SQL query for selecting the selected through the function ids (rows) and column names.
the code so far:
Public Function getData(id As Range, cols As Range)
Set ActiveC = Application.ActiveCell
Set Connection = CreateObject("ADODB.Connection")
Connection.Open "DSN=SumitODBC"
strQuery = "SELECT " & cols & " FROM [Sheet1$A1:Z500] where id = '" & id & "' "
Set resultSet = Connection.Execute(strQuery)
Do While Not resultSet.EOF
ActiveC.Value = resultSet.Fields("cols").Value
resultSet.movenext
Loop
resultSet.Close
End Function
A simplistic dataset:
Data and what I want to achive

How do I prompt for a CSV user input on my on-start event for my form?

at work, we're trying to build a database to get an output of all kinds of data from different excel tables (items in stock, sales per week, price, etc) into one table by using the ItemID as primary key for all the tables and as a user input for the queries.
My goal is to let the user enter multiple itemIDs seperated by commas and get back a table with multiple rows (one for each item).
I have now created a form with all the fields I need and have used one of my queries that I implemented before as my data source.
I'm trying to write some code for my On-Start event and have used an example someone posted online regarding entering multiple parameters:
Option Compare Database
Const record source = Query01
Private Sub Form_Open(Cancel As Integer)
Dim m As String
m = InputBox("Enter itemID (numeric), separated by comma", "itemID")
If m <> "" Then
Me.RecordSource = record_source & " where itemID in (" & m & ");"
Else
Me.RecordSource = record_source
End If
End Sub
For the line:
Me.RecordSource = record_source & " where SKU in (" & m & ");"
I get the error
Run-Time error '2580'
The record source 'where SKU in (" & m & ");' specified on this form or report does not exist.
Do I need to declare my record source differently in the second line? Do I need to somehow specify which table I get my "itemID" field out of? The Query01 that I put as my data source gets fields out of three different tables.
Thank you so much for your help!
Cheers!
I fixed my issue by using the Filter-method instead and just deleting the declaration in line 2 since the data source was already defined in the properties sheet before.
My VBA code:
Private Sub Form_Open(Cancel As Integer)
Dim m As String
m = InputBox("Enter itemID (numeric), separated by comma", "itemID")
If m <> "" Then
Me.Filter = "itemID in (" & m & ")"
Me.FilterOn = True
Else
Me.FilterOn = False
End If
End Sub
Hope this helps anyone encountering a similar problem.
Cheers!

Changing headers display and order from a recordset to excel sheet

I have an excel sheet that I use as database, and a search form that allow user to search for information, based on some criterias, and display the filtered results in a new sheet.
I am using SQL to fetch data, and display them to the user.
I use something like top open connection, then to create a record set and pass my sql request to it
m_Connection.Open "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & ThisWorkbook.FullName & ";Extended Properties=""Excel 12.0;HDR=Yes;"";"
Set OpenRecordset = CreateObject("ADODB.Recordset")
OpenRecordset.Open sql, GetConnection(), 3, 3, &H1
Set rst = OpenRecordset("SELECT * FROM [Database$] Where " & Myconditions & ";")
Everything works fine, but I need to allow users to choose the column headers names and order that may be different from what i have in the sheet from which i make my select that i call database
We need this because users are in different countries, so the display name will be configured based on the country of the user, but also we want to allow user to change the display name and order of the fields based on their needs and because the display name may be too long (multi line).
My real "database" sheet is about 110 columns, and about 1000 records (rows).
I can't publish the real data because it's confidential, but to help you understand me, I created this example that represent what I have
let us suppose I have this "database"
the user enter this search screen to select the information he needs from the database
I wish that the user will get this result and not in the same order and display of the database
as you can see, the display names and orders of the columns in the result page is different from the "database" sheet
I would like to create a configuration page, in which the user can specify the display name he wants and the order in which he wants the fields to appears. something like that
Is there any fast way to do that directly in my Recordset in SQL/EXCEL or I should after I fetch data, change the headers in excel sheet using vba ? if so, I have to make a kind of array in vba that contains both database Names and display names and replace the names of the database by its corresponding just before I show the result page shows ?
any suggestions ?
same question about the order of the fields, how to sort them based on the order the user choosed ? any fast way ?
Thanks for anyone who can help with the best way to do that
You could do the following, it loops through the data range based on the number of rows in the range being the min and max of positions available, it looks for these rankings in turn, in column C, then checks if shown, then add's the field name and it's alias to an array. This array is then joined. So using data similar to yours, in columns of the the same ordering, I called:
GenerateOrderedSQL("table 1",range("a2:d6"),3,4) A1:D1 contained headers
This called my function
Function GenerateOrderedSQL(strInputTable As String, _
rngRangeForSelection As Excel.Range, _
lngOrderColumn As Long, _
lngShowColumn As Long) As String
Dim l As Long
Dim fPos As Long
Dim lfPos As Long
Dim a() As Variant
l = rngRangeForSelection.Rows.Count
ReDim a(l)
For fPos = 1 To l
lfPos = Application.WorksheetFunction.Match(fPos, _
rngRangeForSelection.Columns(lngOrderColumn), 0)
If rngRangeForSelection.Cells(lfPos, lngShowColumn).Value = "Yes" Then
a(fPos-1) = "[" & rngRangeForSelection.Cells(lfPos, 1) & _
"] AS [" & rngRangeForSelection.Cells(lfPos, 2) & "]"
a(fPos-1) = a(fPos-1) & IIf(fPos < l, ",", vbNullString)
End If
Next
Debug.Print "SELECT " & Join(a, vbNullString) & " FROM [" & strInputTable; "]"
End Function`
This gave the following
SELECT [Fname] AS [First Name],[Lname] AS [Last Name],[Zip] AS [Zip],[City] AS [City] FROM [table 1]

Excel OLE DB connection refresh issues

I have an Excel workbook with two dynamic OLE DB queries. I'm having issues with refreshes.
To set things up I have a SQL table-valued function as the source. The data is of the nature of...
SGrp SG_Desc SKU SKU_Desc Server Billed
1 Item 1 111 whatever 15 12
1 Item 2 222 some more 10 9
2 Item 3 333 zzz 10 8
3 Item 4 555 abc 20 18
On the first sheet ("Overall") I have a data connection that summarizes the groups with the command text of dynamically modified with one button.
SELECT SGrp, SG_Desc, SUM(Served) AS Served, SUM(Billed) AS Billed FROM mySQLdb ('8/19/2018','8/25/2018') WHERE SGrp <> '' GROUP BY SGrp, SG_Desc ORDER BY SG_Desc
I then have a cell with a data validation list that selects the group and a button to execute the VBA to dynamically modify the other connection. There's also two cells with the report start & end date for filtering. When I push the "Detail" button it runs the code below.
Private Sub RunDetail_Click()
Dim StartDate As Date
Dim EndDate As Date
Dim SGrp As String
Range("A1").Value = Range("G8").Value2
StartDate = Sheets("Overall").Range("H1").Value
EndDate = Sheets("Overall").Range("H2").Value
SGrp = Sheets("Overall").Range("A1").Value
SGrp = LTrim(RTrim(SGrp))
With ActiveWorkbook.Connections("CJP_DeliveryRecap_Detail").OLEDBConnection
.CommandText = "SELECT SKU, SKU_Desc, Served, Billed FROM mySQLdb ('" & StartDate & "','" & EndDate & "') WHERE SG_Desc='" & SGrp & "'"
' .Refresh
' ActiveWorkbook.Connections("CJP_DeliveryRecap_Detail").Refresh
End With
'RefreshOLEDB
'ThisWorkbook.RefreshAll
ActiveWorkbook.Connections("CJP_DeliveryRecap_Detail").Refresh
'Application.CalculateUntilAsyncQueriesDone
Application.Wait (Now + TimeValue("0:00:03"))
Dim rc As Integer
Dim i As Integer
Worksheets("Detail").Activate
With Worksheets("Detail").Range("CJP_DeliveryRecap_Detail")
rc = .Rows.Count
End With
With Worksheets("Detail").Range("E1048576")
.Select
.End(xlUp).Select
End With
i = Selection.Row
Worksheets("Detail").Range("E5").Select
Worksheets("Detail").Range("E5:G" & i).ClearContents
Worksheets("Detail").Range("E5").Value = 1
Worksheets("Detail").Range("F5").Value = "=+CJP_DeliveryRecap_Detail[#Served]*E5"
Worksheets("Detail").Range("G5").Value = "=+CJP_DeliveryRecap_Detail[#Billed]*E5"
Sheets("Detail").Range("E5:G5").Copy Sheets("Detail").Range("E6:E" & rc + 4)
Sheets("Detail").Range("F1").Value = ("=SUM(F5:F" & rc + 4 & ")")
Sheets("Detail").Range("G1").Value = ("=SUM(G5:G" & rc + 4 & ")")
End Sub
So, what is it doing? The first "strange" thing is I occasionally get errors in the code that I have to continue on. A lot of times, but not all, it hits the Application.Wait line, then the Worksheets("Detail").Activate line, and sometimes the line where I set values or copy data.
There are some comments where I've been testing various refreshes etc. The issue is because while when the code completes it displays the data conn detail correctly but the calculations as to the size of the results is from the prior set. If I click the button a second time then it calculates them correctly. I would of course prefer not to have a arbitrary 3sec delay but simply run the rest of the code after the resultant records have been retrieved.
Where am I going wrong as I've been banging my head against the wall with this. Most of what I do is in Access but Excel was the proper tool in this case.
I just thought of, but not thrilled to, would it work to have a separate sub to handle the record size calcs and cell values clear\set and call that from the button click or would that still not be generated until after the original sub ends.
Thanks in advance,
You need to wait till query is complete before proceeding with calculations.
What you need is to add .BackgroundQuery = False to your connection before .refresh
With ActiveWorkbook.Connections("CJP_DeliveryRecap_Detail").OLEDBConnection
.BackgroundQuery = False
.CommandText = "SELECT SKU, SKU_Desc, Served, Billed FROM mySQLdb ......"
.Refresh
End With
This should help
If the stored procedure being called from Excel does not contain "SET NOCOUNT ON" it will not run properly from Excel. I had the same issue and SET NOCOUNT ON was not included in my stored proc; the second i added it, it worked!

Loop through employee numbers and see if certain date falls within a period

Who can help a beginner out. I've added two screenshots to make my story clearer.
My excel sheet is two tabs. One is 'calculation' and other is 'project'.
What i'd like to know is how to program the following in vba:
In the calculation tab there is a employee number in column E. I have to look if that number also is written in the projects tab. If so i need to know if the date of the calculation tab falls within the start and end date in the projects tab. If so then write the info if that row to the empty columns in the calculation tab.
Another problem arises when an employee works multiple jobs in the projects tab. I guess there needs to be another loop in here:
If the date from calculation tab doesn't fall in the period from start to end in the projects tab, is there another row with the same employee number and maybe it falls within that period.
I hope i made my story clear. I know what the steps should be, just not how to program it. I hope someone is able to help me with this.
Since your screenshots appear to be Excel for Windows consider an SQL solution using Windows' JET/ACE Engine (.dll files) as you simply need to join the two worksheets with a WHERE clause for date filter. In this approach you avoid any need for looping and use of arrays/collections.
To integrate below, add a new worksheet called RESULTS as SQL queries on workbooks are read-only operations and do not update existing data. A LEFT JOIN is used to keep all records in Calculations regardless of matches in Projects but matched data will populate in empty columns. Results should structurally replicate Calculations. Adjust column names in SELECT, ON, and WHERE clauses as required (as I cannot clearly read column names from screenshots). Finally, a very important item: be sure date columns are formatted as Date type.
Dim conn As Object, rst As Object
Dim strConnection As String, strSQL As String
Dim i As Integer
Set conn = CreateObject("ADODB.Connection")
Set rst = CreateObject("ADODB.Recordset")
' OPEN DB CONNECTION
strConnection = "DRIVER={Microsoft Excel Driver (*.xls, *.xlsx, *.xlsm, *.xlsb)};" _ '
& "DBQ=C:\Path\To\Workbook.xlsx;"
conn.Open strConnection
' OPEN QUERY RECRDSET
strSQL = "SELECT t1.*, t2.[Project] AS [Which Project], t2.[Customer] As [Which Customer]," _
& " t2.[Start], t2.[End planned], t2.[Hours per week]" _
& " FROM [Calculation$A$3:$D$1048576] t1" _
& " LEFT JOIN [Projects$A$3:$J$1048576] t2" _
& " ON t1.EmployeeNum = t2.EmployeeNum" _
& " WHERE t1.[Date] BETWEEN t2.Start AND t2.[End planned];"
rst.Open strSQL, conn
' COLUMNS
For i = 1 To rst.Fields.Count Worksheets("RESULTS").Cells(3, i) =
rst.Fields(i - 1).Name
Next i
' DATA ROWS
Worksheets("RESULTS").Range("A4").CopyFromRecordset rst
rst.Close
conn.Close
Set rst = Nothing
Set conn = Nothing
it would be great to let us know, what you have done so far. I think the easiest way for a beginner is to just use two loops. One for the calculation and one for the projects tab.
Then you can start to develop your functionality. Use a "row counter" for each worksheet and iterate trough the rows. Here is an example pseudo code:
Dim lRowCountCalc as Long
Dim lRowCountPrj as Long
lRowCountCalc = 1
lRowCountPrj = 1
do
do
If Table2.Range("A" & lRowCountPrj).Value = Table1.Range("E" & lRowCountPrj).Value Then
If ... dates are equal
'Do some stuff
End if
End If
lRowCountPrj = lRowCountPrj +1
Loop Until lRowCountPrj = 5000 Or Table2.Range("A" & lRowCountPrj).text = ""
lRowCountCalc = lRowCountCalc +1
Loop Until lRowCountCalc = 5000 Or Table1.Range("A" & lRowCountCalc).text = ""
Just check for each employee number in calculation if there is a the same number in the current row in projects. If so, do your checks and fill in the information you need. If there is more than one project, you will find it also because all rows will be checked.
But be careful. This is very expensive because this code iterates for each row in projects over all rows in calculation. But for the beginning I would do it like this.