Use Offset with Excel SQL query - sql

I'm working on an excel file to collect information from others closed Excel files
The provider is Microsoft.ACE.OLEDB.12.0 and everything works fine (almost).
In order to have updateable query, I used the command HDR = no in order to have column name like F1, F2, F3... and I retrieve the name after (see the code below, code from Stack Overflow).
However, with the command Union All, I also retrieved the headers as data, if I collect data from 5 files, I'll get 5 headers.
So I'm looking for a solution to retrieve header with command HDR = NO on Excel SQL query (start at line 2 in each file).
I tried OFFSET command in SQL query but I get an error message.
I also tried to get the row number in the original file but I didn't find the command.
Do you have any idea to help me on this issue?
Many thanks in advance,
BR
Code for information:
Option Explicit
Sub SqlUnionTest()
Dim strConnection As String
Dim strQuery As String
Dim objConnection As Object
Dim objRecordSet As Object
strConnection = _
"Provider=Microsoft.ACE.OLEDB.12.0;" & _
"User ID=Admin;" & _
"Data Source='" & ThisWorkbook.FullName & "';" & _
"Mode=Read;" & _
"Extended Properties=""Excel 12.0 Macro;"";"
strQuery = _
"SELECT * FROM [Sheet1$] " & _
"IN '" & ThisWorkbook.Path & "\Source1.xlsx' " & _
"[Excel 12.0;Provider=Microsoft.ACE.OLEDB.12.0;Mode=Read;Extended Properties='HDR=NO;'] " & _
"UNION " & _
"SELECT * FROM [Sheet1$] " & _
"IN '" & ThisWorkbook.Path & "\Source2.xlsx' " & _
"[Excel 12.0;Provider=Microsoft.ACE.OLEDB.12.0;Mode=Read;Extended Properties='HDR=NO;'] " & _
"UNION " & _
"SELECT * FROM [Sheet1$] " & _
"IN '" & ThisWorkbook.Path & "\Source3.xlsx' " & _
"[Excel 12.0;Provider=Microsoft.ACE.OLEDB.12.0;Mode=Read;Extended Properties='HDR=NO;'] " & _
"ORDER BY ContactName;"
Set objConnection = CreateObject("ADODB.Connection")
objConnection.Open strConnection
Set objRecordSet = objConnection.Execute(strQuery)
RecordSetToWorksheet Sheets(1), objRecordSet
objConnection.Close
End Sub
Sub RecordSetToWorksheet(objSheet As Worksheet, objRecordSet As Object)
Dim i As Long
With objSheet
.Cells.Delete
For i = 1 To objRecordSet.Fields.Count
.Cells(1, i).Value = objRecordSet.Fields(i - 1).Name
Next
.Cells(2, 1).CopyFromRecordset objRecordSet
.Cells.Columns.AutoFit
End With
End Sub

You can specify the starting and ending row while querying the excel file. So Instead of -
SELECT * FROM [Sheet1$]
Use This -
SELECT * FROM [Sheet1$A2:end]
A2 - it will start reading from 2nd row.
end - will read until the sheet has data. So suppose, if you want to only some rows from SHEET1. Use this -
SELECT * FROM [Sheet1$A2:A10]

Related

Issue Referencing Column in VBA SQL Query

I have an excel spreadsheet that I'm trying to perform SQL queries on. I get "no value given for one or more required parameters", so I think it's a problem with my query. I can do a query like "SELECT * FROM [Employee$A2:A4]", but when I reference a particular column using the name (i.e. name, title...etc, or even using the generic column reference like F1) I get "No value given for one or more required parameters."
Here's my code:
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
strFile = ThisWorkbook.FullName
strCon = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" & strFile _
& ";Extended Properties=""Excel 12.0;HDR=Yes;IMEX=1"";"
Set cn = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
cn.Open strCon
strSQL = "SELECT Employee FROM [Employee$] AS e WHERE e.Skill_Title = " & """" & skillTitle & """" & " AND e.Branch = " & """" & branchTitle & """" & " AND e.Skill_Prof = 5"
rs.Open strSQL, cn
MsgBox (rs.GetString)
Any ideas what might be going on?
Try applying the following example.
Tell me if the problem persists and the inputs you're using.
I have this on Employee sheet:
Created "MyQuery" subprocess as follows (as you can see, this is a replica of your code, with some little differences):
Sub MyQuery(ByVal skillTitle As String, _
ByVal branchTitle As String, _
ByVal skillProf As Integer)
Dim Cn As ADODB.Connection
Dim Rs As ADODB.Recordset
strFile = ThisWorkbook.FullName
strCon = _
"Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & strFile & ";" & _
"Extended Properties=""Excel 12.0;" & _
"HDR=Yes;" & _
"IMEX=1"";"
Set Cn = CreateObject("ADODB.Connection")
Set Rs = CreateObject("ADODB.Recordset")
Cn.Open strCon
strSQL = _
"SELECT Employee " & _
"FROM [Employee$] AS e " & _
"WHERE e.Skill_Title = '" & skillTitle & "' AND " & _
"e.Branch = '" & branchTitle & "' AND " & _
"e.Skill_Prof = " & CStr(skillProf)
Rs.Open strSQL, Cn
MsgBox (Rs.GetString)
'Do not forget closing your connection'
Rs.Close
Cn.Close
End Sub
Made a quick test:
Sub test()
'Try running this'
Call MyQuery("FOUR", "Y", 5)
End Sub
Result:
Have you named the columns? I wasn't sure from your code example whether you had named the columns or were assuming the column header would suffice for a reference. A "named" column is not the same as using a column header. To access the column by name try assigning a name to the column first.
From: How to give a name to the columns in Excel
Click the letter of the column you want to change and then click the "Formulas" tab.
Click "Define Name" in the Defined Names group in the Ribbon to open the New Name window.
Enter the new name of the column in the Name text box.

MS EXCEL to MS ACCESS .accdb Database from VBA SQL Syntax error

I am completely stuck and pulling out my hair on this one..
From Excel VBA I have two sets of code:
1- To Create a table is MS Access via a SQL statement
2- Populated newly created table with a For loop, also using SQL
The first set of code works perfectly, so I know that my connection string is working properly.
Here is the first set:
Sub Create_Table()
'Add Reference to Microsoft ActiveX Data Objects 2.x Library
Dim strConnectString As String
Dim objConnection As ADODB.Connection
Dim strDbPath As String
Dim strTblName As String
Dim wCL As Worksheet
Dim wCD As Worksheet
Set wCL = Worksheets("Contract List")
Set wCD = Worksheets("Contract Data")
'Set database name and DB connection string--------
strDbPath = ThisWorkbook.Path & "\SpreadPrices.accdb"
'==================================================
strTblName = wCL.Range("TableName").Value
strConnectString = "Provider = Microsoft.ACE.OLEDB.12.0; data source=" & strDbPath & ";"
'Connect Database; insert a new table
Set objConnection = New ADODB.Connection
On Error Resume Next
With objConnection
.Open strConnectString
.Execute "CREATE TABLE " & strTblName & " (" & _
"[cDate] text(150), " & _
"[Open] text(150), " & _
"[High] text(150), " & _
"[Low] text(150), " & _
"[Last] text(150), " & _
"[cChange] text(150), " & _
"[Settle] text(150), " & _
"[cVolume] text(150), " & _
"[OpenInterest] text(150))"
End With
Set objConnection = Nothing
End Sub
Mentioned before that code works perfectly. The bug is on the following set of code used to populate the table.
Here it is:
Sub InsertSQL()
'Add Reference to Microsoft ActiveX Data Objects 2.x Library
Dim strConnectString As String
Dim objConnection As ADODB.Connection
Dim strDbPath As String
Dim strTblName As String
Dim lngRow As Long
Dim strSQL As String
Dim wCL As Worksheet
Dim wCD As Worksheet
Set wCL = Worksheets("Contract List")
Set wCD = Worksheets("Contract Data")
'Set database name and DB connection string--------
strDbPath = ThisWorkbook.Path & "\SpreadPrices.accdb"
'==================================================
strTblName = wCL.Range("TableName").Value
strConnectString = "Provider = Microsoft.ACE.OLEDB.12.0; data source=" & strDbPath & ";"
'Connect Database; insert a new table
Set objConnection = New ADODB.Connection
'On Error Resume Next
With objConnection
.Open strConnectString
For lngRow = 2 To Range("NumberRows").Value
strSQL = "INSERT INTO " & strTblName & " (" & _
"cDate, Open, High, Low, Last, cChange, Settle, cVolume, OpenInterest)" & _
" VALUES ('" & _
wCD.Cells(lngRow, 1) & "' , '" & _
wCD.Cells(lngRow, 2) & "' , '" & _
wCD.Cells(lngRow, 3) & "' , '" & _
wCD.Cells(lngRow, 4) & "' , '" & _
wCD.Cells(lngRow, 5) & "' , '" & _
wCD.Cells(lngRow, 6) & "' , '" & _
wCD.Cells(lngRow, 7) & "' , '" & _
wCD.Cells(lngRow, 8) & "' , '" & _
wCD.Cells(lngRow, 9) & "')"
wCL.Range("A1").Value = strSQL
.Execute strSQL
Next lngRow
End With
Set objConnection = Nothing
End Sub
The error I receive is:
Run-time error, Syntax error in INSERT INTO statement.
Ok, so at first thought I think there must be a error in my SQL string. So I take the exact SQL string and toss it into Access Query Builder and run the SQL command and it imports into the table just fine.
What am I missing?
The problem may be due to field names. There is a function named CDate. Open and Last are both Jet reserved words. See Problem names and reserved words in Access.
Enclose those problem field names in square brackets to avoid confusing the database engine:
"[cDate], [Open], High, Low, [Last], cChange, Settle, cVolume, OpenInterest)"
The brackets may be enough to get your INSERT working. However consider renaming the fields if possible.
That linked page also mentions Allen Browne's Database Issue Checker Utility. You can download that utility and use it to examine your database for other problem names. It can also alert you to other issues which may not affect the current INSERT problem, but could cause trouble in other situations.

How to populate Excel ComboBox with data from SQL Server?

I am trying to populate a combobox in Excel file with data from SQL Server.
Here is code for event:
Private Sub Workbook_Open()
ActiveWorkbook.Sheets("Generation").Activate
Set cn = New ADODB.Connection
On Error Resume Next
With cn
.ConnectionString = "Provider=SQLOLEDB.1;" & _
"Integrated Security=SSPI;" & _
"Server=" & "192.160.160.150;" & _
"Database=" & "em_Consumer;" & _
"User Id= " & "User" & _
"Password = " & "server123"
.Open
End With
Set rs = New ADODB.Recordset
sqltextexec = " SELECT name FROM sys.tables WHERE schema_id = 7 AND name LIKE 'FinalCalculated%' ORDER BY create_date "
rs.Open sqltextexec, cn
rs.MoveFirst
With Sheets("Generation").ComboBox1
.Clear
Do
.AddItem rs![Name]
rs.MoveNext
Loop Until rs.EOF
End With
End Sub
This code works on my computer and on my colleague's as well (we are from DB team) but analysts who don't work with DB don't get list populated in the file.
Is it possible the program uses Windows authentication to connect to the DB?
Connection String Error
It seems there is an error in your connection string. The user ID needs to have a semi-colon after it.
Change this
With cn
.ConnectionString = "Provider=SQLOLEDB.1;" & _
"Integrated Security=SSPI;" & _
"Server=" & "192.160.160.150;" & _
"Database=" & "em_Consumer;" & _
"User Id= " & "User" & _
"Password = " & "server123"
To this
With cn
.ConnectionString = "Provider=SQLOLEDB.1;" & _
"Integrated Security=SSPI;" & _
"Server=" & "192.160.160.150;" & _
"Database=" & "em_Consumer;" & _
"User Id= " & "User;" & _
"Password = " & "server123"
That was an elusive little bugger.
Edit
I'm having trouble pinpointing the issue here, so perhaps a working example will better assist you at this point...
Function getSqlData(queryString As String, myUsername As String, myPassword As String, database As String) As Recordset
Dim conn As New ADODB.Connection
Dim rst As Recordset
Dim serverName As String
serverName = "192.160.160.150"
With conn
.ConnectionString = "Provider=SQLOLEDB.1;" & _
"Data Source=" & serverName & ";" & _
"Initial Catalog=" & database & ";User Id=" & myUsername & ";" & _
"Password=" & myPassword & ";Trusted_Connection=no"
.Open
End With
Set rst = conn.Execute(queryString)
Set getSqlData= rst
End Function
This will return your recordset.
today I tried to write it from scratch using #lopsided help. Here is the code:
Private Sub Workbook_Open()
ActiveWorkbook.Sheets("generation").Activate
Dim rstt As Recordset
MsgBox "1"
Set rstt = getData()
End Sub
-------------------------------------------------
Private Function getData()
Dim conn As New Connection
Dim rst As Recordset
Dim sqlstring As String
Dim rwcnt As Integer
MsgBox "2"
sqlstring = "SELECT productname FROM dbo.products WHERE recalc = 1"
With conn
.ConnectionString = "Provider=SQLOLEDB.1;" & _
"Data Source=192.160.160.150;" & _
"Initial Catalog=em_Consumer;" & _
"User Id=User;" & _
"Password=server!;" & _
"Trusted_Connection=no"
.Open
End With
MsgBox "3"
Set rst = conn.Execute(sqlstring)
rwcnt = rst.RecordCount
MsgBox rwcnt
MsgBox "5"
Set getData = rst
MsgBox "6"
End Function
So when i open the file I get messages:
1 which means that program started;
2 which means that it entered the function;
3 which means that there is no issues with connection;
!! then I get -1 value as record count which means that something is wrong
I tried to run this query in management studio and it returns 50 rows
Then program goes further and I get 5 and 6 ...
Do you have any ideas what is wrong with the code?
---------------------------------------------
Maybe it can help, code which works fine but returns table not recordset in the same document:
Sub Button3_Click()
ActiveSheet.Cells.Clear
Dim qt As QueryTable
sqlstring1 = "SELECT * FROM dbo.Report"
With ActiveSheet.QueryTables.Add(Connection:=getConnectionStr2, Destination:=Range("A3"), Sql:=sqlstring1)
.Refresh
End With
End Sub
----------------------------------
Private Function getConnectionStr2()
'DRIVER={SQL Server};
getConnectionStr2 = "ODBC;DRIVER={SQL Server};" & _
"DATABASE=em_Consumer;" & _
"SERVER=192.160.160.150;" & _
"UID=user;" & _
"PWD=server!;"
End Function

Errors when trying to connect to a database using VBA in excel

I am trying to connect to a database using VBA in excel but don't know what is wrong. Any suggestions?
Here is the code. I want to get the query results and put then into an excel sheet at the same time
Private Sub CommandButton1_Click()
Dim dd As String
Dim nn As String
OMCS_Connection.Show
If OMCS_Connection.CommandButton1 = False Then
dd = OMCS_Connection.TextBoxa.Value
nn = OMCS_Connection.TextBoxb.Value
End If
Dim order_number As String
order_number = TextBox1.Text
Dim sql_query As String
Set ad = CreateObject("ADODB.Connection") 'create and open ODBC connection
ad.ConnectionString = "ODBC;DRIVER=IBM DB2 ODBC DRIVER - DB2COPY1; DSN=ODRPTDB;UID = " & dd & " ; PWD = " & nn & ";"
'UID=me"
ad.Open
sql_query = "SELECT" & _
"CMOT.MFGNO AS MANUFACTURING_NUMBER," & _
"'BOX' AS ORDER_TYPE," & _
"CMOT.IDCUS AS CUSTOMER_ID," & _
"CMOT.WTCTN AS WT_CUSTOMER_ID," & _
"CMOT.PLORN AS ORDER_NUMBER," & _
"CMOT.IDMAC AS MACHINE_TYPE," & _
"CMOT.IDSMN AS MODEL_NUMBER," & _
"' ' AS NEW_MACHINE_TYPE," & _
"' ' AS NEW_MODEL_NUMBER," & _
"CMOT.BOXSH AS PSSD," & _
"CMOT.ALDPF AS RSSD," & _
"COGUS.ITPDC AS REGION" & _
"FROM DB2COATG.COS1S0T0 CMOT" & _
"INNER JOIN COGUS.WTCTN_CONVERSION COGUS" & _
"ON(COGUS.WTCTN = CMOT.WTCTN AND COGUS.REGIO = CMOT.REGIO)" & _
"WHERE CMOT.PLORN IN (" & order_number & ");"
Set RS = ad.Execute(sql_query) 'execute query
Before ad.open try inserting the following line
ad.CommandTimeout=0
This should prevent the query from timing out if it runs long.
If that does not work, it would help to know what line of code is throwing the error. Do you know how to step thought the code?

Counter field in MS Access, how to generate?

How can I generate counter field like this 0001A, 0002A... becouse in standart it is 0,1,2,3,4.... how to change this?
Adding to #HansUp's excellent answer, you could hide the IDENTITY column and at the same time expose the formatted column using a SQL VIEW: you could then revoke privileges on the table so that users work with the VIEW and do not 'see' the table e.g. demo:
copy+paste into any VBA module, no references nor Access UI/object model required, creates a new mdb in the temp folder e.g. use Excel:
Sub YourView2()
On Error Resume Next
Kill Environ$("temp") & "\DropMe.mdb"
On Error GoTo 0
Dim cat
Set cat = CreateObject("ADOX.Catalog")
With cat
.Create _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Data Source=" & _
Environ$("temp") & "\DropMe.mdb"
With .ActiveConnection
Dim Sql As String
Sql = _
"CREATE TABLE YourTable ( " & _
"ID INTEGER IDENTITY(1, 1) NOT NULL UNIQUE, " & _
"data_col VARCHAR(20) NOT NULL);"
.Execute Sql
Sql = _
"CREATE VIEW YourView AS " & _
"SELECT FORMAT$(ID, '0000') & 'A' AS formatted_ID, " & vbCr & _
" data_col " & vbCr & _
" FROM YourTable;"
.Execute Sql
Sql = _
"INSERT INTO YourView (data_col) VALUES ('one');"
.Execute Sql
Sql = _
"INSERT INTO YourView (data_col) VALUES ('day');"
.Execute Sql
Sql = _
"INSERT INTO YourView (data_col) VALUES ('when');"
.Execute Sql
Sql = "SELECT * FROM YourView;"
Dim rs
Set rs = .Execute(Sql)
MsgBox rs.GetString
End With
Set .ActiveConnection = Nothing
End With
End Sub
I think this one would be even better if you include DDL GRANT/REVOKE samples to manage the privileges
Here's the updated code to do just that:
Sub YourView2()
On Error Resume Next
Kill Environ$("temp") & "\DropMe.mdb"
Kill Environ$("temp") & "\DropMeToo.mdw"
On Error GoTo 0
' Create workgroup and db
Dim cat As ADOX.Catalog
Set cat = CreateObject("ADOX.Catalog")
With cat
.Create _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Jet OLEDB:Engine Type=4;" & _
"Data Source=" & _
Environ$("temp") & "\DropMeToo.mdw;" & _
"Jet OLEDB:Create System Database=-1"
.Create _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Jet OLEDB:Engine Type=4;" & _
"Data Source=" & _
Environ$("temp") & "\DropMe.mdb;" & _
"Jet OLEDB:System Database=" & _
Environ$("temp") & "\DropMeToo.mdw;"
' Add table with data and user with privileges
With .ActiveConnection
Dim Sql As String
Sql = _
"CREATE TABLE YourTable ( " & _
"ID INTEGER IDENTITY(1, 1) NOT NULL UNIQUE, " & _
"data_col VARCHAR(20) NOT NULL);"
.Execute Sql
Sql = _
"CREATE VIEW YourView AS " & _
"SELECT FORMAT$(ID, '0000') & 'A' AS formatted_ID, " & vbCr & _
" data_col " & vbCr & _
" FROM YourTable WITH OWNERACCESS OPTION;"
.Execute Sql
.Execute "CREATE USER onedaywhen pwd Chri5tma5;"
.Execute "GRANT ALL PRIVILEGES ON YourView TO onedaywhen;"
.Execute "REVOKE ALL PRIVILEGES ON YourTable FROM onedaywhen;"
End With
End With
' Test user can connect
Dim con As ADODB.Connection
Set con = New ADODB.Connection
With con
.ConnectionString = _
"Provider=Microsoft.Jet.OLEDB.4.0;" & _
"Jet OLEDB:Engine Type=4;" & _
"Data Source=" & _
Environ$("temp") & "\DropMe.mdb;" & _
"Jet OLEDB:System Database=" & _
Environ$("temp") & "\DropMeToo.mdw;" & _
"User ID=onedaywhen;Password=pwd;"
.Open
On Error Resume Next
' Attempt to insert to table (no privileges)
Sql = _
"INSERT INTO YourTable (data_col) VALUES ('one');"
.Execute Sql
If Err.Number <> 0 Then
MsgBox _
Err.Number & ": " & _
Err.Description & _
" (" & Err.Source & ")"
End If
On Error GoTo 0
Dim rs
On Error Resume Next
' Attempt to read table (no privileges)
Sql = _
"SELECT * FROM YourTable;"
Set rs = .Execute(Sql)
If Err.Number <> 0 Then
MsgBox _
Err.Number & ": " & _
Err.Description & _
" (" & Err.Source & ")"
End If
On Error GoTo 0
' From here, work only with VIEW
Sql = _
"INSERT INTO YourView (data_col) VALUES ('one');"
.Execute Sql
Sql = _
"INSERT INTO YourView (data_col) VALUES ('day');"
.Execute Sql
Sql = _
"INSERT INTO YourView (data_col) VALUES ('when');"
.Execute Sql
Sql = "SELECT * FROM YourView;"
Set rs = .Execute(Sql)
MsgBox rs.GetString
Set con = Nothing
End With
End Sub
The simplest solution would be to use a standard autonumber field (long integer). Let Access maintain those values for you. Then anytime you need those values in your "0001A" format, use the Format() function to add the leading zeros, and concatenate an "A".
This is trivially easy. If your autonumber field is named ID, you could do that transformation with this query:
SELECT Format(ID, "0000") & "A" AS formatted_ID
FROM YourTable;
Similarly you can apply the same expression to the control source property of a text box on a form or report.
One solution, that works for forms only!
create a GetId() function that calculates your counter (using DMax generally)
Use the Before insert event in your form to set the value of the field using GetId()
Drawback: in a multiuser environment, if another User2 starts addind a record after User1, but saves it before User1 saves his, there will be a duplicate problem. You will need to use the FormError event to regenerate the ID and resume the save process.