We are developing in Oracle ERP environment. There a quite a few view legacy MDB databases (dozens), that are connceted to Oracle views (hundreds of dependencies). In Oracle there are a custom 800+ views, that are subject to be reworked, set to deprecated and possibly will be deleted in the future.
I can see the connected Oracle DB in MDB-Design view, but I need to write those dependencies into a list. With such a list I could do software maintenance job sketched above.
I have a ADOX-based Metadata-Reader, but this does not list the oracle tables:
Public Sub ADOX_Oracle_Metadata()
'To reference ADO from Microsoft Access
'In Microsoft Access, select or create a module from the Modules tab in the Database window.
'On the Tools menu, select References....
'Verify that at least the following libraries are selected:
'
'Microsoft ActiveX Data Objects x.x Library
'ADO Ext. 2.7 for DDL and Security (ADOX)
'
Dim cn As ADODB.Connection
Dim ct As ADOX.Catalog
Dim tb As ADOX.Table
Dim strDB As String
Dim ws As Worksheet
Set cn = New ADODB.Connection
Set ct = New ADOX.Catalog
strDB = "L:\Applikationen\Access\DepreciationOutputMail.mdb"
cn.ConnectionString = _
"Provider=Microsoft.ACE.OLEDB.12.0;" & _
"Data Source=" & strDB & ";"
cn.Open
Set ct.ActiveConnection = cn
For Each tb In ct.Tables
' Tables can be of type TABLE, ACCESS TABLE, SYSTEM TABLE or VIEW
Debug.Print tb.Type & " " & tb.Name
Next tb
cn.Close
Set ct = Nothing
Set cn = Nothing
End Sub
Anyhow this does not list the connected oracle tables.
Maybe I have just to change the connection string? How do I know the correct connection string? Can I read it somewhere in the computer that runs the MDB?
Can you provide a solution?
This is a screenshot of a sample situation:
The tables I need to list are marked in green.
regards, LPNO
Addon information
on request of Erik, here an extract of relevant columns of MSYSOBJECTS table, created with
SELECT MSysObjects.Connect, MSysObjects.ForeignName, MSysObjects.Name, MSysObjects.Type INTO Extract_MSYSOBJECTS
FROM MSysObjects
WHERE (((MSysObjects.Connect) Is Not Null));
Actually column NAME already lists the information I looked for. Anyhow a VBA-coding approach would still be appreciated, as there are numerous mdb databases to be checked about this.
Don't use ADOX for this, but use DAO instead.
DAO is more native to Access, and can work with linked tables more easily.
Dim db As DAO.Database
Dim td As DAO.TableDef
Set db = DBEngine.OpenDatabase("L:\Applikationen\Access\DepreciationOutputMail.mdb")
For Each td In db.TableDefs
Debug.Print td.Name; td.SourceTableName, td.Connect
Next
Do note that Access can also connect to tables/views via queries or directly from code, these wouldn't be listed. You can iterate querydefs to find the queries, but for code it would be substantially more complex.
Alternate approach using the MSysObjects table:
Dim db As DAO.Database
Set db = DBEngine.OpenDatabase("L:\Applikationen\Access\DepreciationOutputMail.mdb")
Dim rs As DAO.Recordset
Set rs = db.OpenRecordset("SELECT * FROM MSysObjects WHERE Type = 4")
Do While Not rs.EOF
Debug.Print rs!Name; rs!ForeignName
rs.MoveNext
Loop
searching around I found out that this MDB-Query does exactly what I asekd for:
SELECT MSysObjects.Name, MSysObjects.Type, MSysObjects.Flags
FROM MSysObjects
WHERE (((MSysObjects.Type)=6) AND ((MSysObjects.Flags)=2097152)) OR (((MSysObjects.Type)=1) AND ((MSysObjects.Flags)=0))
ORDER BY MSysObjects.Flags;
Related
How to load all fields name into combo box. I am using visual basic 6.0.
My code is like this.
Private Sub combo2_option()
Call Dbase
Set rs = New ADODB.Recordset
rs.Open "select ID,DATE,REFNO,SUPPLIER,MODEL,SERIAL,DESCRIPTION,UOM,CATEGORY,PUH,GDP from product_aging_monitoring ", db, 3, 3
Combo2.AddItem rs(2)
End Sub
Option 1. You can limit the records to 1 if you wish to get only the field names.
For i=0 to rs.fields.count-1
Combo2.AddItem rs.fields(i).Name
Next
Option 2 is to use ADOX (DAO) Reference instead of writing a select query. Use ADOX.Catelog object to get metadata of the tables and database
Background: a LOB app we use has the ability to use macros written in VBScript, but no access to WScript, etc., as far as I know.
I have successfully received user input, passed it to a stored procedure on a SQL Server, and returned a recordset to the VBScript macro in the application.
What I want to do now, is write a function or loop or something, that for as long as there is a record left in the recordset, accept additional user input, and check this against the returned recordset.
The recordset returned from SQL Server contains two columns: PART_ID and PART_QTY. For as many number of entries there are, I want to accept additional user input, lets say PART_ID_INPUT and PART_QTY_INPUT, and validate it against the in-memory recordset.
My biggest problem is working with the disconnected recordset.
When in doubt, read the documentation. You can use the Filter and RecordCount properties to determine if the recordset contains matching records:
part_id_input = InputBox("Enter part ID:")
If part_id_input <> "" Then
rs.Filter = "PART_ID = '" & part_id_input & "'"
If rs.RecordCount > 0 Then WScript.Echo "Found matching record."
End If
The filter is cleared by setting it to an empty string:
rs.Filter = ""
The current record can be removed from the recordset using the Delete method:
rs.Delete
Navigate through records via MoveFirst/MoveLast/MoveNext/MovePrevious.
A client have a long lived IBM Notes application where someone along the line changed the type of a field from number to text.
So, now when we're trying to do an FT search like: [myField] = "1234" receive the error message: "Query is not understandable".
If I do: [myField] = 1234 it works but won't return any hits. Even though there's a document where myField = "1234".
The field is of type text in the design.
I've created a new view for testing, only allowing documents from one form.
Deleted the full text index (even on the file system)
updall -X
Fixup -r
Created full text index
In my test view I've got one column that shows if the field content being searched is of type text #IsText(myField) and all rows shows: 1 (so it's field content must be text)
None of the above worked so I created a new database copy locally.
Same problem.
Created an entirely new database (for testing only), form, view and full text index and that works.
Feels like the existing database design somewhere stores the old field type...
Any ideas appreciated.
Thanks!
/J
Datatypes and field names are stored in the UNK table. There is just one entry per field name, so it's critical not to use the same field name more than once in an application with different datatypes.
You need to rebuild the UNK table, as I blogged here http://www.intec.co.uk/full-text-search-musings/
Note, it must be an offline compact, as Duffbert says here http://www.duffbert.com/duffbert/blog.nsf/d6plinks/TDUF-5SMHV4. If anyone is in the database when you do the compact, it will fail and the UNK table will not be rebuilt.
Links are useful, but if you don't want to remove data from documents - for me such steps worked (and there was no need in removing fields from forms in designer):
Run from designer with manager access with such code inside
Sub Initialize
Dim s As New NotesSession
Dim db As NotesDatabase
Dim dc As NotesDocumentCollection
Dim doc As NotesDocument
Dim i As Integer
Dim nc As NotesNoteCollection
Dim noteid As String
Dim nextnoteid As string
Dim itemArr As Variant
Dim NeedSave As Boolean
Const ITEM_NAME = "itemName1|itemName2"
itemArr = Split( ITEM_NAME, "|" )
'погромист-кун не должен забывать про наличие итемов в формах...
Set db = s.Currentdatabase
Set nc = db.CreateNoteCollection(False)
nc.SelectForms = true
Call nc.BuildCollection
noteid = nc.Getfirstnoteid()
For i = 1 To nc.Count
Set doc = db.Getdocumentbyid( noteid )
noteid = nc.Getnextnoteid( noteid )
NeedSave = false
ForAll IA In itemArr
If doc.Hasitem( IA ) Then
Call doc.Removeitem( IA )
NeedSave = true
End If
End ForAll
If NeedSave Then
Call doc.Save( True, False )
End If
Print CStr( i ) & "\" & CStr( nc.Count )
Next
End Sub
Remove database index
Run from administrator command lo compact database.nsf -c , like mentioned in links above
Create index
I'm trying to insert a lot of records into a Jet (Access) database via ADO.NET / OleDb. It is running painfully slowly. The slowness is mainly due to the DbDataAdapter.Update method. In Microsoft's words,
...these statements are not performed as a batch process; each row is updated individually.
This is a real WTF as my database application gives about 30 times slower performance than the equivalent code in VB6 using old ADO or DAO (a Recordset with an AddNew / Update loop).
The SqlClient provider has the SqlBulkCopy class; is there anything equivalent for OleDb?
Even being able to change its write-caching behaviour might help. (ie. don't flush the cache after each row is inserted!)
Is there anything I can do, or is ADO.NET just broken for Jet?
*Edited to add: Here's a cut down version of my code, using a cut down test database.
First, the VBA/ADO version (Access 2003):
Dim con As ADODB.Connection
Set con = CurrentProject.Connection
con.Execute "DELETE * FROM tblTest", , adCmdText Or adExecuteNoRecords
Dim rs As ADODB.Recordset
Set rs = New ADODB.Recordset
rs.Open "tblTest", con, , adLockPessimistic
Dim i&, t!
t = Timer
For i = 1 To 10000
rs.AddNew
rs!mainKey = i
rs!testColumn = Rnd * 100
rs.Update
Next
rs.Close
Debug.Print "time to add 10000 (by ADO) " & Timer - t
Output: time to add 10000 (by ADO) 0.296875
Now the ADO.NET version (VB.NET 2010):
Dim sProvider As String = "PROVIDER = Microsoft.Jet.OLEDB.4.0;"
Dim sDataSource As String = "Data Source = 'C:\test.mdb'"
Dim connection As New OleDbConnection(sProvider & sDataSource)
connection.Open()
Dim q As New OleDbCommand("DELETE * FROM tblTest", connection)
q.ExecuteNonQuery()
Dim ds As New DataSet
Dim selectCmd As OleDbCommand = connection.CreateCommand()
selectCmd.CommandText = "SELECT * FROM tblTest"
Dim da As New OleDbDataAdapter(selectCmd)
da.Fill(ds, "tblTest")
Dim theTable As DataTable = ds.Tables("tblTest")
For i As Integer = 1 To 10000
Dim row = theTable.NewRow()
row.Item("mainKey") = i
row.Item("testColumn") = Rnd() * 100
theTable.Rows.Add(row)
Next i
Dim t! : t = Microsoft.VisualBasic.Timer
Dim cb As New OleDbCommandBuilder(da)
da.Update(ds, "tblTest")
Debug.Print("time to add 10000 (by ADO.NET): " & Microsoft.VisualBasic.Timer - t)
connection.Close()
Output: time to add 10000 (by ADO.NET): 5.859375
Make sure that the connection is open when the method is called. If the connection is closed before the update method is called (I actually saw that in some example code) the update method may attempt to open the connection in a non-optimal way.
Opening connections in Jet can be very slow if the connection is not pooled. You may need to add OLE DB SERVICES = -1 to make sure the connection is pooled.
Slow MSAccess disk writing
Are you really trying to populate a table with random values? If so, there are faster ways to do it (using an INSERT based on an existing table, or on the table you're appending to, so you can run it multiple times and quickly reach the number of desired records).
In general, a SQL INSERT is going to be an order of magnitude faster than adding one record at a time. If you have to do it the way you're doing it, then you might look into whether or not you can use a Jet/ACE transaction via ADO/ADO.NET. I haven't a clue whether that's available or not. If it's not, and assuming COM is an option, you should consider just using DAO so you could use Jet/ACE transactions, which will delay the write to the very end (like posting a batch).
I'm no ADO maven, but I recally there being some batch functions in Classic ADO, too, so you might consider investigating that, as well.
How can I use Linq with Dataset.xsd files?
I've looked at Linq-to-Datasets and Linq-to-XSD but they don't really seem to work directly with the Visual Studio DataSet.xsd files.
EDIT: I actually found a great link for this: link text but I can't seem to figure out what to do if I want to query on related tables.
Dim taFields As New TestDSTableAdapters.FieldTableAdapter()
Dim fields = taFields.GetData()
Dim results = From f In fields Select f
For Each field In results
Response.Write(field.FieldID & " " & field.Label & " " & field.FieldTypeRow.FieldTypeLabel)
Next
I get an "Object reference not set to an instance of an object." error when running the above code because, I guess, field.FieldTypeRow.FieldTypeLabel isn't actually part of the query of data. Do I have to create a method that returns data from that data also? I'm assuming this would be the case (that taFields.GetData has to return all the FieldType data results too if I'm going to reference it - in Linq to SQL it does all this for me so it's a little disappointing)
A DataSet is just a container for your data, so you need to fill it first. LINQ to SQL will create SQL and go to the database for you...but when you're working with DataSets, you're using LINQ to Objects, which won't create SQL. So you need to make sure that all tables you need in the DataSet are filled before you start writing LINQ to query your DataSet.
I think you're looking for something along these lines:
Dim taFields As New TestDSTableAdapters.FieldTableAdapter()
Dim taFieldTypes As New TestDSTableAdapters.FieldTypesTableAdapter()
Dim ds As New TestDS
taFields.Fill(ds.Fields)
taFieldTypes.Fill(ds.FieldTypes)
Dim results = From f In ds.Fields Select f
For Each field In results
Response.Write( _
field.FieldID & " " & field.Label & " " &
field.FieldTypeRow.FieldTypeLabel)
Next
Dim taFields As New TestDSTableAdapters.FieldTableAdapter()
Dim fields as TestDSTableAdapters.FieldsDataTable = taFields.GetData()
Dim results = From f In fields Select f
For Each field In results
Response.Write(field.FieldID & " " & field.Label & " " & field.FieldTypeRow.FieldTypeLabel)
Next
You forgot to set the type for fields. That is why yo uwere getting object reference problems. You do not have to create a new blank dataset.