Improve asp script performance that takes 3+ minutes to run - sql

I use an SQL statement to remove records that exist on another database but this takes a very long time.
Is there any other alternative to the code below that can be faster? Database is Access.
email_DB.mdb is from where I want to remove the email addresses that exist on the other database (table Newsletter_Subscribers)
customers.mdb is the other database (table Customers)
SQLRemoveDupes = "DELETE FROM Newsletter_Subscribers WHERE EXISTS (select * from [" & strDBPath & "Customers].Customers " _
& "where Subscriber_Email = Email or Subscriber_Email = EmailO)"
NewsletterConn = "Driver={Microsoft Access Driver (*.mdb)};DBQ=" & strDBPath & "email_DB.mdb"
Set MM_editCmd = Server.CreateObject("ADODB.Command")
MM_editCmd.ActiveConnection = NewsletterConn
MM_editCmd.CommandText = SQLRemoveDupes
MM_editCmd.Execute
MM_editCmd.ActiveConnection.Close
Set MM_editCmd = Nothing
EDIT: Tried the SQL below from one of the answers but I keep getting an error when running it:
SQL: DELETE FROM Newsletter_Subscribers WHERE CustID IN (select CustID from [" & strDBPath & "Customers].Customers where Subscriber_Email = Email or Subscriber_Email = EmailO)
I get a "Too few parameters. Expected 1." error message on the Execute line.

I would use WHERE Subscriber_Email IN (Email, Email0) as the WHERE clause
SQLRemoveDupes = "DELETE FROM Newsletter_Subscribers WHERE EXISTS " & _
(select * from [" & strDBPath & "Customers].Customers where Subscriber_Email IN (Email, EmailO)"
I have found from experience that using an OR predicate in a WHERE clause can be detrimental in terms of performance because SQL will have to evaluate each clause separately, and it might decide to ignore indexes and use a table scan. Sometime it can be better to split it into two separate statements. (I have to admit I am thinking in terms of SQL Server here, but the same may apply to Access)
"DELETE FROM Newsletter_Subscribers WHERE EXISTS " & _
(select * from [" & strDBPath & "Customers].Customers where Subscriber_Email = Email)"
"DELETE FROM Newsletter_Subscribers WHERE EXISTS " & _
(select * from [" & strDBPath & "Customers].Customers where Subscriber_Email = EmailO)"

Assuming there's an ID-column present in the Customers table, the following change in SQL should give better performance:
"DELETE FROM Newsletter_Subscribers WHERE ID IN (select ID from [" & strDBPath & "Customers].Customers where Subscriber_Email = Email or Subscriber_Email = EmailO)"
PS. The ideal solution (judging from the column names) would be to redesign the tables and code logic of inserting emails in the first place. DS

Try adding an Access Querydef and calling that.

It sounds like you do not have an index on the subscriber_enail field. This forces a table scan ( or several). Add an index on this field and you should see significant improvement.
I would have coded the query
DELETE FROM Newsletter_Subscribers where (Subscriber_Email = Email or Subscriber_Email = EMail0)

I would try splitting this into two separate statements with separate database connections.
First, fetch the list of email addresses or IDs in the first database (as a string).
Second, construct a WHERE NOT IN statement and run it on the second database.
I would imagine this would be much faster as it does not have to interoperate between the two databases. The only possible issue would be if there are thousands of records in the first database and you hit the maximum length of a sql query string (whatever that is).
Here are some useful functions for this:
function GetDelimitedRecordString(sql, recordDelimiter)
dim rs, str
set rs = db.execute(sql)
if rs.eof then
str = ""
else
str = rs.GetString(,,,recordDelimiter)
str = mid(str, 1, len(str)-len(recordDelimiter))
end if
rs.close
set rs = nothing
GetDelimitedRecordString = str
end function
function FmtSqlList(commaDelimitedStringOrArray)
' converts a string of the format "red, yellow, blue" to "'red', 'yellow', 'blue'"
' useful for taking input from an html form post (eg a multi-select box or checkbox group) and using it in a SQL WHERE IN clause
' prevents sql injection
dim result:result = ""
dim arr, str
if isArray(commaDelimitedStringOrArray) then
arr = commaDelimitedStringOrArray
else
arr = split(commaDelimitedStringOrArray, ",")
end if
for each str in arr
if result<>"" then result = result & ", "
result = result & "'" & trim(replace(str&"","'","''")) & "'"
next
FmtSqlList = result
end function

Related

Update or insert field from table into another table VBA sql

First this is my first post so I apologize for the layout or any other errors with presentation.
I have two tables.
tempWkEndHrs: Is created when the user selects a weekending date and their name from two comboboxs. The result is 5 fields: Name1, Task, Closed Date, Initiated Date and #xx/xx/xxxx# <--the same date as chosen by the user from the combobox to create the table. This is done to limit the number of date fields returned and built into the temp table.
The purpose of this table is so the user can populate as many values under the #xx/xx/xxxx# field/column to show/see the distribution of hours taken on tasks for that week. (its also used as a check to make sure entered values sum up as expected)
I would like to save the new values entered underfield #xx/xx/xxxx# in tempWkEndHrs to the same field #xx/xx/xxxx# in tblHrs.
tblHrs: has fields... Task , #xx/xx/xxxx#, #xx/xx/xxxx#, #xx/xx/xxxx#, #xx/xx/xxxx#, etc.. <--Same dates available within combobox.
I have having difficulty finding an example of updating multiple records from one table to another to fit my situation.
Below is my last attempt from several variations.
Dim strITEM As String
Dim strWkEnd As String
strWkEnd = Me.cmbWkEnd
strSQL = "UPDATE tblHrs SET " & _
"[" & strWkEnd & "] = [" & strWkEnd & "] " & _
"FROM tempWkEndHrs " & _
" WHERE [Task] = [Task]"
*Update 10-24-2017 5:37pm
Dim strITEM As String
Dim strWkEnd As String
strWkEnd = Me.cmbWkEnd
strSQL = "UPDATE tblHrs " & _
"SET tempWkEndHrs.[" & strWkEnd & "] = tblHrs.[" & strWkEnd & "] "& _
"FROM tempWkEndHrs " & _
" WHERE tempWkEndHrs.[Task] = tblHrs.[Task]"
Result:
UPDATE tblHrs SET tempWkEndHrs.[9/30/2017] = tblHrs.[9/30/2017] FROM tempWkEndHrs WHERE tempWkEndHrs.[TASK] = tblHrs.[TASK]
***Run-Time error '3075
***Syntax error(missing operator) in query expression 'tblHrs.[9/30/2017] FROM tempWkEndHrs'.
If this is clear as mud please let me know.
enter image description here
BIG SHOUT OUT TO
Oscar Anthony and Parfait
https://stackoverflow.com/a/34910889/8797058
5.You don't need the FROM tblTemp clause in the SQLReplace String.
6.EDIT: As #Parfait pointed out, tblTemp does not exist in scope of the SQLReplace statement. You should do an INNER JOIN to fix that: <--AWESOME!!
strWkEnd = Me.cmbWkEnd
strSQL = "UPDATE tblHrs INNER JOIN tempWkEndHrs ON tblHrs.TASK = tempWkEndHrs.TASK SET tblHrs.[" & strWkEnd & "] = tempWkEndHrs.[" & strWkEnd & "] "
Works! >:P
I didn't receive any answers to this post so obviously I need to write better questions, I figured it was mud...Thanks for the ~21 Views ?lol.

Run Time error 3061 Too Few parameters. Expected 6. Unable to update table from listbox

All,
I am running the below SQL and I keep getting error 3061. Thank you all for the wonderful help! I've been trying to teach myself and I am 10 days in and oh my I am in for a treat!
Private Sub b_Update_Click()
Dim db As DAO.Database
Set db = CurrentDb
strSQL = "UPDATE Main" _
& " SET t_Name = Me.txt_Name, t_Date = Me.txt_Date, t_ContactID = Me.txt_Contact, t_Score = Me.txt_Score, t_Comments = Me.txt_Comments" _
& " WHERE RecordID = Me.lbl_RecordID.Caption"
CurrentDb.Execute strSQL
I am not sure but, you can try somethink like that
if you knom the new value to insert in the database try with a syntax like this one
UPDATE table
SET Users.name = 'NewName',
Users.address = 'MyNewAdresse'
WHERE Users.id_User = 10;
Now, if you want to use a form (php)
You have to use this
if(isset($_REQUEST["id_user" ])) {$id_user = $_REQUEST["id_user" ];}
else {$id_user = "" ;}
if(isset($_REQUEST["name" ])) {$name= $_REQUEST["name" ];}
else {$name = "" ;}
if(isset($_REQUEST["address" ])) {$address= $_REQUEST["adress" ];}
else {$adress= "" ;}
if you use mysql
UPDATE table
SET Users.name = '$name',
Users.address = '$adress'
WHERE Users.id_User = 10;
i don't know VBA but I will try to help you
Going on from my comment, you first need to declare strSQL as a string variable.
Where your error expects 6 values and access doesn't know what they are. This is because form objects need to be outside the quotations of the SQL query, otherwise (as in this case) it will think they are variables and obviously undefined. The 6 expected are the 5 form fields plus 'strSQL'.
Private Sub b_Update_Click()
Dim db As DAO.Database
dim strSQL as string
Set db = CurrentDb
strSQL = "UPDATE Main" & _
" SET t_Name = '" & Me.txt_Name & "'," & _
" t_Date =#" & Me.txt_Date & "#," & _
" t_ContactID =" & Me.txt_Contact & "," & _
" t_Score =" & Me.txt_Score & "," & _
" t_Comments = '" & Me.txt_Comments & "'," & _
" WHERE RecordID = '" & Me.lbl_RecordID.Caption & "';"
CurrentDb.Execute strSQL
end sub
Note how I have used double quotes to put the form fields outside of the query string so access knows they aren't variables.
If your field is a string, it needs encapsulating in single quotes like so 'string'. If you have a date field it needs encapsulating in number signs like so #date# and numbers/integers don't need encapsulating.
Look at the code I have done and you can see I have used these single quotes and number signs to encapsulate certain fields. I guessed based on the names of the fields like ID's as numbers. I may have got some wrong so alter where applicable... Or comment and I will correct my answer.

Store a sql select statement, ran from VBA, in a numeric variable

I'm working on creating a dynamic pass-through query and in order to do so I first need to query my local db and get an ID.
This ID is what I will put into my pass-through query for my WHERE clause of the query.
My string:
getCorpID = "SELECT corpID " & _
"FROM dbo_corp " & _
"WHERE name = " & Forms.frmMain.Combo4.Value
I'm then trying to do something akin to:
CorpID (integer) = docmd.runsql (getCorpID)
I realize, however that docmd runsql doesn't work with select statements, or return a value even. What can I use to run my string
getCorpId
as sql and store the result (It will only be one result, every time.. one number) in my variable CorpID
Thank you.
Consider about using Recordset :
Dim dbs As DAO.Database
Dim rsSQL As DAO.Recordset
Dim getCorpID As String
Dim CorpID
Set dbs = CurrentDb
getCorpID = "SELECT corpID " & _
"FROM dbo_corp " & _
"WHERE name = " & Forms.frmMain.Combo4.Value
Set rsSQL = dbs.OpenRecordset(getCorpID , dbOpenSnapshot)
rsSQL.MoveFirst
CorpID = rsSQL.Fields("corpID")

error '80020009' - But I'm not in a query

I have a system that places an order in ASP, and when an order is submitted, a notification email goes out.
When I submit the order, I get error '80020009' and the line it points to is this:
email_text = email_text & "<html>" & _
That's a simple line that just starts building the html string that fills the email! Isn't error '80020009' supposed to be for SQL statements? Or am I missing something?
The page still continues on and completes the order process, it just shows that error message first.
I realize this question doesn't really provide much detail, but I don't know what else to specify, I'm just at a loss.
EDIT: Here's some SQL that is a few lines up from that line. Not sure how this would cause the issue, since it's telling me it's on that email line, but it can't hurt to throw it in:
str = "SELECT creation_date, supplier FROM argus.PURCHASE_ORDER WHERE purchase_order = '" & Trim(Request.Form("order_id")) & "' AND customer = '" & Session("customer_id") & "' AND store = '" & Session("store_id") & "' AND userid = '" & Session("user_id") & "'"
Set rst = ConnFW.Execute(str)
str2 = "SELECT a.store_name, a.address1, a.address2, a.city, a.state_cd, a.zipcode, b.customer_name, c.supplier_account FROM argus.STORE a, argus.CUSTOMER b, argus.ELIGIBLE_SUPPLIER c WHERE a.customer = b.customer AND a.customer = c.customer AND a.store = " & Session("store_id") & " AND b.customer = " & Session("customer_id") & " AND c.supplier = " & Trim(rst("supplier")) & ""
Set rst2 = ConnFW.Execute(str2)
Can you provide additional code? There isn't anything wrong with your code except you need to make sure you have something on the line following the underscore:
email_text = email_text + "<html>" & _
""
-- EDIT
Thanks for posting your edits. I see a couple potential issues. In your second recordset object, your trying to access rst("supplier"), but that field hasn't been read yet. Instead of using connfw.execute(str) which is used to execute sql statements like INSERT, UPDATE and DELETE, use rst.open which is used with SELECT.
Try something like:
supplier = ""
rst.open str, connfw
if not rst.eof then
supplier = rst("supplier")
end if
rst.close
Then use supplier in your 2nd sql statement. Also, if the supplier field from the eligible_supplier table is a string, you need to wrap single quotes around that field in your where clause.

VBA Wait for DoCmd.RunSQL OR Better Query - Ms Access

I have a query which uses an array of tables (stored as a string array) to loop through my tables and perform a Delete/Update set of queries. The queries are identical other than the table names, hence using a loop to iterate through using the table name as a variable.
The problem is, my Delete query locks the table and the Update query runs too quickly afterward; I get a "db is locked" error.
I need either of two things:
A way to tell VBA to "wait for previous command" OR
A way to concatenate these queries into one (or two) queries: one to delete the database rows and another to import the new ones. With this I could just run the queries from a standard access query (which should allocate proper time, finish queries etc)
The only catch to this is that there are parent-child relations, so the parent table has to be updated before its children (currently accomplished through array ordering).
Here's the current code which (sometimes) produces the "DB locked/in use" message:
For i = 0 To UBound(tables)
'Delete all data first
sql = "DELETE * FROM " & tables(i)
DoCmd.RunSQL sql
'Update all data second
sql = "INSERT INTO " & tables(i) & " IN """ & toDB & """ SELECT " & tables(i) & " .* FROM " & tables(i) & " IN """ & fromDB & """;"
DoCmd.RunSQL sql
Next
Should clarify: the queries take one backend's (fromDB) rows from identical tables and pushes it to another backend's (toDB) rows
EDIT: In response to the questions regarding INSERT INTO, my problem with that is if I add fields to the toDB, it will delete them if I overwrite. The reason I have to do this backdoor approach is because the database is still in development, but is also being used with select tables. Updates and feature improvements are done daily. I cannot use a simple split-backend either, because the other computer accessing the database is not always on the network (we have to manually sync it when it returns to the network), so I work on one backend and it works on another, identical(ish, minus my schema update) backend.
You can use ADO instead of DoCmd.RunSQL to execute your SQL synchronously.
Dim cmd As ADODB.Command
Dim cnn As New ADODB.Connection
Set cnn = CurrentProject.Connection
For i = 0 To UBound(tables)
Set cmd = New ADODB.Command
With cmd
.CommandType = adCmdText
.ActiveConnection = cnn
.CommandText = "DELETE * FROM " & tables(i)
.Execute
End With
Set cmd = New ADODB.Command
With cmd
.CommandType = adCmdText
.ActiveConnection = cnn
.CommandText = "INSERT INTO " & tables(i) & " IN """ & toDB & """ SELECT " & tables(i) & " .* FROM " & tables(i) & " IN """ & fromDB & """;"
.Execute
End With
Next
You could also add cnn.BeginTrans and cnn.CommitTrans to make the two statements Atomic.
Try something like this (Note I've commented your DoCmd.RunSQL. I changed it with db.Execute:
Sub DeleteInsertData(tables() As String, toDB As String, fromDB As String)
Dim db As DAO.Database
Dim i As Integer
Dim SQL As String
Set db = CurrentDb()
For i = 0 To UBound(tables)
'Delete all data first
SQL = "DELETE * FROM " & tables(i)
db.Execute SQL, dbFailOnError
'DoCmd.RunSQL SQL
'Update all data second
SQL = "INSERT INTO " & tables(i) & " IN """ & toDB & """ SELECT " & tables(i) & " .* FROM " & tables(i) & " IN """ & fromDB & """;"
db.Execute SQL, dbFailOnError
'DoCmd.RunSQL SQL
Next
Set db = Nothing
End Sub
I'm no expert, but I don't think you need the DELETE part. SELECT INTO is the syntax for making a table. If the table already exists, it's overwritten.
If I need to wait for something, I tend to use DoEvents to allow windows to process any other actions that have been put on hold.
From the help: "Yields execution so that the operating system can process other events."
Found this:
.BeginTrans then
.CommitTrans dbForceOSFlush
as a way to force updates to be written before continuing