I want to apply an SQL query to one Access table, from which it is retrieving data from a table in another Access file. I've looked around on this subject and can't seem to get solutions to work.
Based on this source http://support.microsoft.com/kb/113701, I came up with the following, but still have no luck.
sSQL = "UPDATE TableInCurrentDB
SET [Field1InCurrentDB]= DAvg('Field1InExternalDB','[;database=C:\VB\ExternalDB.accdb].[TableInExternalDB]','Field2InExternalDB= & Year(Now()) & ')
WHERE [Field2InCurrentDB]='1';"
DoCmd.RunSQL sSQL
I know that the error lies somewhere in the reference to the external DB, because the code works fine if the tables are in the same database. However, it's tough to tell exactly what's wrong because the error I get is 'Unknown'.
How can I modify this statement to update an Access table from another Access database's table?
You prefer not to use a link to the table in the external database, but that choice is a complication when you want to use DAvg. However, since you're doing this with VBA code, you can ditch DAvg and do what you need in 2 steps:
First retrieve the average from the external table.
Use that step #1 average in your UPDATE.
For step #1, test this as a new query in the Access query designer ...
SELECT Avg(Field1InExternalDB)
FROM TableInExternalDB IN 'C:\VB\ExternalDB.accdb'
WHERE Field2InExternalDB=Year(Date());
Assuming that query returns the correct value, adapt your VBA code to retrieve the same value.
Dim db As DAO.database
Dim strSelect As String
Dim varAvg As Variant
strSelect = "SELECT Avg(Field1InExternalDB)" & vbCrLf & _
"FROM TableInExternalDB IN 'C:\VB\ExternalDB.accdb'" & vbCrLf & _
"WHERE Field2InExternalDB=Year(Date());"
'Debug.Print strSelect
Set db = CurrentDb
varAvg = db.OpenRecordset(strSelect)(0)
Debug.Print Nz(varAvg, 0) ' see note
Note that query will return Null when no rows include Field2InExternalDB values which match the current year. That is why varAvg is declared as Variant. Later Nz(varAvg, 0) will give you zero instead of Null.
Then you can use a parameter query for your UPDATE and supply Nz(varAvg, 0) as the parameter value.
Dim qdf As DAO.QueryDef
Dim strUpdate As String
strUpdate = "UPDATE TableInCurrentDB" & vbCrLf & _
"SET [Field1InCurrentDB]=[pAvg]" & vbCrLf & _
"WHERE [Field2InCurrentDB]='1';"
'Debug.Print strUpdate
Set qdf = db.CreateQueryDef(vbNullString, strUpdate)
qdf.Parameters("pAvg") = Nz(varAvg, 0)
qdf.Execute dbFailOnError
Set qdf = Nothing
Set db = Nothing
Could you not do this as a single step? Incorporate the output of the first SQL as the input to the "set" in the second?
In other words,bypass the first query and just do the second using this as the "strUpdate" string:
strUpdate = "UPDATE TableInCurrentDB" & vbCrLf & _
"SET [Field1InCurrentDB]=" & vbCrLf & _
" (SELECT Val(Nz(Avg(Field1InExternalDB),0))" & vbCrLf & _
" FROM TableInExternalDB IN 'C:\VB\ExternalDB.accdb'" & vbCrLf & _
" WHERE Field2InExternalDB=Year(Date()) )" & vbCrLf & _
"WHERE [Field2InCurrentDB]='1';"
Related
I have an issue while trying to get every line from a table in my database. In VBA when requesting the table with 'SELECT * FROM companies;', the results duplicates the first row, and remove the last one. As a result, I have 3 records, which corresponds to the real number of records in my DB, but instead of having 1, 2 and 3, I have 1, 1 and 2.
Any idea?
You can see here Database records for table 'companies', when requesting 'SELECT * FROM companies': DB Records
You can see here the result of the same request in Excel/VBA using the following code:
sqlQuery = "SELECT ALL * FROM companies;"
rsDB.Open sqlQuery, conDB, adOpenDynamic
Do While Not rsDB.EOF
For Each col In rsDB.GetRows
Debug.Print col
Next
Loop
Results: VBA request
Would love to get any piece of advice on this issue!
The fun fact is that if I try to select only one column of the table, such as 'idCompany', then I have the result '1, 2, 3' with VBA, which is fine. The real issue only appears when using '*'.
Thanks a lot for your time,
--- EDIT
The connection string used to connect to the DB:
Set conDB = New ADODB.Connection
Set rsDB = New ADODB.recordSet
Set rsDBCol = New ADODB.recordSet
conDB.connectionString = "DRIVER={MariaDB ODBC 3.1 Driver};" _
& "SERVER=s-mypricing-1;" _
& "DATABASE=db_pricing;" _
& "PORT=3306" _
& "UID=user;" _
& "PWD=pwd;" _
& "OPTION=3"
conDB.Open
rsDB.CursorLocation = adUseServer
rsDBCol.CursorLocation = adUseServer
Difficult to test, but I suspect you need this instead:
rsDB.MoveFirst
Do While Not rsDB.EOF
For Each fld In rsDB.Fields
Debug.Print fld.Name & ": " & fld.Value
Next
rsDB.MoveNext
Loop
When you iterate an ADO recordset, the object itself represents a current row. So you refer to the Fields of the current row to get the columns. And the properties of each field to get descriptive information about that cell (name of column, value in cell).
Through the comments we learned that the issue is related to opening the recordset with adOpenDynamic mode. What follows is code that should represent a working case for MaraiaDB.
Set conDB = New ADODB.Connection
Set rsDB = New ADODB.recordSet
Set rsDBCol = New ADODB.recordSet
conDB.connectionString = "DRIVER={MariaDB ODBC 3.1 Driver};" _
& "SERVER=s-mypricing-1;" _
& "DATABASE=db_pricing;" _
& "PORT=3306" _
& "UID=user;" _
& "PWD=pwd;" _
& "OPTION=3"
conDB.Open
rsDB.CursorLocation = adUseServer
rsDBCol.CursorLocation = adUseServer
sqlQuery = "SELECT ALL * FROM companies;"
With rsDB.Open(sqlQuery, conDB)
If Not (.BOF And .EOF) Then
.MoveFirst
Do Until .EOF
For Each fld In .Fields
Debug.Print fld.Name & ": " & fld.Value
Next
.MoveNext
Loop
End If
.Close
End With
conDB.Close
I am told (and agree) that it is better to replace the SQL in an existing querydef, rather than delete and re-define the querydef each time the query needs to change. But my code only seems to work the second way. Here is the code I have that works:
Dim db As Database
Set db = CurrentDb
Dim QD As QueryDef
Dim mySql As String
mySql = ""
mySql = "TRANSFORM COUNT(tblDocs.Document) AS CountOfDocument " & _
"SELECT tblDocs.[Contractor Dept], " & _
"COUNT(tblDocs.Document) AS [Total Of Document] " & _
"FROM tblDocs " & _
"GROUP BY tblDocs.[Contractor Dept] " & _
"PIVOT tblDocs.[Engineering Status Code]"
On Error Resume Next
db.QueryDefs.Delete "qryX" 'Remove temporary query if exists
Set QD = db.CreateQueryDef("qryX", mySql) 'create temporary query
DoCmd.RunSQL "SELECT * INTO tblDocsCrossTabX FROM qryX;"
Here is the code I can't get to work
Dim db As Database
Set db = CurrentDb
Dim QD As QueryDef
Dim mySql As String
mySql = " "
Set QD = db.CreateQueryDef(("qryX"), mySql)
mySql = "TRANSFORM COUNT(tblDocs.Document) AS CountOfDocument " & _
"SELECT tblDocs.[Contractor Dept], " & _
"COUNT(tblDocs.Document) AS [Total Of Document] " & _
"FROM tblDocs " & _
"GROUP BY tblDocs.[Contractor Dept] " & _
"PIVOT tblDocs.[Engineering Status Code]"
QD.SQL = mySQL 'overwrite query SQL
DoCmd.RunSQL "SELECT * INTO tblDocsCrossTabX FROM qryX;"
Oddly, the second version doesn't throw any errors at me, but it doesn't make the crosstab table at all.
Edit: Maybe I wasn't clear enough. The problem is that the second set of code Does. Not. Execute. The. SQL. If it executed the code, I would be happy to re-write and use the same temp query over and over, but it does. not. execute. the. SQL..
Please respond with how to make the code in the second block actually execute the indicated SQL statement and build the desired table.
I know I have to remake a query if I delete it. Duh.
I know I "should" be able to re-use the same query if I can get the Set statement to properly overwrite the previous sql with the desired sql.
I know you all want to provide an answer, but please make it an answer to the question I am asking.
You can recycle the object if you don't delete it:
' On Error Resume Next
Set QD = db.QueryDefs("qryX")
QD.SQL = mySQL
DoCmd.RunSQL "SELECT * INTO tblDocsCrossTabX FROM qryX;"
Solved.
In the first block of code, when I delete and recreate the query, the string mySql contains a valid SQL statement, so Access is able to assign that SQL to the querydef when I completely recreate it with SET. Specifically,
mySql = "Transform..."
Comes Before
Set QD = db.CreateQueryDef("qryX", mySql)
In the second set of code, the string mySql contains only a space when the Set command is used, as " " is not a valid SQL Statement, the Set command won't even get executed, and the QueryDef QD never even gets created.
Specifically, the error in the second code occurs because
mySql = " " 'NOT a valid SQL Statement
comes before the attempt to create the queryDef
Set QD = db.CreateQueryDef(("qryX"), mySql) 'QD never gets created because mySql is not valid SQL
preventing the assignment of a valid SQL statement later, and so no table gets created.
If you can use a subquery, you can use a tempory QueryDef instead of bothering with checking forQueryDefalready existing or not, just combine the SELECT and the INSERT-Query to use a temporaryQueryDef
Unfortunally you can't useTransformin a subquery so this code leads toRuntime-Error 3129 Invalid SQL statement.
Sql = "SELECT * INTO tblDocsCrossTabX FROM (TRANSFORM COUNT(tblDocs.Document) AS CountOfDocument " & _
"SELECT tblDocs.[Contractor Dept], " & _
"COUNT(tblDocs.Document) AS [Total Of Document] " & _
"FROM tblDocs " & _
"GROUP BY tblDocs.[Contractor Dept] " & _
"PIVOT tblDocs.[Engineering Status Code])"
With CurrentDb.CreateQueryDef(vbNullString) 'or db.CreateQueryDef("") creates a not named and therefore temporary QueryDef
.SQL = Sql
.Execute dbFailOnError
End With
Sample of successful code.
Dim db As Database
Set db = CurrentDb
Dim QueryString As String
Dim QDDocsCross As QueryDef
Set QDDocsCross = db.CreateQueryDef("DocsCross")
QueryString = "TRANSFORM COUNT(Docs.Document) AS CountOfDocument " & _
"SELECT Docs.[Contractor Dept], " & _
"COUNT(Docs.Document) AS [Total Of Document] " & _
"FROM Docs " & _
"GROUP BY Docs.[Contractor Dept] " & _
"PIVOT Docs.[Engineering Status Code]"
QDDocsCross.SQL = QueryString
I have a central database that is kept on a network that stores part numbers & descriptions for various components for machines. Often times individuals will need to use an offline copy during design and will add new entries to it. Is there a script to find the differences and update the master file that is on the network? I played around with union queries but I'm struggling to be able to update the original file and original table. My sql/microsoft access knowledge is limited.
For sake of clarity let's call the files as such:
Network Database: Network_DB.mdb
Offline Database: Offline_DB.mdb
Table: MISC_CAT
In my experience a generic solution is pretty difficult. I tend to store the field names I want in a table then run the relevant update:
Public Function UpdateData()
Dim db As Database
Dim qd As QueryDef
Dim rs As Recordset
Dim strSQL As String
On Error GoTo Err_UpdateData
Set db = CodeDb
strSQL = "SELECT fldName FROM MyTables WHERE tblName = 'MISC_CAT'"
Set rs = db.OpenRecordset(strSQL, dbOpenSnapshot)
Do Until rs.EOF
strSQL = "UPDATE MISC_CAT_Network INNER JOIN MISC_CAT_Offline ON MISC_CAT_Network.KeyID = MISC_CAT_Offline.KeyID " & _
"SET MISC_CAT_Network.[" & rs!fldName & "] = [MISC_CAT_Offline].[" & rs!fldName & "] " & _
"WHERE (((Nz([MISC_CAT_Network].[" & rs!fldName & "],''))<>Nz([MISC_CAT_Offline].[" & rs!fldName & "],'')))"
db.Execute strSQL, dbFailOnError
rs.MoveNext
Loop
db.Execute strSQL, dbFailOnError
rs.Close
db.Close
Exit_UpdateData:
Set rs = Nothing
Set qd = Nothing
Set db = Nothing
Exit Function
Err_UpdateData:
Debug.Print "Error - " & Err.Number & " - " & Err.Description
Resume Exit_UpdateData
End Function
I have a database containing locations of water wells and a ton of properties associated with those wells. All tables are linked with WELL_ID (name of the well), which is "short text" data type, from what I can tell. Within the database there are existing queries from which I'm trying to get the data (I don't want to mess with the tables in case I make a mistake and mess something up).
I've created a form where the user inputs UTM coordinates for easting and northing, as well as a search radius, then clicks a "search" button. Upon clicking search, the procedure creates a recordset of the [qryUTM_NAD83], then calculates the radial distance of each well and if it is within the specified search radius, it is stored in a new [Search_Results] table using INSERT INTO.
Now, once the well is identified as meeting search criteria the WELL_ID is stored, and passed to a function which searches through a recordset of a different query [qryFormation]. This query has a one-to-many relationship where there is a record for each geologic layer, each having the same WELL_ID (i.e. each well has multiple layers but all these layers have the same WELL_ID). I need to concatenate these layers into one string, pass them back to the search function and add it to the [Search_Results] table. However, i get a data type mismatch error in the SQL statement.
Here's the code I have:
(I've omitted some parts of code to keep it short for you all)
Private Sub SearchButton_Click()
Dim WellID As String, mySQL As String, NewLitho As String
'Creating new recordsets from [qryUTM_NAD83] table
Dim rs1 As DAO.Recordset
'[ZONE] is just user specified, helps me narrow the search a little
Set rs1 = CurrentDb.OpenRecordset("SELECT [qryUTM_NAD83].* " & _
"FROM [qryUTM_NAD83] " & _
"WHERE [ZONE] = " & frmZone, dbOpenDynaset)
'Moving through the recordset
rs1.MoveFirst
Do While Not rs1.EOF
'calculated radius, r , for this well (omitted)
If r < SearchRadius Then
WellID = Val(rs1.Fields("WELL_ID").Value)
NewLitho = LithoTransform(WellID)
mySQL = "INSERT INTO [Search_Results] " & _
"([WELL_ID], [Easting], [Northing], [Radius], [Lithology]) " & _
"VALUES (" & WellID & ", " & x & ", " & y & ", " & r & ", " & NewLitho & ")"
CurrentDb.Execute mySQL
rs1.MoveNext
End If
Loop
End Sub
The function:
(error occurs in "Set rs2..." - data type mismatch)
Public Function LithoTransform(CurrentID As String) As String
'CurrentID is the well which was identified as being within the search radius
Dim rs2 As DAO.Recordset
Dim mySQL2 As String
'SQL statement and new recordset of the well we are looking at in the search
Debug.Print CurrentID
mySQL2 = "SELECT [qryFormation].* " & _
"FROM [qryFormation] " & _
"WHERE [WELL_ID] = " & CurrentID
Set rs2 = CurrentDb.OpenRecordset(mySQL2, dbOpenDynaset)
'Move through recordset rs2 and concatenating into new string
'Bunch of code here
'Close recordset set it to 'nothing'
End Function
Since WELL_ID is text type, include quotes around the value of CurrentID when you include it in your WHERE clause. So the quickest fix is probably this ...
mySQL2 = "SELECT [qryFormation].* " & _
"FROM [qryFormation] " & _
"WHERE [WELL_ID] = '" & CurrentID & "'"
However, you could switch to a parameter query instead and thereby avoid issues with quotes. Here is an untested example ...
Dim db As DAO.Database
Dim qdf AS DAO.QueryDef
Dim rs2 As DAO.Recordset
Dim mySQL2 As String
mySQL2 = "SELECT [qryFormation].* " & _
"FROM [qryFormation] " & _
"WHERE [WELL_ID] = [which_id]"
Set db = CurrentDb
Set qdf = db.CreateQueryDef(vbNullString, mySQL2)
qdf.Parameters("which_id") = CurrentID
Set rs2 = qdf.OpenRecordset
Use the Access help system to check functions, syntax, etc. in that code in case I made errors. Since you're new to Access, it will be to your advantage to get comfortable with its help system.
After I edit information and change the information and click update, it gives me a error. I tried the parenthesis brackets no luck.
Too few parameters Expected 1. Run time error '3061'
Private Sub cmdUpdate_Click()
Dim strSql As String
strSql = "UPDATE PlantTransaction " & _
"SET TransactionID=" & Me.txtTranID & _
",[Plant Number]='" & Me.txtPlantNo & "'" & _
",TransactionDate=#" & Me.txtTransDate & "#" & _
",Opening_Hours='" & Me.txtOpeningHRS & "'" & _
",Closing_Hours='" & Me.CloseHrs & "'" & _
",Fuel='" & Me.txtFuel & "'" & _
",[Fuel Cons Fuel/Hours]='" & Me.txtFuelConsFuelHr & "'" & _
",[Hour Meter Replaced]='" & Me.txtHrMtrRep & "'" & _
",Comments='" & Me.txtComments & "'" & _
",[Take on Hour]='" & Me.txtTOH & "'" & _
" WHERE TransactionID=" & Me.PlantTransactionQuery.Form.Recordset.Fields("Tr ansactionID")
Debug.Print strSql ' <- prints to Immediate window
CurrentDb.Execute strSql, dbFailOnError
cmdClear_Click
Me.PlantTransactionQuery.Form.Requery
End Sub
You were smart to include this line in your code:
Debug.Print strSql ' <- prints to Immediate window
Now when you get the missing parameter message, go to the Immediate window (you can use Ctrl+g to go there) and copy the SQL statement.
Then create a new Access query in the query designer, switch to SQL View, and paste in the text you copied. When you attempt to run that query, Access will present a parameter input box which includes the name of whatever it thinks is the parameter.
Compare that parameter name with the field names in your data source. Often this situation occurs because the query includes a misspelled field name. Another possibility with an UPDATE is that one of the values you're trying to update is unquoted text. Regardless of the cause, the parameter name from that input box should help you track it down. Show us the actual text from that UPDATE statement if you need further help.
Any time that you "glue together" a long SQL statement with lots of user input you face the challenges of
correctly delimiting strings and dates,
escaping delimiters within such fields (usually quotes inside a text field), and
getting all of the required commas in the right places
You can avoid those annoyances by using a Recordset to perform the update:
Dim rst As DAO.RecordSet
Set rst = CurrentDb.OpenRecordset("PlantTransaction", dbOpenDynaset)
rst.FindFirst "TransactionID=" & Me.PlantTransactionQuery.Form.Recordset.Fields("Tr ansactionID")
If Not rst.NoMatch Then
rst.Edit
rst!TransactionID = Me.txtTranID
rst![Plant Number] = Me.txtPlantNo
rst!TransactionDate = Me.txtTransDate
rst!Opening_Hours = Me.txtOpeningHRS
rst!Closing_Hours = Me.CloseHrs
rst!Fuel = Me.txtFuel
rst![Fuel Cons Fuel/Hours] = Me.txtFuelConsFuelHr
rst![Hour Meter Replaced] = Me.txtHrMtrRep
rst!Comments = Me.txtComments
rst![Take on Hour] = Me.txtTOH
rst.Update
End If
rst.Close
Set rst = Nothing