Corrupted Access 2010 database - recover query definitions - ms-access

I have an Access DB, it's a local development copy, not production, and corrupted during (ironically) compact and repair, a large amount of data. Unfortunately I did a lot of work on it and I do have a backup plan but it failed me..
When I start it up it gives me useless error messages like:
'Id' is not an index in this table.
'ParentIdName' is not an index in this table.
I suppose these indexes are part of the hidden MSys* tables in the DB. I managed to read those and they're mostly empty while my older healthy backup has a lot of data in these tables.
First I tried to restore the healthy tables into the destroyed ones using VBA and an ADO connection (made a copy beforehand), I get a lot of errors saying I don't have write permissions on the tables.
Next attempt, still ongoing, is to recover the query definitions (90%+ of my changes) through the same ADO connection (tried ADOX and DAO too).
The most successful attempt was ADO:
Sub DebugPrintQueryDefsADO()
Dim dmgDB As DAO.Database
Dim dmgQD As DAO.QueryDef
Set dmgDB = DBEngine.OpenDatabase("C:\Database.accdb", , True)
For Each dmgQD In dmgDB.QueryDefs
If Left(dmgQD.Name, 1) <> "~" Then ' ~ Query defs seem to be the form views'
Debug.Print "---------------------------------"
Debug.Print dmgQD.Name
Debug.Print "---------------------------------"
Debug.Print dmgQD.SQL
End If
Next dmgQD
Set dmgQD = Nothing
Set dmgDB = Nothing
End Sub
This actually works! As long as the Query definition evaluation runs fine first. Which in my case cannot be true for most QD's since the tables are imported when required and then deleted. This means that the fields defined in the QD's cannot be found because the tables aren't there. Then Access presents me an error:
3258: The SQL Statement could not be executed because it contains ambiguous outer joins. To force one of the joins to be performed first, create a separate query that performs the first join and then include that query in your SQL statement.
Go home Access, you're drunk.
Next quest was to figure out how to not evaluate the SQL statement (I only need the SQL statement, not the result). I found that there is a property: Querydef.Prepare in this Access 2007 doc: http://msdn.microsoft.com/en-us/library/office/bb208511(v=office.12).aspx
According to the documentation (of Access 2007, I have 2010), I should set
Querydef.Prepare = dbQUnprepare. But that does not work..
So long problem short..
Is anyone aware of a way to fix the corrupted DB?
OR..
Does anyone know how to retrieve my SQL statements from the query definitions without the need to evaluate them?
Thanks!

One option you could try would be
Application.SaveAsText acQuery, "QueryName", "C:\path\QueryName.txt"
At least in some cases it dumps the SQL string at the beginning of the file (with some arbitrary breaks and escaped characters, just to keep things interesting). For example:
dbMemo "SQL" ="SELECT YEAR(Assigned.[Date]) AS Yr, MONTH(Assigned.[Date]) AS Mo, SUM(Assigned.["
"Hours Worked]) AS Hrs\015\012FROM Assigned INNER JOIN Projects ON Projects.[Proj"
"ect Name] = Assigned.[Project Name]\015\012WHERE Projects.Billable\015\012GROUP "
"BY YEAR(Assigned.[Date]), MONTH(Assigned.[Date]);\015\012"
dbMemo "Connect" =""
dbBoolean "ReturnsRecords" ="-1"
dbInteger "ODBCTimeout" ="60"
dbBoolean "OrderByOn" ="0"
dbByte "Orientation" ="0"
dbByte "DefaultView" ="2"
dbBinary "GUID" = Begin
0x1304ebdf78bef2459211d478954077cd
End
dbBoolean "FilterOnLoad" ="0"
dbBoolean "OrderByOnLoad" ="-1"
dbLongBinary "DOL" = Begin
0x0acc0e5500000000534cc617867b4d43b98002a1b002c8eb00000000c732603a ,
0xa958e4400000000000000000410073007300690067006e006500640000000000 ,
0x00006b84dfe37aec2248a1ccfe3e157361df0000000040b3affda358e4400000 ,
0x000000000000500072006f006a006500630074007300000000000000d5b092f5 ,
0x1c13394884636d668e066e66070000001304ebdf78bef2459211d478954077cd ,
0x59007200000000000000efa363bc29cbfc478a958e7b935d03da070000001304 ,
0xebdf78bef2459211d478954077cd4d006f00000000000000b1174c1abe1d3d4c ,
0xb9a1a7e5915a2e47070000001304ebdf78bef2459211d478954077cd48007200 ,
0x7300000000000000b2e4aa9aab3a9e479c47b2772754fce107000000534cc617 ,
0x867b4d43b98002a1b002c8eb4400610074006500000000000000404c3a80ef29 ,
0xa4498e153e354319e84c07000000534cc617867b4d43b98002a1b002c8eb4800 ,
0x6f00750072007300200057006f0072006b0065006400000000000000a3a95acb ,
0xe7ec994bab8df870ce5b3d98070000006b84dfe37aec2248a1ccfe3e157361df ,
0x500072006f006a0065006300740020004e0061006d006500000000000000f5cb ,
0xefb11bcf5e4da4b2806eed09a70207000000534cc617867b4d43b98002a1b002 ,
0xc8eb500072006f006a0065006300740020004e0061006d006500000000000000 ,
0x453445bf7e67b54ead2c9317ca29d906070000006b84dfe37aec2248a1ccfe3e ,
0x157361df420069006c006c00610062006c006500000000000000000000000000 ,
0x000000000000000000000c000000050000000000000000000000000000000000
End
dbByte "PublishToWeb" ="1"
Begin
Begin
dbText "Name" ="Yr"
dbLong "AggregateType" ="-1"
dbBinary "GUID" = Begin
0xd5b092f51c13394884636d668e066e66
End
End
Begin
dbText "Name" ="Mo"
dbLong "AggregateType" ="-1"
dbBinary "GUID" = Begin
0xefa363bc29cbfc478a958e7b935d03da
End
End
Begin
dbText "Name" ="Hrs"
dbLong "AggregateType" ="-1"
dbBinary "GUID" = Begin
0xb1174c1abe1d3d4cb9a1a7e5915a2e47
End
End
End

You can use Access Fix from AccessFix.com, if you have a copy of the database, which just got corrupted, and you didn't try anything on it that may have changed the data. Like, if you tried to compact and repair, your objects are gone after that. I use it at work and it fixes dbs for me in most cases. I would also try http://allenbrowne.com/tips.html, can't quite remember, but he had a set of steps there that you can use to recover the entire database from text. I think he explained it in his tips and tricks book, with Dough Steele, also

Related

Query of concatenation on Access [duplicate]

Any help that can be provided to a Access and VB noob would be greatly appreciated. What I'm trying to do is concatenate the values from one table and insert it as a comma delimited value into a field in another table. I'm trying to take all the server names that are say Linux boxes and concatenate them into a different field.
Table A looks like this
Machine Name | Zone | Operating System
----------------------------------------
Server01 Zone A Linux
Server02 Zone B Linux
Server03 Zone A Windows
Server04 Zone C Windows
Server05 Zone B Solaris
Table B has the field I want to insert into: Affected_Machine_Names.
Now, I've tried looking through the Concatenate/Coalesce posts, but the SQL view in Access doesn't like the Declare statements. My VB skills suck badly and I can't seem to get the code to work in VB for Applications. Unfortunately, I can't get this database converted into our SQL farm cause I don't have a server available at the moment to host it.
Can anyone point me in the right direction?
You can use Concatenate values from related records by Allen Browne for this. Copy the function code from that web page and paste it into a new standard module. Save the module and give the module a name different from the function name; modConcatRelated would work.
Then I think you should be able to use the function in a query even though you're not proficient with VBA.
First notice I changed the field names in TableA to replace spaces with underscores. With that change, this query ...
SELECT
sub.Operating_System,
ConcatRelated("Machine_Name", "TableA",
"Operating_System = '" & sub.Operating_System & "'") AS Machines
FROM [SELECT DISTINCT Operating_System FROM TableA]. AS sub;
... produces this result set:
Operating_System Machines
Linux Server01, Server02
Solaris Server05
Windows Server03, Server04
If you can't rename the fields as I did, use a separate query to select the distinct operating systems.
SELECT DISTINCT TableA.[Operating System]
FROM TableA;
Save that as qryDistinctOperatingSystems, then use it in this version of the main query:
SELECT
sub.[Operating System],
ConcatRelated("[Machine Name]", "TableA",
"[Operating System] = '" & sub.[Operating System] & "'") AS Machines
FROM qryDistinctOperatingSystems AS sub;
This is a fairly basic VBA function that will loop through every row in a column, and concatenate it to a comma-delimited result string. i.e., for your example, it will return "Server01, Server02, Server03, Server04, Server05". (Don't forget to replace the column and table names)
Function ConcatColumn(OS As String) As String
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordset("Select * from TableA")
Dim result As String
'For every row after the first, add a comma and the field value:
While rst.EOF = False
If rst.Fields("Operating System") = OS Then _
result = result & ", " & rst.Fields("MyValue")
rst.MoveNext
Wend
'Clean it up a little and put out the result
If Left(result, 2) = ", " Then result = Right(result, Len(result) - 2)
Debug.Print result
ConcatColumn = result
End Function
To use this,
1. ConcatColumn("Windows") will return "Server04, Server03"
2. ConcatColumn("Linux") will return "Server01, Server02"
3. ConcatColumn("Solaris") will return "Server05"
4. ConcatColumn("") will return "".

Access with 1 MySQL Table asking for credentials

I have a access database using a local sql server backend for all tables except 1 web based MySQL table. The MySQL table has 50 rows or so, 3 fields, not big at all. I have a odbc connection setup and the table is linked with the password saved. This table is updated 30 times per day at most... Sometimes the connection breaks and the MySQL connection popup will appear... clicking test will result in a success, and clicking ok will allow the code to proceed. It is doing a 1 line update (SET LastUpdatedDate = #" & now() & "# WHERE ItemID = 'xyz').
I want to capture an error, or get it to continue without the connection if it is unavailable... but it appears no error is generated. I would rather not update the table when this happens, then have to physically select ok to get it running again. This problem exists from multiple locations, on multiple PCs around the US. I assume it is the server the MySQL db is hosted on that is having problems - I just want to know how to ignore them and move on with the other code... again, no error generated (So On Error ... won't work). Any Ideas? Using Access 2016.
UPDATE: My current setup is to ping the server... and if the ping gets a response, I assume it is up... then I run 'CurrentDb.Execute "UPDATE XYZ SET ABC = 'DEF' WHERE GHI = 'JKL'". That simple. If I try to query the table XYZ and it isn't available, I get the same connection popup. How should I go about refreshing the table? Delete the link and recreate?
NEW UPDATE
Finally got around to try out the DSN-less pass through query proposed by Andre below. When I get to the 'execute' step I get an error saying I cannot execute a select query... but it is an update query. Here is the SQL string... .SQL = "UPDATE [Status] SET ItemDate = NOW() WHERE PlantID = '" & PlantID & "' AND ItemID = '" & ItemID & "'"
I suggest that instead of running an Access query on the linked table, you use a DSN-less Pass-Through query that you create on the fly.
This should either always work, or raise a trappable error.
Const ConnectString = "ODBC;DRIVER={MySQL ODBC 5.1 Driver};SERVER=your.server.com;PORT=3306;DATABASE=mydatabase;UID=myuserid;PWD=mypassword"
Dim qdf As DAO.QueryDef
Set qdf = CurrentDb.CreateQueryDef("")
With qdf
' Setting .Connect turns it into a Pass-Through query
.Connect = ConnectString
' Need to set this for non-SELECT queries
.ReturnsRecords = False
' Note: you need to use MySql syntax here, not Access SQL, especially the correct date format
.SQL = "UPDATE XYZ SET ABC = 'DEF' WHERE GHI = 'JKL'"
' or since MySql has a NOW() function too, just this:
.SQL = "UPDATE foo SET LastUpdatedDate = NOW() WHERE ItemID = 'xyz'"
.Execute
End With
You can also try a saved Pass-Through query, it might work as well. Then you would only need to supply the current .Sql, not the connect string.
Maybe you will get an helpful error if you execute your SQL command with the option dbFailOnError ? Like CurrentDB.Execute("Your SQL", dbFailOnError)
Before trying the UPDATE, do a simple SELECT. If it does not return a reasonable result, assume that the connection is down.

ALTER TABLE Syntax Error

I'm having trouble with an update query in Access.
I'm trying to do two things, add a field to a table, and change all values in another field to the same value.
ALTER TABLE 103 ADD COLUMN test TEXT;
UPDATE 103 SET [103].Workcenter = "103";
When I run these two lines independently, they work fine, but when I put them in the same query I get "Syntax Error in ALTER TABLE" statement. Does anyone know why I can't do this?
It would also be great if I could add a column and update all values in that field to a default value. I've tried DEFAULT in the ALTER TABLE command but it's not working either.
Thanks in advance for suggestions!
AS ron tornambe said, you can't have more than a single command in an Access Query. They do not support batching.
VBA code is your friend when doing alterations on tables: The Data Definition Language used in Access is more limited than what is available from VBA when directly manipulating the database objects.
For instance, to do exactly what you seek:
Public Sub AddFieldAndUpdate()
' Initialise '
Dim db As DAO.Database
Dim tb As DAO.TableDef
Dim fd As DAO.Field
Set db = CurrentDb()
' Get the 103 table '
Set tb = db.TableDefs("103")
' Create a new 'test' field, 128 char long '
Set fd = tb.CreateField("test", dbText, 128)
' Set the Default value for the new field '
fd.DefaultValue = "000"
' Add the new field to the 103 table
tb.Fields.Append fd
' Now do the update
db.Execute "UPDATE 103 SET [103].Workcenter = '103';", dbFailOnError
Debug.Print "Number of Updated records: " & db.RecordsAffected
' Cleanup
Set fd = Nothing
Set tb = Nothing
Set db = Nothing
End Sub
This is the jest of it, although you probably want to do more than that, for instance, set indexes, default formatting, etc as required.
Some table design features are only available when using the DAO object model to modify the TableDef. Others are only available when executing a DDL statement from an ADO connection.
Your table design change involves features which are available with either method. Use whichever you wish, but I would personally choose this way:
Dim strDdl As String
strDdl = "ALTER TABLE 103 ADD COLUMN test TEXT(128) DEFAULT ""000"";"
CurrentProject.Connection.Execute strDdl

Concatenate fields from one column in one table into a single, comma delimited value in another table

Any help that can be provided to a Access and VB noob would be greatly appreciated. What I'm trying to do is concatenate the values from one table and insert it as a comma delimited value into a field in another table. I'm trying to take all the server names that are say Linux boxes and concatenate them into a different field.
Table A looks like this
Machine Name | Zone | Operating System
----------------------------------------
Server01 Zone A Linux
Server02 Zone B Linux
Server03 Zone A Windows
Server04 Zone C Windows
Server05 Zone B Solaris
Table B has the field I want to insert into: Affected_Machine_Names.
Now, I've tried looking through the Concatenate/Coalesce posts, but the SQL view in Access doesn't like the Declare statements. My VB skills suck badly and I can't seem to get the code to work in VB for Applications. Unfortunately, I can't get this database converted into our SQL farm cause I don't have a server available at the moment to host it.
Can anyone point me in the right direction?
You can use Concatenate values from related records by Allen Browne for this. Copy the function code from that web page and paste it into a new standard module. Save the module and give the module a name different from the function name; modConcatRelated would work.
Then I think you should be able to use the function in a query even though you're not proficient with VBA.
First notice I changed the field names in TableA to replace spaces with underscores. With that change, this query ...
SELECT
sub.Operating_System,
ConcatRelated("Machine_Name", "TableA",
"Operating_System = '" & sub.Operating_System & "'") AS Machines
FROM [SELECT DISTINCT Operating_System FROM TableA]. AS sub;
... produces this result set:
Operating_System Machines
Linux Server01, Server02
Solaris Server05
Windows Server03, Server04
If you can't rename the fields as I did, use a separate query to select the distinct operating systems.
SELECT DISTINCT TableA.[Operating System]
FROM TableA;
Save that as qryDistinctOperatingSystems, then use it in this version of the main query:
SELECT
sub.[Operating System],
ConcatRelated("[Machine Name]", "TableA",
"[Operating System] = '" & sub.[Operating System] & "'") AS Machines
FROM qryDistinctOperatingSystems AS sub;
This is a fairly basic VBA function that will loop through every row in a column, and concatenate it to a comma-delimited result string. i.e., for your example, it will return "Server01, Server02, Server03, Server04, Server05". (Don't forget to replace the column and table names)
Function ConcatColumn(OS As String) As String
Dim rst As DAO.Recordset
Set rst = CurrentDb.OpenRecordset("Select * from TableA")
Dim result As String
'For every row after the first, add a comma and the field value:
While rst.EOF = False
If rst.Fields("Operating System") = OS Then _
result = result & ", " & rst.Fields("MyValue")
rst.MoveNext
Wend
'Clean it up a little and put out the result
If Left(result, 2) = ", " Then result = Right(result, Len(result) - 2)
Debug.Print result
ConcatColumn = result
End Function
To use this,
1. ConcatColumn("Windows") will return "Server04, Server03"
2. ConcatColumn("Linux") will return "Server01, Server02"
3. ConcatColumn("Solaris") will return "Server05"
4. ConcatColumn("") will return "".

Select ##Identity returning 0 for linked table in Access

I am using Access 2007 and have some linked tables to a mySQL database. I am using DAO to insert a record into a mySQL linked table and trying to retrieve the inserted PK using Select ##identity, but that select is returning 0.
Dim sql As String
Dim oDB As Database
Set oDB = CurrentDb
sql = "INSERT INTO Quotes ( CustomerID ) SELECT 1 AS Expr1;"
oDB.Execute sql
If oDB.RecordsAffected <> 1 Then
MsgBox "cannot create new quote"
Exit Function
End If
Dim rsNewID As DAO.Recordset
Set rsNewID = oDB.OpenRecordset("SELECT ##IDENTITY") ' Create a recordset and SELECT the new Identity
Dim intNewID As Long
intNewID = rsNewID(0).Value ' Store the value of the new identity in variable intNewID
'This value is 0, why?
I've seen another question like this, that has not been satisfactorily answered for me
SELECT LAST_INSERT_ID()
fredrik gets partial credit, for the mySQL statement. It's important to note that I am using DAO, so statements are processed by the JET engine and it does not support this statement. This needs to be run as a pass through query in Access in order to work though. After I made a pass through query with fredrik's select statement, that did the trick. I called this Access passthrough query from my DAO code and it worked.
I have not used mysql. So, translate what I say for mysql.
Is CustomerID an identity column (i.e. does it generate ID on its own)?
If so, use the function that returns the last generated ID.
##Identity function is what people use in SQL Server. I don''t know of equivalent in mysql.
See this
Looking at the code above, you need not open the 2nd recordset. The rsNewID1 should help you get the last inserted ID.
Hope this helps.