How can we do Multiple Operations using Single RecordSet? - vb.net

I have some task regarding do operations on database using ADODB in Vb.NET. Can any help me regarding using the same record set multiple times?
I have tried on this topic just by closing the recordset after performing First operation and doing second operation e.t.c.
Example:
Dim rs As New ADODB.Recordset()
OpenConnection()
rs.Open("table1")
//Some Operations
//Now using same recordSet for Second Operation
rs.Close()
rs.Open("table2")
In ADO.NET we have "MultipleActiveResultSets=True" in ConnectionString for SqlDataReader.Do we have any property like this in ADODB?

The more similar thing that exists in ADODB is sending multiple queries in your Sql String then proccessing them one by one using rs.NextRecordset (rs is the Recordset), here is an example: ADO NextRecordset Method Demonstration
But, personally, i prefer doing it as you're doing it now, using just one recordset for each query. Take into account that using multiple recordsets inside one, like in the previous example may require some additional commands in some dbs to ensure no additional unwanted recordsets are created from messages coming from the backend, for example for Sql Server it's a good idea using:
Set NoCount On and Set ANSI_Warnings OFF

Related

Getting the number of affected records by an action query in VBA

I'm wondering if it is possible to pick a variable which is being used by MS Access when inserting a specific amount of new data rows to a table which happens during a VBA runtime.
Screenshot of the MS Access Notification (in German):
MS Access notifies me here in that case that in the middle of a running VBA script 2327 new data rows are being added to the table. I'm not a programmer but I feel that this variable must be stored somewhere (at least temporary).
Does anyone has an idea how to pick/access this number from the window when it appears and then store it into an VBA Integer for further use in the future?
Update:
Basically, I have a main database which has to be updated every day by an import file.
In the first step there is a check for already existing datasets which will therefore only updated by an UPDATE-Query.
UPDATE ReiseMaster
INNER JOIN Update_Import
ON(ReiseMaster.Col1 = Update_Import.Col1)
SET ReiseMaster.Col2 = Update_Import.Col2,
ReiseMaster.Col3 = Update_Import.Col3;
Then, in the second step new datasets which are not existing in the main database will be inserted into it will all the information they contain. This is the SQL-Query which is responsible for appending new data rows during the VBA procedure:
INSERT INTO ReiseMaster ([Col1],[Col2],[Col3])
SELECT [Col1],[Col2],[Col3] FROM Update_Import
WHERE NOT EXISTS(SELECT 1 FROM ReiseMaster
WHERE Update_Import.[Col1] = ReiseMaster.[Col1]);
I am struggling with identifying the amount of new datasets after the procedure, which is in fact already somehow determined by MS Access itself (see Pop-Up). So my idea was to just use the number which is already determined by MS Access.
All SQL-Queries are stored in a string and run by the "DoCmd.RunSQL" command.
Using DAO, it's really easy to get the number of records affected after executing a query.
You can just use the Database.RecordsAffected property:
Dim db As DAO.Database
Set db = CurrentDb 'Required, don't use CurrentDb.Execute else this won't work
Dim strSQL as String
strSQL = "UPDATE ReiseMaster INNER JOIN Update_Import" 'and the rest of your query
db.Execute strSQL, dbFailOnError 'Execute the query using Database.Execute
Dim recordsAffected As Long
recordsAffected = db.RecordsAffected 'Get the number of records affected
However, this won't allow you to see it before committing the query. To see it and display a prompt, you can use transactions (short sample here), and rollback the transaction if the user doesn't choose to commit.

Using filtered records from a form in a query

I have a large table. I am making a form that displays the table, and allows the user to use Access' standard filters on many different fields to choose only some of the data. Then, after the user has used the filters to choose only some of the data, I want to be able to include only the resulting recordset (and not the entire original table) in various queries.
I'm not sure if there's a way I can directly reference the form's underlying recordset in a query.
The obvious thing would be to use vba to iterate through the recordset, and either fill a temporary table with it that would be used in the queries, or directly do what I want with the data without using sql. But I believe iterating through a recordset is considerably slower than a query. Is there some way that I can reference a form's recordset directly from within a query, or dump the entire contents of the recordset into a temporary table?
Using Form.RecordsetClone will copy the subform's recordset and include any filters applied through the Access' built-in filters.
Note: the Form. is standard and does not refer to your form. So your reference would need to be Me!SubformName!Form.RecordsetClone if triggered from the main form. MSDN
After you create a clone, it seems you can use sql to drop the recordset into a temp table. But as #Andre mentioned, adding the PK only would be ideal to reduce load in large recordsets. The SQL string creation I found is simply:
sql = "INSERT INTO TEMPTable VALUES" & rs.Fields("YourPKFieldNameFromSUBFORM")
Assuming sql and rs have been declared as a string and your cloned recordset respectively, and TEMPTable has already been created with one column for your PK. Source: #5 and MSDN However, you will still need to loop through each record of the clone, as the sql statement only outputs a single row at a time.
As a supplement to Christopher D.'s answer, here is the final code I ended up using:
CurrentDb.Execute "DELETE FROM FilteredJobs"
Dim rs As Recordset
Set rs = Search_subform.Form.RecordsetClone
rs.MoveFirst
While Not rs.EOF
CurrentDb.Execute "INSERT INTO FilteredJobs VALUES (" & rs.Fields("JobID") & ")"
rs.MoveNext
Wend

What is the difference between these statements, and which do most prefer?

I have noticed that a lot of code I have found on the internet will have variables defined and set that I would simply avoid...such as:
Dim db as Database
Dim rs as Recordset
Set db = CurrentDB
Set rs = db.OpenRecordset
I would write this as:
Dim rs as Recordset
Set rs = CurrentDB.OpenRecordset
I save 2 lines of code at the top plus one to Set db = Nothing at the end. Which, over hundreds of subs and functions can really add up...
But is one more preferred over another by most coders? Is there an actual reason to use one or the other? Is there an advantage to actually defining and spelling out the whole thing?? Is there any real savings in doing it my way, other than staving off carpel tunnel for a few more minutes?
In terms of execution, there is no real difference between the two VBA methods. However, the first is a more generic version as the db object can be set to either the local DAO database or an external DAO database using the Workspace.OpenDatabase method. The second is a more shortcut version as usually needed recordsets, querydefs, etc. derive from the working, local database. Below are examples of referencing external databases.
Short cut version:
Set db = OpenDatabase ("C:\Path\ToExternalDB\someotherdb.accdb")
Full generic version:
Set db = DBEngine.Workspaces(0).OpenDatabase("C:\Path\ToExternalDB\someotherdb.accdb")
Therefore, should a developer change database environments (i.e., transition from local to external database source) and even workspaces, they can easily change the set db = ... line instead of adding this line to the code if developer had originally used only the CurrentDb namespace.
This is a good example of the trade-off decision between efficiency (less scripted lines) and scalability (translatable lines).
"The CurrentDb method creates another instance of the current database... The CurrentDb method enables you to create more than one variable of type Database that refers to the current database" from https://msdn.microsoft.com/en-us/library/office/aa221178%28v=office.11%29.aspx?f=255&MSPPError=-2147217396.
So if you use CurrentDB just once in your code than its fine not to declare it, however using it multiple times it won't be the same instance but always created a new one which may create strange bugs for you.

How does "set rs = conn.execute(sql)" actually work?

I am at the moment trying to improve the performance in my ASP Classic application, and I am at the point of improving SQL transactions.
I have been reading a little from http://www.somacon.com/p236.php
So, my question is:
How does the line set rs = conn.execute(sql) actually work?
I believe that the line defines a variable named rs and binding ALL the data collected from the database through the SQL sentence (fx select * from users).
Then afterwards I can actually throw the database-connection to hell and redefine my sql variable if I please, is this true?
If that is true, will I then get the best performance by executing my code like this:
set conn = server.createobject("adodb.connection")
dsn = "Provider = sqloledb; Data Source = XXX; Initial Catalog = XXX; User Id = XXX; Password = XXX"
conn.open dsn
sql = "select id, name from users"
set rs = conn.execute(sql)
conn.close
-- Do whatever i want with the variable rs
conn.open dsn
sql = "select id from logins"
set rs = conn.execute(sql)
conn.close
-- Do whatever i want with the variable rs
conn.open dsn
sql = "select id, headline from articles"
set rs = conn.execute(sql)
conn.close
-- Do whatever i want with the variable rs
set conn = nothing
In this example i open and close the connection each time i do a sql transaction.
Is this a good idea?
Is this a good idea?
No but not for the reasons indicated by Luke. The reality is that ADODB will cache connections anyway so opening and closing connections isn't all that expensive after all. However the question proceeds from the mis-information you appear to have over the behaviour of a recordset...
From you comment on Lukes answer:-
But it is correct, that it stores all the data in the variable when executed?
Not unless you have carefully configured the recordset return to be a static client-side cursor and even then you would have to ensure that the cursor is completely filled. Only then could you disconnect the recordset from the connection and yet continue to use the data in the recordset.
By default a SQL server connection will deliver a simple "fire-hose" rowset (this isn't even really a cursor) the data is delivered raw from the query, only a small amount of buffering occurs of incoming records and you can't navigate backwards.
The most efficient way to minimise the amount of time you need the connection is to use the ADODB Recordset's GetRows method. This will suck all the rows into a 2-dimensional array of variants. Having got this array you can dispense with the recordset and connection.
Much is still made of minimising the number of connections maintained on a server but in reality on modern hardware that is not a real issue of the majority of apps. The real problem is the amount of time an on going query is maintaining locks in the DB. By consuming and closing a recordset quickly you minimise the time locks are held.
A word of caution though. The tradeoff is an increased demand for memory on the web server. You need to be careful you aren't just shifting one bottleneck to another. That said there are plenty of things you can do about that. Use a 64Bit O/S and stuff plenty of memory in it or scale out the web servers into a farm.
Nope, opening and closing connections is costly. Open it, reuse the recordset like you are, then close it.

Running multiple SQL statements in the one operation

I'm trying to use ADO to create several tables at once, into MS Access. Is it possible to do multiple statements in the one operation? For instance:
...
// I have omitted the field details
CString sQuery = "CREATE TABLE [Table1] (..., PRIMARY KEY ([ID])); \nCREATE TABLE [Table2] (..., PRIMARY KEY ([ID]));";
oRecordset.Open(oDatabase.m_pConnection, sQuery)
This fails due to a "Syntax Error in CREATE TABLE statement", although each of the create statements work on their own perfectly. Is there a way of doing this sort of thing? There will also be statements to add constraints, add indexing, etc., and I'd really like to be able to do it so that I don't have to split up the string into separate parts.
ADO isn't the issue: the ACE/Jet engine simply does not support multiple SQL statements within a single operation. In other words, ACE/JET SQL lacks procedural syntax found in most 'industrial-strength' SQL products. See #David-W-Fenton's answer for more detail.
Bottom line: You will need to issue a Connection.Execute for each CREATE TABLE statement i.e. client side procedural code. But they can (perhaps should) all be run in the same transaction, of course.
ADO to MS Access does not support batch SQL statements. You need to run each statement as a separate execution.
People who think you can send multiple SQL statements to Jet in a batch just aren't thinking.
Jet is a file-server database engine -- there is no centralized server process controlling interaction between clients and the actual data store. Instead, clients are all running individual instances of Jet and cooperatively editing a file in a way that is controlled by the Jet locking file (LDB). Without a centralized process to serialize and prioritize the SQL statements, you wouldn't want Jet to be able to process multiple statements in a batch.
Those who are offering the suggestion of using ADO and separating the statements with a CrLf should code it up and give it a try and then get back to us about how useful their speculative advice actually is.
If you're sample set of commands is typical, just do something like this in VBA or the language of your choice:
public sub ExeuteBatch(BatchString as String)
var s as string
var abatch as array
sbatch = replace(sbatch, "\n", "")
abatch = split(BatchString, ";")
for each s in abatch
** adodb execute s here **
next s
end sub
That's off the top of my head, but you should be able to take it from there I hope.
Crude but it works - create the necessary number of queries with one SQL statement each, then use a Macro to run the queries successively. That's about as good as can be done with ADO/Jet.
I don't know if ADO is constructed over JET OleDB Engine, which I suppose, if it is this way, The Jet Engine doesn't support execution of multiple statements in one single batch, we tryed separating with ; and with the GO reserved word, but it does not work.
I think you can run multiple commands in one ADO Command.
You just need proper line feeds between then. i.e. \n doesn't work.
Try something like this:
(Using VB Syntaxish)
MyQuery = "Select * from Whatever " & vbLf <br>
MyQuery = MyString & "Select * from SomethingElse " & vbLF
oRecordset.Open(oDatabase.m_pConnection, MyQuery )