Error 3027, Database or object is ready only? - vba

When I run the code blow on, it stops and gives me an error
3027
Database or Object is Read only
When I clicked debug, it pointed at rec.edit.
Yet I have no idea how it is read only. I did check to make sure that the object was closed and the same error still came up. The person who came up with it said it worked for them and that they didn't have any issues with readonly. Any ideas?
Public Function HitTest()
Dim db As Database
Dim rec As DAO.Recordset
Dim fld As DAO.Field
Set db = CurrentDb
Set rec = db.OpenRecordset("PlayerSal")
EditTable = "PlayerSal"
For Each fld In rec.Fields
If fld.Name <> "Name" And fld.Name <> "Salary" And Left(fld.Name, 4) <> "Per_" Then
strFieldName = "Per_" & fld.Name & ""
'rs.Fields (strFieldName)
'X = "IIf(rec([" & fld.Name & "]) <> 0, Format((rec([Salary]) / rec([" & fld.Name & "])), '$#,###.##'), 0)"
If FieldExists(EditTable, strFieldName) Then
Else
'AltTable = "ALTER TABLE " & EditTable & " ADD COLUMN " & strFieldName & " Double;"
'CurrentDb.Execute (AltTable)
End If
rec.Edit
X = IIf(rec((fld.Name)) <> 0, Format((rec("Salary") / rec((fld.Name))), "$#,###.##"), 0)
rec.Fields(strFieldName).Value = X
rec.Update
End If
Next fld
End Function
Because I know that Access can be really silly at times, I decided to try a new Database and just import the few files I needed. I've had times where doing that randomly makes things work for some reason. When I imported the module you see below, it then stopped at the
If FeildsExists(EditTable, strFieldName)
and it said Sub or Function not defined... I don't know if either of these two are related to something simple.. but this is getting silly... especially when the guy who put this together had it work fine.

I know the second half. :o) Your function name is wrong. You have "If FeildsExists" and it should be "If FieldExists".

Related

Master-Detail Query in SQL

I am having trouble querying my data where the expected result is a master-detail type output.
I have a table. In this table I have three columns, they are all strings:
Version
URL
Application
In this table, I have the following data:
**Version** **URL** **Application**
New http://www.stackoverflow1.com Application1
New http://www.stackoverflow2.com Application1
Old http://www.stackoverflow3.com Application2
The expected Output would be
New - Application 1 - (2)
http://www.stackoverflow1.com
http://www.stackoverflow2.com
Old - Application 2 - (1)
http://www.stackoverflow3.com
This table represents an inventory of applications that are deployed on a company’s network. An application can exist on multiple URLs, and be one of two versions, in this example “new” or “old
“. The goal of the query I am having a problem with is to be able to provide a report where the Version, then Application, group the URLs so that one could see, for example, I have the “new” version of application “X” deployed at such and such URLs. In addition, I also need to provide the amounts/counts of URL’s for each grouping of Versions and Application, for example the “new” version of application “X” appeared this many times (this data will eventually be exported from SQL to a spreadsheet).
You likely don't need to write any code.
Use the report wizard - it will group for you.
Assuming you have the table in Access. Just click on the table (highlight).
Then from ribbon create - choose the report wizard.
ORDER that you choose the fields is VERY important.
So, application, version, URL.
Group by Application, version.
Choose "stepped"
The report will look like this:
Now, you can save the report -
Now open report in design mode.
Now from ribbon - choose Group and sort.
Choose to add a sum - but choose your "new" column - it will offer a count due to this being a text value.
You get this:
And then you can move up the total box to the detail section.
You get this:
I suppose you could consider a SQL group by, but the sorting and grouping with the report writer can quite much group and total on rows of data just about anyway you want.
You can remove all the extra heading stuff and other junk - once done, then from the ribbon you can export to excel.
Below is some VBA code that uses two recordsets, one to get the "title" information for each group, and the other to get the detailed information for each group, and outputs it all to an Excel file:
Sub sExportAppData()
On Error GoTo E_Handle
Dim db As DAO.Database
Dim rsMaster As DAO.Recordset
Dim rsDetail As DAO.Recordset
Dim strSQL As String
Dim objXL As New Excel.Application
Dim objXLBook As Excel.Workbook
Dim objXLSheet As Excel.Worksheet
Dim strXLFile As String
Dim lngRow As Long
strXLFile = "J:\downloads\app-data.xlsx"
If Len(Dir(strXLFile)) > 0 Then Kill strXLFile
Set db = DBEngine(0)(0)
strSQL = "SELECT A.AppVersion, A.AppApplication, Count(A.AppApplication) AS AppFrequency " _
& " FROM tblApplication A " _
& " GROUP BY A.AppVersion, A.AppApplication " _
& " ORDER BY A.AppVersion ASC, A.AppApplication ASC;"
Set rsMaster = db.OpenRecordset(strSQL)
If Not (rsMaster.BOF And rsMaster.EOF) Then
Set objXLBook = objXL.Workbooks.Add
Set objXLSheet = objXLBook.Worksheets(1)
lngRow = 1
Do
objXLSheet.Cells(lngRow, 1) = rsMaster!AppVersion & " - " & rsMaster!AppApplication & " - (" & rsMaster!AppFrequency & ")"
lngRow = lngRow + 1
strSQL = "SELECT AppURL FROM tblApplication " _
& " WHERE AppVersion='" & rsMaster!AppVersion & "' AND AppApplication='" & rsMaster!AppApplication & "' " _
& " ORDER BY AppURL ASC;"
Set rsDetail = db.OpenRecordset(strSQL)
If Not (rsDetail.BOF And rsDetail.EOF) Then
Do
objXLSheet.Cells(lngRow, 1) = rsDetail!AppURL
lngRow = lngRow + 1
rsDetail.MoveNext
Loop Until rsDetail.EOF
End If
rsMaster.MoveNext
Loop Until rsMaster.EOF
objXLBook.SaveAs strXLFile
End If
sExit:
On Error Resume Next
rsDetail.Close
rsMaster.Close
Set rsDetail = Nothing
Set rsMaster = Nothing
Set objXLSheet = Nothing
objXLBook.Close
Set objXLBook = Nothing
objXL.Quit
Set objXL = Nothing
Exit Sub
E_Handle:
MsgBox Err.Description & vbCrLf & vbCrLf & "sExportAppData", vbOKOnly + vbCritical, "Error: " & Err.Number
Resume sExit
End Sub
Regards,

SQL statement in ms access db bringing up InputBox

I have VBA for a form.
I'm trying to take the information in a textbox on the form and update a particular field in a table. (haven't figured out how to do that properly)
This line of code is my current try but I'm getting unexpected behavior
The program doesn't continue executing after this
If (Not IsNull([New_Value_Box].Value)) Then
DoCmd.RunSQL "Update [Export_NDC_Certification] Set " & [Field_List].Value & " = " & [New_Value_Box].Value & " WHERE SellerLoanIdentifier = " & Current_Loan
End If
it does however open an input box with the value of Current_Loan as the caption. It doesn't appear to do anything with the input and it doesn't execute any further code. I've used MsgBox's for debugging and its definitely coming from this line. This line was what I came across for taking a value and updating a particular table value with it. if this isn't the way to do it any push in the right direction would be appreciated. Thank you!
First, I would recommend using the Execute method (of either DAO.Database or DAO.QueryDef), instead of using DoCmd.RunSQL. This makes debugging a lot easier (here's a forum post with more information).
Also, since it seems that you need values in all your controls ([Field_List], [New_Value_Box], and Current_Loan), you should do a null check on all of those.
As noted by #HansUp, your actual SQL string is likely causing the issue, so you probably want to store that in a separate variable you can then output to the immediate window.
With all that being said, revised code might look something like this:
Dim db As DAO.Database, qdf As DAO.QueryDef
Dim strSQL As String
If _
IsNull([New_Value_Box].value) Or _
IsNull([Field_List].value) Or _
IsNull([Current_Loan].value) _
Then
' handle missing input
Else
' we know all required fields have values, so can proceed
strSQL = _
"UPDATE [Export_NDC_Certification " & _
"SET " & [Field_List].value & "=" & [New_Value_Box].value & " " & _
"WHERE SellerLoanIdentifier=" & Current_Loan
Debug.Print strSQL
Set db = CurrentDb
Set qdf = db.CreateQueryDef("")
qdf.SQL = strSQL
qdf.Execute dbFailOnError
End If
' clean up DAO objects
Set qdf = Nothing: Set qdf = Nothing: Set db = Nothing

ADO Recordset data not showing on form

I've got a frustrating issue on MS Access 2010 that I would at this stage qualify as a bug. And after having tried all possible workarounds, I am out of ideas and rely on you.
Context
Huge Ms Access 2010 application with 25k lines of VBA and >50 forms. It has a client server architecture with a frontend compiled and an Access backend on the network. It makes connections to a twentish of different databases (Oracle/SQL Server/Sybase IQ).
The problem
Sometimes when I assign an ADODB recordset to a subform, its data isn't shown in bound fields. I've got #Name? everywhere
The data is there. I can debug.print it, I can see it in the Watches browser, I can read or manipulate it while looping on the recordset object with code. It just not appear in the subform.
It can work flawlessly during months, and suddenly one form will start having this issue without any apparent reason (it might happen even on forms that I have not changed). When it happens, it does for all users, so this is really something wrong in the frontend accdb/accde.
The issue is not related to a specific DBMS/Driver. It can happen with Oracle or Sybase data.
I have created my own class abstracting everything related to ADO connections and queries, and use the same technique everywhere. I've got several tenth of forms based on it and most of them works perfectly.
I have this issue in several parts of my application, and especially in a highly complicated form with lots of subforms and code.
On this Main form, a few subforms have the issue, while others don't. And they have the exact same parameters.
The Code
This is how I populate a form's recordset :
Set RST = Nothing
Set RST = New ADODB.Recordset
Set RST = Oracle_CON.QueryRS(SQL)
If Not RST Is Nothing Then
Set RST.ActiveConnection = Nothing
Set Form_the_form_name.Recordset = RST
End If
The code called with Oracle_CON.QueryRS(SQL) is
Public Function QueryRS(ByVal SQL As String, Optional strTitle As String) As ADODB.Recordset
Dim dbQuery As ADODB.Command
Dim Output As ADODB.Recordset
Dim dtTemp As Date
Dim strErrNumber As Long
Dim strErrDesc As String
Dim intSeconds As Long
Dim Param As Variant
If DBcon.state <> adStateOpen Then
Set QueryRS = Nothing
Else
DoCmd.Hourglass True
pLastRows = 0
pLastSQL = SQL
pLastError = ""
pLastSeconds = 0
Set dbQuery = New ADODB.Command
dbQuery.ActiveConnection = DBcon
dbQuery.CommandText = SQL
dbQuery.CommandTimeout = pTimeOut
Set Output = New ADODB.Recordset
LogIt SQL, strTitle
dtTemp = Now
On Error GoTo Query_Error
With Output
.LockType = adLockPessimistic
.CursorType = adUseClient
.CursorLocation = adUseClient
.Open dbQuery
End With
intSeconds = DateDiff("s", dtTemp, Now)
If Output.EOF Then
LogIt "-- " & Format(Now, "hh:nn:ss") & " | Executed in " & intSeconds & " second" & IIf(intSeconds = 1, "", "s") & " | Now rows returned."
Set QueryRS = Nothing
Else
Output.MoveLast
pLastRows = Output.RecordCount
LogIt "-- " & Format(Now, "hh:nn:ss") & " | Executed in " & intSeconds & " second" & IIf(intSeconds = 1, "", "s") & " | " & Output.RecordCount & " row" & IIf(Output.RecordCount = 1, "", "s") & " returned."
Output.MoveFirst
Set QueryRS = Output
End If
End If
Exit_Sub:
pLastSeconds = intSeconds
Set Output = Nothing
Set Parameter = Nothing
Set dbQuery = Nothing
DoCmd.Hourglass False
Exit Function
Query_Error:
intSeconds = DateDiff("s", dtTemp, Now)
strErrNumber = Err.Number
strErrDesc = Err.DESCRIPTION
pLastError = strErrDesc
MsgBox strErrDesc, vbCritical, "Error " & pDSN
LogIt strErrDesc, , "ERROR"
Set QueryRS = Nothing
Resume Exit_Sub
Resume
End Function
Things I tried so far
For the recordsets I tried every possible variation of
.LockType = adLockPessimistic
.CursorType = adUseClient
.CursorLocation = adUseClient
The subforms handling the recordsets have all a Snapshot recordsettype, problem remains if I try dynaset.
Dataentry, Addition, deletion, edits are all disabled. It's pure read-only.
I have a habit of disconnecting the recordsets using RST.ActiveConnection = Nothing so I can manipulate them afterwards, but this doesn't impact the problem either.
It can happens with very simple queries with only one field in the SELECT clause and only one field bound to it on a subform.
Reimporting all objects in a fresh accdb doesn't solve the problem either.
The solution proposed by random_answer_guy worked at first glance, which accreditate the bug hypothesis. Unfortunately my problems reappeared after some (totaly unrelated) changes in the main form. I am back with 4 or 5 subforms not showing data and adding/removing a Load event on all or part of them doesn't make any difference anymore
If you want more information about how weird is this issue, I advise you to read my comment on random_answer_guy's answer.
To conclude
What is extremely frustrating is that I can have 2 different forms with exactly the same properties and same fields, same SQL instruction over the same DB, same recordset management code: One is showing the data and the other doesn't !
When the problem happens, I have no other choice than erasing all objects manipulated and reimporting them from an older version or recreate them from scratch.
If this is not a bug, I am still looking for the proper word to qualify it.
Does anyone ever experienced the issue and has an explanation and/or a workaround to propose ?
I've had this same issue before and simply adding a blank Form_Load event solved the problem. No code needs to be with the Form_Load it just needs to be present.
So nobody could give at this stage a clear answer to the main question :
Why is this bug happens ?
In the meantime I have "elegantly" bypassed the issue by changing the method used for the subforms encountering the bug, from ADO to DAO.
I have created a new method in my ADO abstracting class, that actually use DAO to return a recordset (not logical, but hey...).
The code where I pass data to the form becomes :
Set RST = Nothing
Set RST = Oracle_CON.QueryDAORS(SQL)
If Not RST Is Nothing Then
Set Form_the_form_name.Recordset = RST
End If
And here's the method QueryDAORS called :
Public Function QueryDAORS(ByVal SQL As String, Optional strTitle As String) As DAO.Recordset
Dim RS As DAO.Recordset
Dim dtTemp As Date
Dim strErrNumber As Long
Dim strErrDesc As String
Dim intSeconds As Long
Dim Param As Variant
On Error GoTo Query_Error
dtTemp = Now
If DBcon.state <> adStateOpen Then
Set QueryDAORS = Nothing
Else
DoCmd.Hourglass True
Set pQDEF = CurrentDb.CreateQueryDef("")
pQDEF.Connect = pPassThroughString
pQDEF.ODBCTimeout = pTimeOut
pQDEF.SQL = SQL
pLastRows = 0
pLastSQL = SQL
pLastError = ""
pLastSeconds = 0
LogIt SQL, strTitle, , True
Set RS = pQDEF.OpenRecordset(dbOpenSnapshot)
intSeconds = DateDiff("s", dtTemp, Now)
If RS.EOF Then
LogIt "-- " & Format(Now, "hh:nn:ss") & " | Executed in " & intSeconds & " second" & IIf(intSeconds = 1, "", "s") & " | Now rows returned."
Set QueryDAORS = Nothing
Else
RS.MoveLast
pLastRows = RS.RecordCount
LogIt "-- " & Format(Now, "hh:nn:ss") & " | Executed in " & intSeconds & " second" & IIf(intSeconds = 1, "", "s") & " | " & RS.RecordCount & " row" & IIf(RS.RecordCount = 1, "", "s") & " returned."
RS.MoveFirst
Set QueryDAORS = RS
End If
End If
Exit_Sub:
pLastSeconds = intSeconds
Set RS = Nothing
DoCmd.Hourglass False
Exit Function
Query_Error:
intSeconds = DateDiff("s", dtTemp, Now)
strErrNumber = Err.Number
strErrDesc = Err.DESCRIPTION
pLastError = strErrDesc
MsgBox strErrDesc, vbCritical, "Error " & pDSN
LogIt strErrDesc, , "ERROR"
Set QueryDAORS = Nothing
Resume Exit_Sub
Resume
End Function
The property pPassThroughString is defined with another Method using the properties that I already had at my disposal in the class, because they were neccessary to open an ADO connection to the database :
Private Function pPassThroughString() As String
Select Case pRDBMS
Case "Oracle"
pPassThroughString = "ODBC;DSN=" & pDSN & ";UID=" & pUsername & ";Pwd=" & XorC(pXPassword, CYPHER_KEY)
Case "MS SQL"
pPassThroughString = "ODBC;DSN=" & pDSN & ";DATABASE=" & pDBname & ";Trusted_Connection=Yes"
Case "Sybase"
pPassThroughString = "ODBC;DSN=" & pDSN & ";"
Case Else
MsgBox "RDBMS empty ! ", vbExclamation
LogIt "RDBMS empty ! ", , "ERROR"
End Select
End Function
So the issue was solved rapidly by just changing the recordset assigned to the forms from ADODB.Recordset to DAO.recordset and adapting the method called from .OpenRS to .OpenDAORS.
The only con is that with DAO I can't use this anymore to disconnect the recordset:
Set RST.ActiveConnection = Nothing
Still, I would have prefered to get an explanation and fix :(

How to locate a rogue SELECT statement in an Access frontend?

I have two version of my access frontend. One for Access 2003 which is still being run by a few computers not yet upgraded to Access2010 and Win7, the Acces2010 version unfortunately is caused in Access crash in 2003 at close of the mainform that I have been unable to fix. Backend is SqlServer 2005 SqlExpress version.
Therefore I am stuck with the older frontend for 2003 people, who fortunately don't need the newer capabilities in 2010 version.
Now, a maintenance utility which loads data from a mainframe dump is getting blocked by a "SELECT 1 on Patient_Clinic_Visits" when the Access 2003 version is running somewhere. What I can't find, is where that "SELECT 1 on Patient_Clinic_visits" is coming from.
I have looked in all the module code, and all the queries, but can't find anything like that.
I assume it must therefore be in the frontend form, but how do iI search that without looking through all the objects and controls of that form for RecordSource with some SQL code in it?
cheers,
JonHD
In the end I searched some other questions and thought about programmatically dumping the likely offending information. This is my concoction of two different answers to do what I want. Do to the limits of the Instant Window in VBA over how many lines it will keep from a Debug.Print, I have used WScript object to dump to a log file.
The code basically:
opens each form in the database in turn
dumps its RecordSource description
then for each control on its form, dumps relevant information that MAY contain SQL in some way
note: I use the fact that a Writeline (some code) that causes and error will fail and not write to avoid a lot of testing for different control types for which properties to dump or not dump.
then closed the forms
then it goes through all the queries in the database, and dumps SQL code
(Note1: in the end this didn't find the answer to my problem - see my other recent question!!)
(Note2: this was a quick and dirty script. I noticed the first time it ran the WriteStream didn't write anything, even though it on Step ing through the code it seemed to be doing something. Anyhow when I ran it again it worked. Haven't taken the time to debug why!).
Function DumpFormsAndQueries()
Dim obj As AccessObject
Dim objctrl As Control
Dim frm As Form
Dim dbs As Object
Dim fsoSysObj As FileSystemObject
Dim filFile As Object
Dim txsStream As TextStream
Dim strPath As String
Set dbs = Application.CurrentProject
Set fsoSysObj = New FileSystemObject
' Return Windows Temp folder.
strPath = "C:\Temp\"
On Error Resume Next
' See if file already exists.
Set filFile = fsoSysObj.GetFile(strPath & "Database_Form_dump.Log")
' If not, then create it.
If Err <> 0 Then
Set filFile = fsoSysObj.CreateTextFile(strPath & "Database_Form_dump.Log")
End If
Debug.Print ">> dumping to: " & strPath & "Database_form_dump.log"
Set txsStream = filFile.OpenAsTextStream(ForAppending)
For Each obj In dbs.AllForms
DoCmd.OpenForm obj.name, acDesign
Set frm = Forms(obj.name)
Debug.Print ">>>> dump form: " & obj.name
txsStream.WriteLine "====================================================================="
txsStream.WriteLine "Form : " & obj.name
txsStream.WriteLine "RecordSource: " & frm.RecordSource
txsStream.WriteLine "====================================================================="
For Each objctrl In frm.Controls
txsStream.WriteLine " --------------------------------------------------"
txsStream.WriteLine " : " & objctrl.name & " Type = " & TypeName(objctrl)
txsStream.WriteLine " --------------------------------------------------"
On Error Resume Next
txsStream.WriteLine " >>>> Recordsource: (" & objctrl.RecordSource & ")"
txsStream.WriteLine " >>>> Controlsource: (" & objctrl.ControlSource & ")"
txsStream.WriteLine " >>>> Rowsource: (" & objctrl.RowSource & ")"
txsStream.WriteLine " >>>> Caption: (" & objctrl.Caption & ")"
txsStream.WriteLine " >>>> Text: (" & objctrl.Text & ")"
txsStream.WriteBlankLines 1
Next objctrl
DoCmd.Close acForm, obj.name, acSaveNo
txsStream.WriteBlankLines 3
Next obj
txsStream.WriteLine "====================================================================="
txsStream.WriteLine " Q U E R I E S - in database"
txsStream.WriteLine "====================================================================="
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Set db = CurrentDb()
For Each qdf In db.QueryDefs
txsStream.WriteLine "Query: " & qdf.name
txsStream.WriteLine "SQL (start) ---------------------------------------------------- "
txsStream.WriteLine qdf.sql
txsStream.WriteLine "SQL (end) ---------------------------------------------------- "
Next qdf
Set qdf = Nothing
Set db = Nothing
txsStream.Close
Debug.Print ">> ended"
End Function
In Access menu select Database Tools->Database Documenter. There, select all objects and push OK. It will take some time, but then you will be presented with a report that lists everything in your database, including the code at the end.
The report could be quite big for big databases.
You can export the report to Word (there is an option for it). There, search for your string. (I think it should be "SELECT 1 from Patient_Clinic_visits")

Building SQL strings in Access/VBA

Occasionally, I have had to build a SQL string in VBA and execute it with Docmd.RunSql(). I have always built these strings by concatenating variables into the string, e.g:
Dim mysqlstring as String
mysqlstring = "INSERT INTO MyTable (Field1, Field2, Field3 ...) VALUES ("
mysqlstring = mysqlstring + Me.TextMyField1 + ", " 'parameter comments
mysqlstring = mysqlstring + Me.TextMyField2 + ", "
mysqlstring = mysqlstring + Me.TextMyField3 + ", "
...
mysqlstring = mysqlstring + ");"
Docmd.RunSql mysqlstring
VBA doesn't seem to have a unary concatenation operator (like +=) and while this doesn't look ideal, at least I can comment each of my parameters and change them independently. It makes it easier to read and to change than one monster concatenated string. But it still seems like a terrible way to build SQL strings. I have one with about 50 parameters at work, so 50 lines of mysqlstring = mysqlstring +.... Not cute.
Incidentally, that rules out the use of line-continuations to format the string, as there is a limit on the number of line-continuations you can use on a single string (hint: less than 50). Also, VBA doesn't let you put a comment after the line-continuation, grr!
Up until recently, I thought this was the only way to build these strings. But recently I have seen a different pattern, injecting the parameters in the string like this question (VB.NET) that I posted an answer on, and wondered if there was an equivalent of Parameters.AddWithValue() for VBA, or if that would even be any better than the string concatenation approach. So I figured that this deserves its own question. Maybe there's something I'm missing here.
Can some of the Access experts please clarify what are the best practices for building SQL strings in Access/VBA.
I have a timesheet app with a reasonably complex unbound labour transaction entry form. There is a lot of data validation, rate calculation and other code. I decided to use the following to create my SQL Insert/Update fields.
The variables strSQLInsert, strSQLValues, strSQLUpdate are form level strings.
Many lines of the following:
Call CreateSQLString("[transJobCategoryBillingTypesID]", lngJobCategoryBillingTypesID)
followed by:
If lngTransID = 0 Then
strSQL = "INSERT into Transactions (" & Mid(strSQLInsert, 3) & ") VALUES (" & Mid(strSQLValues, 3) & ")"
Else
strSQL = "UPDATE Transactions SET " & Mid(strSQLUpdate, 3) & " WHERE transID=" & lngTransID & ";"
End If
conn.Open
conn.Execute strSQL, lngRecordsAffected, adCmdText
Note that the Mid lines remove the leading ", ". lngTrans is the value of the autonumber primamy kay.
Sub CreateSQLString(strFieldName As String, varFieldValue As Variant, Optional blnZeroAsNull As Boolean)
' Call CreateSQLString("[<fieldName>]", <fieldValue>)
Dim strFieldValue As String, OutputValue As Variant
On Error GoTo tagError
' if 0 (zero) is supposed to be null
If Not IsMissing(blnZeroAsNull) And blnZeroAsNull = True And varFieldValue = 0 Then
OutputValue = "Null"
' if field is null, zero length or ''
ElseIf IsNull(varFieldValue) Or Len(varFieldValue) = 0 Or varFieldValue = "''" Then
OutputValue = "Null"
Else
OutputValue = varFieldValue
End If
' Note that both Insert and update strings are updated as we may need the insert logic for inserting
' missing auto generated transactions when updating the main transaction
' This is an insert
strSQLInsert = strSQLInsert & ", " & strFieldName
strSQLValues = strSQLValues & ", " & OutputValue
' This is an update
strSQLUpdate = strSQLUpdate & ", " & strFieldName & " = " & OutputValue
On Error GoTo 0
Exit Sub
tagError:
MsgBox "Error " & Err.Number & " (" & Err.Description & ") in procedure CreateSQLString of VBA Document Form_LabourEntry"
Exit Sub
End Sub
I see that the other posters are all using the Execute method. The problem with DoCmd.RunSQL is that it can ignore errors. Either of the following will display any error messages received by the query. If using DAO, use Currentdb.Execute strSQL,dbfailonerror.. For ADO use CurrentProject.Connection.Execute strCommand, lngRecordsAffected, adCmdText You can then remove the docmd.setwarnings lines.
If you're going to use docmd.setwarnings make very sure you put the True statement in any error handling code as well. Otherwise weird things may happen later on especially while you are working on the app. For example you will no longer get the "Do you wish to save your changes" message if you close an object. This may mean that unwanted changes, deletions or additions will be saved to your MDB.
Also performance can be significantly different between the two methods. One posting stated currentdb.execute took two seconds while docmd.runsql took eight seconds. As always YMMV.
Adding to what #astander has said, you could create a querydef (with parameters) and save it as part of the database.
e.g.
Parameters dtBegin DateTime, dtEnd DateTime;
INSERT into myTable (datebegin, dateend) values (dtBegin, dtEnd)
Assume, you saved it with a name myTableInsert, you could write the code as below
dim qd as QueryDef
set qd = CurrentDB.QueryDefs("myTableInsert")
qd.Parameters("dtBegin").Value = myTextFieldHavingBeginDate
qd.Parameters("dtEnd").Value = myTextFieldHavingEndDate
qd.Execute
Note: I have not tested this piece of code. But, I am guessing this should be it.
Hope this gives you enough info to get started.
Private Sub Command0_Click()
Dim rec As Recordset2
Dim sql As String
Dim queryD As QueryDef
'create a temp query def.
Set queryD = CurrentDb.CreateQueryDef("", "SELECT * FROM [Table] WHERE Val = #Val")
'set param vals
queryD.Parameters("#Val").Value = "T"
'execute query def
Set rec = queryD.OpenRecordset
End Sub
As others have said, it's probably better to utilize parameters in the first place. However, ...
I, too, have missed a concatenation operator, having become accustomed to .= in PHP. In a few cases, I've written a function to do it, though not specific to concatenating SQL strings. Here's the code for one I use for creating a query string for an HTTP GET:
Public Sub AppendQueryString(strInput As String, _
ByVal strAppend As String, Optional ByVal strOperator As String = "&")
strAppend = StringReplace(strAppend, "&", "&")
strInput = strInput & strOperator & strAppend
End Sub
And an example of where I've called it:
AppendQueryString strOutput, "InventoryID=" & frm!InventoryID, vbNullstring
AppendQueryString strOutput, "Author=" & URLEncode(frm!Author)
...and so forth.
Now, for constructing SQL WHERE clauses, you might consider something like that as a wrapper around Application.BuildCriteria:
Public Sub ConcatenateWhere(ByRef strWhere As String, _
strField As String, intDataType As Integer, ByVal varValue As Variant)
If Len(strWhere) > 0 Then
strWhere = strWhere & " AND "
End If
strWhere = strWhere & Application.BuildCriteria(strField, _
intDataType, varValue)
End Sub
You would then call that as:
Dim strWhere As String
ConcatenateWhere strWhere,"tblInventory.InventoryID", dbLong, 10036
ConcatenateWhere strWhere,"tblInventory.OtherAuthors", dbText, "*Einstein*"
Debug.Print strWhere
strSQL = "SELECT tblInventory.* FROM tblInventory"
strSQL = strSQL & " WHERE " & strWhere
...and the Debug.Print would output this string:
tblInventory.InventoryID=10036 AND tblInventory.OtherAuthors Like "*Einstein*"
Variations on that might be more useful to you, i.e., you might want to have an optional concatenation operator (so you could have OR), but I'd likely do that by constructing a succession of WHERE strings and concatenating them with OR line by line in code, since you'd likely want to place your parentheses carefully to make sure the AND/OR priority is properly executed.
Now, none of this really addresses the concatenation of VALUES for an INSERT statement, but I question how often you're actually inserting literal values in an Access app. Unless you're using an unbound form for inserting records, you will be using a form to insert records, and thus no SQL statement at all. So, for VALUES clauses, it seems that in an Access app you shouldn't need this very often. If you are finding yourself needing to write VALUES clauses like this, I'd suggest you're not using Access properly.
That said, you could use something like this:
Public Sub ConcatenateValues(ByRef strValues As String, _
intDatatype As Integer, varValue As Variant)
Dim strValue As String
If Len(strValues) > 0 Then
strValues = strValues & ", "
End If
Select Case intDatatype
Case dbChar, dbMemo, dbText
' you might want to change this to escape internal double/single quotes
strValue = Chr(34) & varValue & Chr(34)
Case dbDate, dbTime
strValue = "#" & varValue & "#"
Case dbGUID
' this is only a guess
strValues = Chr(34) & StringFromGUID(varValue) & Chr(34)
Case dbBinary, dbLongBinary, dbVarBinary
' numeric?
Case dbTimeStamp
' text? numeric?
Case Else
' dbBigInt , dbBoolean, dbByte, dbCurrency, dbDecimal,
' dbDouble, dbFloat, dbInteger, dbLong, dbNumeric, dbSingle
strValue = varValue
End Select
strValues = strValues & strValue
End Sub
...which would concatenate your values list, and then you could concatenate into your whole SQL string (between the parens of the VALUES() clause).
But as others have said, it's probably better to utilize parameters in the first place.
FWIW, I use a slightly different format, using Access's line break character "_". I also use the concatenation operator "&". The main reason is for readability:
Dim db as Database: Set db = Current Db
Dim sql$
sql= "INSERT INTO MyTable (Field1, Field2, Field3 ...Fieldn) " & _
"VALUES (" & _
Me.TextMyField1 & _
"," & Me.TextMyField2 & _
"," & Me.TextMyField3 & _
...
"," & Me.TextMyFieldn & _
");"
db.Execute s
Set db = nothing
I would use the approach above, with each parameter on a separate line it is nice and easy to debug and add to.
If however you really did not like that way then you could look at a parameter query. Slightly less flexible but in some cases slightly quicker.
Or another way would be to define a public function for inserting into that table and pass the values to it as parameters.
I however would stick with what you have got but it would be nice if VBA would understand =+
One of the things I've done in the past is create a system for parsing SQL code to find parameters and storing the parameters in a table. I would write my MySQL queries outside of Access. Then all I had to do was open the file from Access and it would be ready to be updated on the fly each time I wanted to run it.
It was a really complicated process, but I'd be happy to dig up the code next week when I get back to work if you're interested.