T-SQL query with variable declarations in Excel VBA Code fail - sql

I got a problem with running SQL query with "declare" and "set" functions in VBA.
Sheets("Arkusz1").Select
connstring = _
"ODBC;DRIVER=SQL Server;SERVER=my_database_server;UID=user;PWD=password;APP=Microsoft Office 2010;WSID=some_id;DATABASE=mydatabase"
With ActiveSheet.QueryTables.Add(Connection:=connstring, Destination:=Worksheets("Arkusz1").Range("A1"), Sql:=Array( _
"declare #dzisiaj date" & Chr(13), _
"set #dzisiaj = getdate()" & Chr(13), _
"select #dzisiaj as dzisiaj"))
.BackgroundQuery = False
.Refresh
End With
In SQL Server 2012 that code works fine, but... when I embed it into it gives me a run-time error '1004'. Also VBA code works on other queries works well.
My full SQL query has about 90 lines with 2 variable declarations (one declaration is a value from another 30 line SQL query), so it's mandatory to include variable declarations :)
How to solve that problem?

I figured it out. The key is to use ADODB connection to import data via SQL Query. Also necessary is to check Microsoft Active X Data Objects 2.0 library in Tools->References in Visual Basic Editor (Shortcut: Alt+F11 in Excel).
So, there is an example of my VBA code:
Sub sql_query_import()
' Declarations
Dim Cn As ADODB.Connection
Dim Server_Name As String
Dim Database_Name As String
Dim User_ID As String
Dim Password As String
Dim SQLStr As String
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
' Server connection settings
Server_Name = "192.168.1.106\my_database" ' IP of server name
Database_Name = "mydatabase" ' Database name
User_ID = "myusername" ' User name
Password = "mypassword" ' User password
' SQL Query
SQLStr = "SET NOCOUNT ON " & Chr(13) ' it's mandatory if you don't want to get error 3704
SQLStr = SQLStr & "declare #dzisiaj date " & Chr(13)
SQLStr = SQLStr & "set #dzisiaj = getdate() " & Chr(13)
SQLStr = SQLStr & "select #dzisiaj as 'today'
' Connect to database
Set Cn = New ADODB.Connection
Cn.Open "Driver={SQL Server};Server=" & Server_Name & ";Database=" & Database_Name & _
";Uid=" & User_ID & ";Pwd=" & Password & ";"
' Start connection
rs.Open SQLStr, Cn, adOpenStatic
' Load data
With rs
For i = 1 To .Fields.Count
Worksheets(1).Cells(1, i) = .Fields(i - 1).Name ' Include column name if not - delete it
Next i
End With
Worksheets(1).Cells(2, 1).CopyFromRecordset rs ' Start loading data to Cell A2
rs.Close
Set rs = Nothing
Cn.Close
Set Cn = Nothing
End Sub
Using in SQL Query "SET NOCOUNT ON" is necessary if you don't want to get error 3704.
Also, using
SQLStr = "SET NOCOUNT ON " & Chr(13) ' it's mandatory if you don't want to get error 3704
SQLStr = SQLStr & "declare #dzisiaj date " & Chr(13)
is more efficient way to include multi-line SQL Queries :)

I'm still new to vb and vba and learning myself, but I know you can declare and write to variables in VB.net which can then feed into an embedded SQL script. I would think you can do the same thing in vba. Here's what I suggest.
Declare a vb string like SQL_Var_1
Insert the 30-line SQL query as a separate query before the main query.
Write the result of the 30-line query to the vb string SQL_Var_1.
Remove the declarations from the Main SQL query but leaving the references to those variables.
Reference SQL_Var_1 as an input parameter in the embedded main query using the exact same name you used in the main query (i.e., #dzisiaj), like here.
If you follow these steps for both SQL variables, you should be able to achieve the same result as if you were using the declared SQL variables.

Related

VBA to query field contents in CSV

I'm struggling with ADO connections/recordsets.
My problem statement is: a function that will return the first value of a chosen field, in a chosen .csv file.
I am doing this to identify variably-named .csv files before adding the data to the relevant tables in a database. I am making the assumption that this field is always present and that either it is consistent throughout the file, or only relevant ones are grouped (this is controlled higher up the chain and is certain enough).
My code is being run as part of a module in an MS Access database:
Public Function GetFirstItem(File As Scripting.File, Field As String)
Dim Conn As ADODB.Connection, Recordset As ADODB.Recordset, SQL As String
Set Conn = New ADODB.Connection
Set Recordset = New ADODB.Recordset
'Microsoft.ACE.OLEDB.16.0 / Microsoft.Jet.OLEDB.4.0
Conn.ConnectionString = "Provider=Microsoft.ACE.OLEDB.16.0;Data Source=""" & File.ParentFolder & _
"""; Extended Properties=""text;HDR=Yes;FMT=Delimited;"";"
SQL = "SELECT " & Field & " FROM """ & File.Name & """ LIMIT 1"
Debug.Print Conn.ConnectionString
Debug.Print SQL
Conn.Open
Recordset.Source = SQL
Recordset.ActiveConnection = Conn.ConnectionString
Recordset.Open
Recordset.MoveFirst
'GetFirstItem = Recordset!Questionnaire
Recordset.Close
Conn.Close
Set Recordset = Nothing
Set Conn = Nothing
End Function
ConnectionString = Provider=Microsoft.ACE.OLEDB.16.0;Data Source="D:\Documents\Jobs\TestPath"; Extended Properties="text;HDR=Yes;FMT=Delimited;";
Field = Questionnaire
SQL = SELECT Questionnaire FROM "test.csv" LIMIT 1
I get an error on Recordset.Open of:
This may be (is probably) down to a complete lack of understanding of how ADO connections/recordsets work. I have tried sans-quotes and it complains about a malformed FROM expression. Additionally, once this hurdle is overcome I am unsure of the syntax of how to return the result of my query. If there is a better way of doing this I am all ears!
Thanks.
In Access you don't need ADO library to query a CSV file:
Public Function GetFirstItem(File As Scripting.File, Field As String) As String
Dim RS As DAO.Recordset, SQL As String
SQL = "SELECT TOP 1 [" & Field & "]" _
& " FROM [" & File.Name & "]" _
& " IN '" & File.ParentFolder & "'[Text;FMT=CSVDelimited;HDR=Yes];"
Debug.Print SQL
Set RS = CurrentDb.OpenRecordset(SQL)
GetFirstItem = RS(0)
RS.Close
Set RS = Nothing
End Function
Usage:
?GetFirstItem(CreateObject("Scripting.FileSystemObject").getfile("c:\path\to\your\file.csv"), "your field")

How to append data from a SQL Server table to an Access table?

I am trying to write a query in MS access to open a connection to a local SQL Server and then to import select tables into MS Access.
My code runs until the Cn.Execute statement. I get
Run-time error '-2471765 (80040e37)' [Microsoft][ODBC SQL Server Driver][SQL Server] Invalid Object Name 'dbo_SQLServertable'.
I need to import additional tables so I need a code that will work when I change table names.
Private Sub Command28_Click()
Dim Cn As ADODB.Connection
Dim Server_Name As String
im Database_Name As String
Dim User_ID As String
Dim Password As String
Dim SQLStr As String
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
Server_Name = "" ' Enter your server name here
Database_Name = "Test" ' Enter your database name here
User_ID = "" ' enter your user ID here
Password = "" ' Enter your password here
Set Cn = New ADODB.Connection
Cn.Open "Driver={SQL Server};Server=" & Server_Name & ";Database=" & Database_Name & ";"
Cn.Execute "INSERT INTO Access Table SELECT dbo_SQLServerTable.* FROM dbo_SQLServerTable;"
Set rs = Nothing
Cn.Close
Set Cn = Nothing
I made changes and I get a new error message
Run-time error '-2147216900 (80040e14)' [Microsoft][ODBC SQL Server Driver][SQL Server] Cannot Insert the value NULL into column 'DiagnosisOrdinal', table 'Office.dbo.Test' column does not allow nulls. Insert fails.
It appears that my insert statement is still referencing (or trying to reference) a table in the SQL server. 'Office' is the database name that I am pulling from.
Do I have to close the connection and then paste the data into my local Access table? Will I then have to re-open and close the connection if I want to do this for multiple tables?
I changed my execute statement from
Cn.Execute "INSERT INTO Access Table SELECT dbo_SQLServerTable.* FROM dbo_SQLServerTable;"
to
Cn.Execute "INSERT INTO Test(VisitID, Provider) SELECT VisitID, Provider FROM dbo.SQLServerTable;"
Least amount of code I can think of is to use a pass-through query.
Setup a PT query to the server database in question.
Then your code to create a new table in access would look like:
Sub TestImport()
Dim strSQL As String
With CurrentDb.QueryDefs("qryPassR")
.SQL = "select * from tblHotels"
End With
Dim strLocalTable As String
strLocalTable = "zoo"
CurrentDb.Execute "select * into " & strLocalTable & " FROM qryPassR"
End Sub
The above of course assumes you setup the connection to sql server (one database) when you created the PT query. The above approach is nice since you don't mess around with connection strings in code.
However, given that you need (want) to specify the database (and likely server), then above becomes this:
Sub TestImport2()
Dim strSQL As String
Dim strServer As String
Dim strDatabase As String
Dim strUser As String
Dim strPass As String
strServer = ""
strDatabse = ""
strUser = ""
strPass = ""
Dim strLocalTable As String
Dim strServerTable As String
With CurrentDb.QueryDefs("qryPassR")
.Connect = dbCon(strServer, strDatabase, strUser, strPass)
.SQL = "select * from " & strServerAble
End With
CurrentDb.Execute "select * into " & strLocalTable & " FROM qryPassR"
End Sub
The above uses a "handy" function to create your connection string.
That function is as follows:
Public Function dbCon(ServerName As String, _
DataBaseName As String, _
Optional UserID As String = "", _
Optional USERpw As String, _
Optional APP As String = "Office 2010", _
Optional WSID As String = "Import") As String
' returns a SQL server conneciton string
dbCon = "ODBC;DRIVER=" & SQLDRIVER & ";" & _
"SERVER=" & ServerName & ";" & _
"DATABASE=" & DataBaseName & ";"
If UserID <> "" Then
dbCon = dbCon & "UID=" & UserID & ";" & "PWD=" & USERpw & ";"
End If
dbCon = dbCon & _
"APP=" & APP & ";" & _
"WSID=" & WSID & ";" & _
"Network=DBMSSOCN"
End Function
Edit
Poster has asked for solution to append data into a EXISTING table.
In that case, simply change this:
CurrentDb.Execute "select * into " & strLocalTable & " FROM qryPassR"
to
CurrentDb.Execute "INSERT INTO " & strLocalTable & " SELECT * FROM qryPassR"
You don't want the table prefix in your SELECT from the SQL table. Just do SELECT * FROM dbo_SQLServerTable; Best practice, though, is not to use SELECT * but rather specify the columns in case the table schemas ever change.
There's a few suggestions for this particular issue
Pass thru queries
Linked Tables from SQL Server. Youll prolly have to set up a dsn file which isnt too terribly difficult.
Or handle it directly in SQL Server Insert into Access from SQL Server
ODBC connection via VBA (what youre doing and seemingly the most convoluted)
All of these approaches will work fine. I suggest linked tables so you dont duplicate data but thats a cinsderation fro you since I dont knwo the requirements of the project.
dbo_SQLServerTable is an Access table name, not SQL server table name. Because you already created the linked table dbo_SQLServerTable, you can use the following VBA code.
Currentproject.connection.execute "INSERT INTO MyAccessTable(fld1, fld2, fld3) SELECT fld1, fld2,fld3 FROM dbo_SQLServerTable"
There is no need to create connection object in VBA code. Currentproject.connection is always available to be referenced.

SQL Select Statement in Microsoft Access VBA - Need help inserting in Access table as block, not loop

I am trying to do a Select statement from an SQL server using an ADODB connection, within Microsoft Access VBA. The select statement is very complex with calculations and would work better to keep that to try and turn in to a connection with the DB.
I can append the data row by row, but am wondering how I could append as simple as possible as a block of data to save time/vba effort. (Lots of data!)
In order to simplify the example, I have put a basic SQL statement, but if you could review this and let me know if there is a way to append as a recordset block or what would be a better option... The code below works for pasting as a block into excel, and I would think I only need to change that commented out line's method to append in Access...
Option Private Module
Private Const DataServer = "GLSSQLMADPS2"
Private Const sqlconstring = "Driver={SQL Server};Server=" & DataServer & "; Database=PERF_MGMT_BWRSRV_PROD; Trusted_Connection=yes; Connect Timeout = 600"
Sub M2DSQLPull()
Dim cn As ADODB.Connection
Dim rs As ADODB.Recordset
Dim sql As String
Dim sql As String
sql = "SELECT " & _
"Employee_Token_NR, " & _
"Null as 'Sum_Month', " & _
"1000 AS 'Data_ID', " & _
"DATEADD(m, DATEDIFF(m, 0, MIN(CALL_TS)), 0) AS 'Metric_1' " & _
"FROM PMBSED_PhoneEvaluations " & _
"GROUP BY Employee_Token_NR "
Set cn = New ADODB.Connection
cn.CommandTimeout = 120
cn.Open sqlconstring
Set rs = New ADODB.Recordset
rs.Open SqlString(i), cn, adOpenStatic, adLockReadOnly
'How I would paste in an excel spreadsheet, how do I block paste in an access database?
'ThisWorkbook.Worksheets("RawData").Cells(RowNumber, 1).CopyFromRecordset rs
RowNumber = RowNumber + rs.RecordCount
rs.Close
cn.Close
End Sub
Thank you for any assistance!
-Robert

VBS Script to Execute SQL statement?

To open, I have never written a vbs script. I have written many SQL scripts, views, developed databases. I have written plenty of VBA in Access applications.
For this, I am just trying to set up a SQL script as a VBS script, so the users don't have to go into SSMS to run it. They can just double-click the VBS script, specify the server and database when prompted, and the quick script will run for them.
This is what I have gotten so far, but I keep getting Microsoft VBScript compilation errors. The latest one is line 3 char 17, which is on a Dim statement. Just wanted to see if anyone can tell if I am missing something fundamental to this script, that is preventing it from compiling or processing correctly.
This is the very short script:
Dim conn
Set conn = createobject("Adodb.Connection")
Dim sConnString As String
Dim SqlStatement As String
sSourceServer = InputBox ("Enter the name of the SQL Server","Enter SQL Server Name","")
If Len(sSourceServer) = 0 Then
MsgBox "No SQL Server was specified.", , "Unable to Continue"
Exit Sub
End if
sSourceDB = InputBox ("Enter the name of the Law SQL Database","Enter Law SQL DB Name","")
If Len(sSourceDB) = 0 Then
MsgBox "No SQL DB was specified.", , "Unable to Continue"
Exit Sub
End if
' Create the connection string.
sConnString = "Provider=SQLOLEDB;Data Source=" & sSourceServer & "; Initial Catalog=" & sSourceDB & "; Integrated Security=SSPI;"
MsgBox sConnString
' Open the connection and execute.
conn.Open sConnString
conn.CommandTimeout = 900
SqlStatement = "UPDATE [tablename] " & _
"SET UUID = CASE WHEN CHARINDEX('.',[Filename]) > 1 THEN LEFT(CAST([Filename] AS VARCHAR),CHARINDEX('.',[Filename])-1) ELSE [Filename] END " & _
"WHERE [Filename] IS NOT NULL"
conn.Execute(SqlStatement)
conn.Close
Set rs = Nothing
SqlStatement = vbNullString
MsgBox "All Done! Go Check your results!"
If anyone can help, I'd greatly appreciate it.
Thank you
nevermind. I kept troubleshooting and finally got it to work. For those that this might help, unlike VBA, it's easier not to declare variables as a type. just Dim them and move on. see below:
Dim conn
Set conn = createobject("Adodb.Connection")
Dim sConnString
Dim SqlStatement
StartScript
Sub StartScript()
sSourceServer = InputBox ("Enter the name of the SQL Server","Enter SQL Server Name","")
If Len(sSourceServer) = 0 Then
MsgBox "No SQL Server was specified.", , "Unable to Continue"
Exit Sub
End if
sSourceDB = InputBox ("Enter the name of the Law SQL Database","Enter Law SQL DB Name","")
If Len(sSourceDB) = 0 Then
MsgBox "No SQL DB was specified.", , "Unable to Continue"
Exit Sub
End if
' Create the connection string.
sConnString = "Provider=SQLOLEDB;Data Source=" & sSourceServer & "; Initial Catalog=" & sSourceDB & "; Integrated Security=SSPI;"
' Open the connection and execute.
conn.Open sConnString
conn.CommandTimeout = 900
SqlStatement = "UPDATE [tablename] " & _
"SET UUID = CASE WHEN CHARINDEX('.',[Filename]) > 1 THEN LEFT(CAST([Filename] AS VARCHAR),CHARINDEX('.',[Filename])-1) ELSE [Filename] END " & _
"WHERE [Filename] IS NOT NULL"
conn.Execute(SqlStatement)
conn.Close
Set rs = Nothing
SqlStatement = vbNullString
End Sub
MsgBox "All Done! Go Check your results!"
Remember, for those looking to use this as a basis for a script - I'm not doing any checks, so if you don't know your data, this is a dangerous thing to run.
Know your data, backup your data, and if you can, add in some checks, to make sure anything that isn't a select statement, is checked and re-checked before it is run.

Performing SQL queries on basic Excel 2013 worksheet as table using ADO with VBA triggers Errors

I'm developping modules on a client XLSm with 32-bits 2013 Excel.
I'd like to use datas on worksheet as if it is an Access table.
With a lot of difficulties, I think connection is now OK.
Still, I have error : 3001 Arguments are of wrong type, are out of acceptable range. Error that I cannot understand.
Here excerpts of VBA lines :
In addition, I added 20 lines in data Worksheet below the header line to permit to Excel to interpret for the type of each columns.
varCnxStr = "Data Source=" & G_sWBookREINVOICingFilePath & ";" & "Extended Properties='Excel 12.0 Xml;HDR=YES;IMEX=15';"
With conXLdb
.Provider = "Microsoft.ACE.OLEDB.12.0"
.Mode = adModeShareExclusive
.Open varCnxStr
End With
strSQL = "SELECT * "
strSQL = strSQL & " FROM [ReInvoiceDB$B2B5072] inum "
strSQL = strSQL & " WHERE inum.InvoiceNum LIKE '1712*' "
strSQL = strSQL & ";"
'>> TRIGGERs ERROR with the current Where Clause !!'
adoXLrst.Open strSQL, conXLdb, dbOpenDynamic, adLockReadOnly, adCmdText
If adoXLrst.BOF And adoXLrst.EOF Then
'no records returned'
GoTo Veloma
End If
adoXLrst.MoveFirst
Do While Not adoXLrst.EOF
'Doing stuff with row'
adoXLrst.MoveNext
Loop
sHighestSoFar = adoXLrst(1).Value '> just to try for RecordSet : Codes are not completed...
sPrefixeCURR = Mid(sHighestSoFar, 1, 4)
Highest = CInt(Mid(sHighestSoFar, 5))
'> Increment >'
Highest = Highest + 1
HighestStr = sPrefixeCURR & Format(Highest, "00")
strGSFNumber = HighestStr
adoXLrst.Close
conXLdb.Close
Veloma:
On Error Resume Next
Set adoXLrst = Nothing
Set conXLdb = Nothing
Exit Sub
Etc.
Any idea about what seems be wrong ?
Thank you
Below is an old example I have been using successfully. Note that the sheet name in the book are Sheet1 and Sheet2, but in the query I had to use sheet1$ and sheet2$. I noticed you had $ signs in the middle of your sheet names. perhaps that's the issue ?
Sub SQLUpdateExample()
Dim con As ADODB.Connection
Dim rs As ADODB.Recordset
Set con = New ADODB.Connection
con.Open "Driver={Microsoft Excel Driver (*.xls)};" & _
"DriverId=790;" & _
"Dbq=" & ThisWorkbook.FullName & ";" & _
"DefaultDir=" & ThisWorkbook.FullName & ";ReadOnly=False;"
Set rs = New ADODB.Recordset
Set rs = con.Execute("UPDATE [Sheet1$] inner join [Sheet2$] on [Sheet1$].test1 = [Sheet2$].test1 SET [Sheet1$].test3 = [Sheet2$].test2 ")
Set rs = Nothing
Set con = Nothing
End Sub
To give more details about the whole module to be implemented : it is to perform a Transaction unit.
This transaction will comprise 3 operations : get a max value from a column (Invoice number) to increment it, record the new number inside an Access table (by DAO), the same Excel file (by ADO) and generating document on HDD.
So it is aimed to use the Excel file as a table not as a file manipulated with Windows script or Excel VBA. My end user is disturbed by the pop-uping of an Excel opening file operation. As a developer, I'm feeling more comfortable with using SQL statements as much as possible inside Transaction session. Is that your opinion too ?