I have a database with a few multi-valued lookup fields. When i split my database, there is a repeated error that the junction table is not found. I know Access makes shadow tables when you use the lookup wizard. How do i link these tables?
I tried the following code:
Sub refresh()
Dim db As Database
Dim rs As Recordset
Set db = CurrentDb
Set rs = db.OpenRecordset("SELECT [Name] FROM [MSysObjects] WHERE ([Type] = 6);", dbOpenSnapshot, dbForwardOnly)
Do While (Not rs.EOF)
db.TableDefs.Delete rs.Fields("Name").Value
rs.MoveNext
Loop
rs.Close
Set rs = Nothing
db.Close
Set db = Nothing
End Sub
but when I ran it it still gave me the same error message, that the hidden junction table "in this case called "TblAudienceTblProg"" was not found.
Is there any way to get around this or do I have to restructure the whole back end to include the actual junction tables?
I think the multivalued datatype is only really useful when the backend is going to be in SharePoint or you do not plan to split a local database.
Basically what a multivalued field type is is a many to many relationship without the hassle of creating a bridge table yourself.
Please click here for more information
Related
I have this code that lists all indexes, but I only need those where Indexed: "Yes, duplicates OK" is set. Is there any way to do that instead of manually look through all indexes? I need to migrate the data to SQL Server, but with this I only get empty tables in SQL Server.
Const adSchemaIndexes As Long = 12
Dim cn As Object ' ADODB.Connection
Dim rs As Object ' ADODB.Recordset
Dim i As Long
Set cn = CurrentProject.Connection
Set rs = cn.OpenSchema(adSchemaIndexes)
With rs
' enable next three lines to view all the recordset column names
' For i = 0 To (.Fields.Count - 1)
' Debug.Print .Fields(i).Name
' Next i
Do While Not .EOF
Debug.Print !TABLE_NAME, !INDEX_NAME, !PRIMARY_KEY
.MoveNext
Loop
.Close
End With
Set rs = Nothing
Set cn = Nothing
As written, that procedure displays only 3 of the recordset's 25 columns. If you follow the comment instruction to "enable next three lines", you can view the names of all the available columns. One column in particular (UNIQUE) should be useful for your purpose. When you choose "Yes (Duplicates OK)" for the "Indexed" property in table design, it will be displayed as False in the UNIQUE column of the schema recordset.
I assumed you're not interested in information for indexes on system tables ("MSys*" table names). And, for the non-system table indexes, only present information for those without a unique constraint. Here is how I modified the Do While loop accordingly:
Do While Not .EOF
If !Unique = False And Not !TABLE_NAME Like "MSys*" Then
Debug.Print !TABLE_NAME, !INDEX_NAME, !COLUMN_NAME, !Unique
End If
.MoveNext
Loop
Here is the output from the revised procedure in my test database:
DiscardMe compound_4_5 f4 False
DiscardMe compound_4_5 f5 False
DiscardMe f3 f3 False
That table has 2 non-unique indexes. One of them is a compound index, based on 2 fields. So the schema recordset includes separate rows for each of those fields. The other index is based on a single field, so is presented as a single row in the recordset.
I think that gives you what your question asks for. But I have no idea how those indexes would interfere with migrating your table data to SQL Server. Good luck.
I have a problem updating views with ADO in MS Access.
I created views for my tables, they contain columns and rows that are eligable to be changed. For example, the table tbl_tablename will have the view: vw_tablename_editable with some WHERE clauses to determine wether the the rows should be editable or not.
CREATE VIEW vw_tablename_editable
AS
SELECT tablename_id, tablename_fieldtoupdate
FROM dbo.tbl_tablename
WHERE table_criteriafield > 1000 -- 1000 being some criteria
The permissions work: when I run UPDATE queries on the view as a user with only UPDATE permissions on the view, the information is correctly updated. Also when using DAO linked tables in Access, I can view and modify the data.
The problem is when I am using ADO and try to update the views, both using the Bound controls and manual updating in VBA.
Dim rs as ADODB.Recordset
Set rs = New ADODB.Recordset
With rs
Set .ActiveConnection = connectionstr
.source = "SELECT tablename_id, tablename_fieldtoupdate FROM vw_tablename_editable " & _
"WHERE tablename_id = " & id_ofrecordtobeupdated
.cursortype = adOpenStatic
.cursorlocation = adUseClient
.locktype = adLockOptimistic
.Open
End With
rs!tablename_fieldtoupdate = "new value"
rs.update
The error message I am getting is:
The UPDATE permission was denied on the object 'tbl_tablename', database 'mydatabase', schema 'dbo'
It seems to be related with this question, however, the SELECT statements in ADO just fine without giving the user access to the underlying tables.
The problem is solved when I give the users direct UPDATE access to the tables or the DBO schema, but I would like to avoid this. When logged in as DBO updating works both with the MS Access bound controls and the manual VBA way.
I also tried to make an indexed view, but this also does not seem to make a difference.
Edit
By suggestion of user #wqw inserting WITH VIEW_METADATA, it now works perfectly!
CREATE VIEW vw_tablename_editable
WITH VIEW_METADATA
AS
SELECT tablename_id, tablename_fieldtoupdate
FROM dbo.tbl_tablename
WHERE table_criteriafield > 1000 -- 1000 being some criteria
You just need to create your views using WITH VIEW_METADATA option which is generally harmless for anything T-SQL related (e.g. queries or stored procedures) but instructs OLEDB/ADO (and other data-access connectors like ODBC) to update through view's columns and not the (several) base tables.
Ownership chains will suppress permissions checks on tables when any DML is executed on a view owned by the same user. Apparently ADO is discovering the underlying table and accessing it directly.
I'm a self taught Excel VBA and SQL user. I'm testing out some simple queries before I add complexity. I must be missing something blindingly obvious here...
I am using an ADO connection to run a SQL SELECT statement on a table in the activeworkbook (ThisWorkBook). The Excel Table is named "tbl_QDB" and is on worksheet "MyQDB". The table starts on cell A1, so there are no blank or populated cells above the Table HeaderRowRange.
I have set up an ADO connection to ThisWorkBook and this is working fine. Here's the code:
Sub ConnectionOpen2()
'### UNDER DEVELOPMENT
Dim sconnect As String
Const adUseClient = 3
Const adUseServer = 2
Const adLockOptimistic = 3
Const adOpenKeyset = 1
Const adOpenDynamic = 2
'used to connect to this workbook for SQL runs
On Error GoTo err_OpenConnection2
Set cn2 = CreateObject("ADODB.Connection")
Set rec2 = CreateObject("ADODB.Recordset")
rec2.CursorLocation = adUseClient
rec2.CursorType = adOpenStatic
rec2.LockType = adLockOptimistic
datasource = ThisWorkbook.FullName
sconnect = "Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & datasource & ";" & _
"Extended Properties=""Excel 12.0;HDR=YES;ReadOnly=False;Imex=0"";"
cn2.Open sconnect
'etc, etc...
End Sub
I can run this simplest basic SELECT query:
SQLSTR="SELECT * FROM [MYQDB$]"
rec2.open SQLSTR, cn2
This works and produces 10 records i.e. rec2.recordcount=10.
However, if I try this, it errors:
SQLSTR="SELECT QID_1 FROM [MYQDB$]"
QID_1 is a valid field in the table on worksheet "MyQDB".
It doesn't change the error if I enclose QID_1 in () or [] or ``
I can even replace the field name with a made up field e.g. DonaldDuck and I get the same error.
Why would the SELECT statement work if I use "*" but not if I use any of the field names in the table? This seems so basic that I feel I must have missed a simple but key point.
I really will appreciate if someone can point out the mistake!
The SQL should work - if the field exists. Execute the Select * and dump the field list:
For i = 0 To rec2.Fields.Count - 1
Debug.Print rec2.Fields(i).Name
Next i
Thank you all for your comments.
That suggestion #FunThomas was an eye opener! The results were F1, F2, F3 etc, so the field names (or column names if you prefer) were not being recognised.
This would explain why, after days of trying to join this table with another in a closed, external workbook, it was not working. SQL error messages can be quite obtuse and were not saying it didn't recognise the field name.
I have now fixed that issue. Here's what I can tell / warn others:
I started this table with rows above the header. In 2 of those cells
above I recorded the last connection time and status to another
workbook table. I realised before that these extra rows, with data
populated in ANY cell above the headers, were causing problems with
SQL. Despite having my data in an Excel Table, the SQL "engine" for
Excel looks at the sheet, i.e. [MYQDB$] where the data is stored
(although I am aware that you can specify a sheet and range, but
cannot use the actual table name as the range).
It is ok to have blank rows above the table headerrowrange. So, I
deleted the cells containing the data above the table
headerrowrange. Instead, I placed a Text Box and used a formula to
look at another sheet where the last connection time and status were
now stored to supply the text for the text box.
I can now see that even this Text Box, which occupies no cell, causes a problem for Excel SQL.
Before posting my question here, I made a copy of the workbook and removed the text box and the rows above the table headerrowrange. I still got errors. I still got F1, F2, F3 etc as field names (per #FunThomas's suggestion).
Only after deleting these rows and the text box and then resizing the table (actually, the same range as before) did the Excel SQL recognise the proper field names. I was then even able (just for curiosity) to insert a blank row above the table headerrowrange, and the SQL still worked.
It seems to me that Excel retained in memory the old table definition and only by removing all data above the table headerrowrange and then resizing the table did it refresh that. Perhaps I should be less lazy in future and call the sheetname and range (table address) in the sql: maybe that would ignore data in cells above the headerrowrange?
#PanagiotisKanavos: I was originally trying to compare two tables (actual Excel Tables, not just ranges, hence they have Field Names), one in ThisWorkBook and another in a closed Excel workbook. SQL is the best way to do this. Having failed to get a left join to work between these tables (and this Question might now have revealed why that wouldn't work!) I decided to bring the data from the external workbook into ThisWorkBook and compare there. Then I was going to find the differences, store in a recordset (hence SQL) and then INSERT INTO the external workbook.
Thanks for your help guys!
Yesterday I had to run a query in MS Access 2010. One field I needed was not in the tables I usually use (already linked through the ODBC Database) and I didn't know what table it was a part (there are several hundred tables in the Machine Data Sources). Aside from manually importing all the tables and looking in each one for this field is there a way I can search for a field without knowing the table either 1. without importing any tables from the ODBC Databases, or if not 2. importing a handful of possible tables and searching once those tables have been linked into my active MS Access 2010 session?
Install Access Dependency Checker, link all tables and search for column name (enable checkbox for search in linked tables)
You could do this in a Function using ADO schema's.
Try this function in a standard module:
Function ListTablesContainingField(SelectFieldName) As String
Dim cn As New ADODB.Connection
Dim rs As ADODB.Recordset
Dim strTempList As String
Set cn = CurrentProject.Connection
'Get names of all tables that have a column called <SelectFieldName>
Set rs = cn.OpenSchema(adSchemaColumns, _
Array(Empty, Empty, Empty, SelectFieldName))
'List the tables that have been selected
While Not rs.EOF
'Exclude MS system tables
If Left(rs!Table_Name, 4) <> "MSys" Then
strTempList = strTempList & "," & rs!Table_Name
End If
rs.MoveNext
Wend
ListTablesContainingField = Mid(strTempList, 2)
rs.Close
Set cn = Nothing
End Function
I have just learned about the pass through queries in MS-ACCESS.
I have a SQL SERVER backend and
if I'm right, for a query access loads all records before to do the where clause... so what would be the point of having a SQL SERVER backend?
That's why I want to try using pass through queries as much as possible but is there a way I can get the connection string from my linked tables for my pass through queries?
I tried CurrentDb.TableDefs("One of my table name").Connect in the ODBC Connect Str property but I got the error saying it's an invalid connection string.
It would be nice because I know I will have to change the connection soon so I wouldn't have to edit the connection string at many places.
Thank you.
I'm not sure what you meant here: "for a query access loads all records before to do the where clause"
If the WHERE clause can be applied at the server, ODBC will translate it to the server's language, and only the matching rows will be sent back to Access:
WHERE date_field >= #2011-01-01# AND date_field < #2012-01-01#
That WHERE clause would limit the rows sent to Access to only those whose date_field values are from 2011.
However, if a WHERE clause includes functions which must be evaluated by Access, ODBC must retrieve all candidate rows and hand them over to the Access db engine so it can perform the evaluation.
WHERE Format(date_field, 'yyyy') = '2011'
But for your actual question ... connection string for pass through queries ... consider the following code example. I have an ODBC link named dbo_foo whose source table in SQL Server is [dbo].[foo]. So I can grab the .Connect property from dbo_foo and use it for the .Connect property of a pass through query based on the same server table.
Public Sub CreatePassThruQuery()
Dim db As DAO.Database
Dim qdf As DAO.QueryDef
Dim strConnect As String
Set db = CurrentDb
strConnect = db.TableDefs("dbo_foo").Connect
Set qdf = db.CreateQueryDef("qryDbo_Foo")
qdf.Connect = strConnect
qdf.SQL = "SELECT * FROM [dbo].[foo];"
qdf.Close
Set qdf = Nothing
Set db = Nothing
End Sub
Still when you change the .Connect property of the table, you will also need to do it for the query. If you have many of them and/or change the connections frequently, it may be worth the effort to create a VBA procedure to update them. Alternatively, you might use a DSN for the .Connect property of the table and matching query. Then revise the DSN as needed. A pitfall with that approach is that, if other people will be using your application, you would need to manage the DSNs on multiple machines.