How to improve efficiency of this query & VBA? - sql

I have this query in Access:
SELECT TOP 10 title,
ConcatRelated("DOCTEXT","DocumFrag", "title='" & title & "'" ) AS result
FROM DocumFrag
GROUP BY title;
DocumFrag contains about 9000 records, ConcatRelated is VBA code found here: http://allenbrowne.com/func-concat.html
When I run this query for only TOP 10 and it completes, it continually lags to the point of 20 second response time (clicking, typing, etc).
Is there a way I can improve this to be more stable? I'm doing TOP 10 as an example to test if it lags; in the end I'd need to select all.
My goal of this query is the same as Concatenating record values in database (MS Access) or in server side code (ASP.NET) (except in Access, not ASP.NET)
Or is there a way I can accomplish this using a query, instead of VBA?

My best guess is that ConcatRelated evaluates for every 'title' in 'DocumFrag'. Select the top 10 in an inner query before you apply the function:
SELECT q.title, ConcatRelated("DOCTEXT","DocumFrag", "title='" & q.title & "'" ) AS result
FROM
(SELECT TOP 10 title FROM DocumFrag) AS q
GROUP BY q.title;

yes, 1st make sure your data table has a clustered index (this determines the order the data is stored on disk), otherwise you have a heap and the sql engine needs to query the entire table as the data can be anywhere in the table. 2nd put a covering index on the querying parameters and the data you want to return. 3rd you are trying to group text? It would be better to Find the top 10 items and then conconate the text associate with them rather than conconate every group item as your code is doing and then select the top 10.

Related

How to query only old and duplicate data from a database in SQL

I'm trying to query my database to pull only duplicate/old data to write to a scratch section in excel (Using a macro passing SQL to the DB).
For now, I'm currently testing in Access alone to only filter out the old data.
First, I'm trying to filter my database by a specifed WorkOrder, RunNumber, and Row.
The code below only filters by Work Order, RunNumber, and Row. ...but SQL doesn't like when I tack on a 2nd AND statement; so this currently isn't working.
SELECT *
FROM DataPoints
WHERE (((DataPoints.[WorkOrder])=[WO2]) AND ((DataPoints.[RunNumber])=6) AND ((DataPoints.[Row]=1)
Once I figure that portion out....
Then if there is only 1 entry with specified WorkOrder, RunNumber, and Row, then I want filter it out. (its not needed in the scratch section, because its data is already written to the main section of my report)
If there are 2 or more entries with said criteria(WO, RN, and Row), then I want to filter out the newest entry based on RunDate and RunTime, and only keep all older entries.
For instance, in the clip below. The only item remaining in my filtered query will be the top entry with the timestamp 11:47:00AM.
.
Are there any recommended commands to complete this problem? Any ideas are helpful. Thank you.
I would suggest something along the lines of the following:
select t.*
from datapoints t
where
t.workorder = [WO2] and
t.runnumber = 6 and
t.row = 1 and
exists
(
select 1
from datapoints u
where
u.workorder = t.workorder and
u.runnumber = t.runnumber and
u.row = t.row and
(u.rundate > t.rundate or (u.rundate = t.rundate and u.runtime > t.runtime))
)
Here, if the correlated subquery within the where clause finds a record with the same workorder, runnumber and row, but with either a later rundate or the same rundate and a later runtime, then the record is returned by the main query.
You need two more )'s at the end of your code snippet. Or you can delete the parentheses completely in this example, MS Access will ad them back in as it deems necessary.
M.S. Access SQL can be tricky as it is not standards compliant and either doesn't allow for super complex queries, or it needs an ugly work around, like having a parentheses nesting nightmare when trying to join more than two tables.
For these reasons, I suggest using multiple Access queries to produce your results.

Insert textbox value into Access SQL query

Being an amateur in Access and VBA, I've hit a wall with the following...
In MS Access 2007, I have the following query:
SELECT .... WHERE format(NZ(l_f_date),'yyyy-mm-dd')<=**'2012-04-03'**);
I have shortened it a bit of course.
The database has approx 20 queries that need to be run on a daily basis. I have created 4 macros to run groups of queries in the manner that I need them to be run. The problem is that in every one of those queries I first have to change the date (like in the upper query). I am looking for the way to automate it a bit more.
I have an idea to create a Form, place a button for every macro on it and 2 textbox-es to enter the 2 dates I need. Now, I need those dates to appear in the bolded part. As I think about it, I have 2 options:
Create a temporary table in the database to store those two dates and pull those 2 fields in my queries.
Insert the value of the textbox somehow directly into the bolded part of the query.
I think I can manage the first solution, but the second one is making my head hurt.
Can you help?
SQL from comment
select proc_desc,count(s) as broj
into upit3
from ( select distinct a.case_id as s,
a.org_case_id,a.act_date as day,d.prod_id,d.prod_desc,
c.fname,c.dpd,c.due_amount,proc_id,proc_desc
from actions_b as a, cases_old_b as c,processes_b as p,
product_dict_b as d
where a.org_case_id=c.[org_ case_id]
and a.act_date=Forms!Form!Text10 and d.prod_id=c.product
and p.proc_id=c.process and not_lead=1 )
group by proc_desc order by proc_desc;
OK, sample data....
In x.dates, value is exactly like this: 03.04.2012
In a.act_date value is like this: 01.07.2011 13:53:56
so if its not possible with this values as they are, is it possible to use a LIKE statement in the query? Pseudo: WHERE a.act_date LIKE x.date%
If its possible, how to use it? i am a novice in sql and access queries... I google but sometimes, like this time, i get stuck.
Thanks
This structure:
SELECT .... WHERE format(NZ(l_f_date),'yyyy-mm-dd')<='2012-04-03');
Is not a good idea. The general rule is to try and stick to field (column) names on the left side of the equals. So
SELECT .... WHERE l_f_date <=#2012-04-03#
Nulls will not be included and I hope your dates are dates and not strings.
Next add the form:
SELECT .... WHERE l_f_date <= Forms!TheNameOfTheForm!TheNameOfTheTextbox
EDIT re comments
You are using the query design window, yes? Please try this test query:
SELECT a.case_id, a.act_date
FROM actions_b AS a
WHERE a.act_date=Forms!Form!Text10
OK, so i decided to create a simple 2-column table in my database that will be used just for storing yesterdays and todays date... i added 2 text-boxes to a form, 1 button and added the next procedure to the button:
Private Sub Command25_Click()
CurrentDb.Execute "DELETE * FROM Datumi"
Dim tbl As Recordset
Set tbl = CurrentDb.OpenRecordset("Datumi")
tbl.AddNew
tbl!brojka = "1"
tbl!datum = Text8.Value
tbl.Update
tbl.AddNew
tbl!brojka = "2"
tbl!datum = Text10.Value
tbl.Update
End Sub
As you can see, the click on the button will clear the table and store new values into 2 rows... row 1 is yesterday, row 2 is today...
And after that im pulling the values from that table like this:
... where x.brojka=1 and format(a.act_date,'yyyy-mm-dd')=format(x.datum,'yyyy-mm-dd') ...
I'm sure it can be done better but this one works for me...
I Solved this problem with :
[Software: MS Access 2013]
CurrentDb.Execute "INSERT INTO [Inventory Transactions] ([Product ID], [Order ID])" _
& "VALUES ( " & Me.Product_ID & ", " & Me.Order_ID & ")"

Import multiple queries written in SQL into access

I've got to write 50 relatively simple queries, that all use the same basic form, but each successive query depends on the one before it to run.
I can quick and easily write the queries in SQL in an text editor e.g. word, but I dont know how to import the text back into access. Nor do I know how to specify the name of the query in the SQL code or how to specify that the end of a query has been readched.
Here is a sample of 4 queries. Here, the 1st line is the name of the Query and the two consecutive hard return represents the end of eqch query.
‘Ring2Q1
SELECT RINGS.Parent, RINGS_1.Child, 2 AS Ring
FROM RINGS INNER JOIN RINGS AS RINGS_1 ON RINGS.Child = RINGS_1.Parent;
‘Ring2Q2
SELECT Ring2Q1.Parent, Ring2Q1.Child, Max(Ring2Q1.Ring) AS Ring
FROM Ring2Q1
GROUP BY Ring2Q1.Parent, Ring2Q1.Child;
‘Ring3Q1
SELECT RINGS.Parent, Ring2Q2.Child, 3 AS Ring
FROM RINGS INNER JOIN Ring2Q2 ON RINGS.Child = Ring2Q2.Parent;
‘Ring3Q2
SELECT Ring3Q1.Parent, Ring3Q1.Child, Max(Ring3Q1.Ring) AS Ring
FROM Ring3Q1
GROUP BY Ring3Q1.Parent, Ring3Q1.Child;
Go into Access. Create a new query. Select SQL View. You can copy and paste the text of the query in here. Save it as the name you need for the next query. Repeat. You will obviously need a starting table that the first query calls. I would look at why you need a cascading set of 50 queries, on any sizeable amount of data this is going to take a long time to run.

Top N in View or Crystal Reports?

I am wondering if it's possible to use a view to get the top 5 lines from a table.
I am finding that Crystal reports doesn't seem to have anything built in to do this, or I'd do it there.
When I query the view Select * from qryTranHistory, it returns the first 5 items, but if I try to select a specific type Select * from qryTranHistory Where tID = 45 it returns nothing, since there are no tID=45 in the top 5 normally.
Is it possible to do this?
Can it be accomplished in a sub report in Crystal Reports?
It is easy to limit a report to the top 5 records. In the menu, just choose
Report --> Selection Formulas... --> Group
In the formula, enter "RecordNumber <= 5" and you are done.
You don't need to have a group field nor summary field to do the group filter. You don't need a sort order, but using top N records without a sort order doesn't usually make much sense. It might not be efficient as OMG Ponies suggested, but for small number of records it is OK.
You can reference a sproc from Crystal Reports. In the sproc, use a conditional on the parameter.
ALTER PROCEDURE dbo.Get_TOP5
(
#tID INT = NULL
)
AS
IF #tID IS NULL
BEGIN
SELECT TOP 5
FIELD1,
FIELD2
FROM qryTranHistory
END
ELSE
BEGIN
SELECT
FIELD1,
FIELD2
FROM qryTranHistory
WHERE tID =#tID
END
A simple setting can limit the records to top 5!! Here it is, if you're using .Net 1.1 (similar arrangement of options in higher frameworks too!).
Right click on the report layout > Reports > Top N/Sort Group Expert > Choose Top N in the Dropdown that asks for the type of filtering/ sorting you wish to do > Set the Value of top N (5 in your case) > Uncheck the option that includes other records.
Your report will be filtered for only the top 5 records from the Dataset.
There's another way how it could be done and that is through the Record selection formula where you limit the No. of records, as suggested by John Price in this thread.
Cheers!
Can you put the TOP in your SELECT statement instead of in the view?
SELECT TOP 5
col1,
col2,
...
FROM
qryTranHistory
WHERE
tid = 45
If your table has more then 5 rows I hope this query:
SELECT * FROM qryTranHistory
Returns more then 5 rows because you never mentioned TOP 5.
Your question doesn't make a lot of sense as I am not sure waht you are after.
You mentioned if you ran your query with WHERE tID=45, it returns nothing, what exactly do you want it to return ?
Read up on TOP in BOL:
SELECT TOP 10 Recs FROM Records WHERE...
By the way you do not want to do this in the report / a form interface, you want to do this in your db layer.
You can do Top N processing in Crystal Reports, but it's a little obscure - you have to use the group sort expert (and in order to use that, you need to have groups and summary fields inserted into the groups.)
Doing the Top N processing in the query should be more efficient, where possible.
If you have a small recordset, you can create a running total that counts the change of rows (field1), then in Section Expert in Details, tell it to supress RTotal0 (your running total variable) to > 5

LIMIT in FoxPro

I am attempting to pull ALOT of data from a fox pro database, work with it and insert it into a mysql db. It is too much to do all at once so want to do it in batches of say 10 000 records. What is the equivalent to LIMIT 5, 10 in Fox Pro SQL, would like a select statement like
select name, address from people limit 5, 10;
ie only get 10 results back, starting at the 5th. Have looked around online and they only make mention of top which is obviously not of much use.
Take a look at the RecNo() function.
FoxPro does not have direct support for a LIMIT clause. It does have "TOP nn" but that only provides the "top-most records" within a given percentage, and even that has a limitation of 32k records returned (maximum).
You might be better off dumping the data as a CSV, or if that isn't practical (due to size issues), writing a small FoxPro script that auto-generates a series of BEGIN-INSERT(x10000)-COMMIT statements that dump to a series of text files. Of course, you would need a FoxPro development environment for this, so this may not apply to your situation...
Visual FoxPro does not support LIMIT directly.
I used the following query to get over the limitation:
SELECT TOP 100 * from PEOPLE WHERE RECNO() > 1000 ORDER BY ID;
where 100 is the limit and 1000 is the offset.
It is very easy to get around LIMIT clause using TOP clause ; if you want to extract from record _start to record _finish from a file named _test, you can do :
[VFP]
** assuming _start <= _finish, if not you get a top clause error
*
_finish = MIN(RECCOUNT('_test'),_finish)
*
SELECT * FROM (SELECT TOP (_finish - _start + 1) * FROM (SELECT TOP _finish *, RECNO() AS _tempo FROM _test ORDER BY _tempo) xx ORDER BY _tempo DESC) yy ORDER BY _tempo
**
[/VFP]
I had to convert a Foxpro database to Mysql a few years ago. What I did to solve this was add an auto-incrementing id column to the Foxpro table and use that as the row reference.
So then you could do something like.
select name, address from people where id >= 5 and id <= 10;
The Foxpro sql documentation does not show anything similar to limit.
Here, adapt this to your tables. Took me like 2 mins, i do this waaaay too often.
N1 - group by whatever, and make sure you got a max(id), you can use recno() to make one, sorted correctly
N2 - Joins N1 where the ID = Max Id of N1, display the field you want from N2
Then if you want to join to other tables, put that all in brackets and give it an alias and include it in a join.
Select N1.reference, N1.OrderNoteCount, N2.notes_desc LastNote
FROM
(select reference, count(reference) OrderNoteCount, Max(notes_key) MaxNoteId
from custnote
where reference != ''
Group by reference
) N1
JOIN
(
select reference, count(reference) OrderNoteCount, notes_key, notes_desc
from custnote
where reference != ''
Group by reference, notes_key, notes_desc
) N2 ON N1.MaxNoteId = N2.notes_key
To expand on Eyvind's answer I would create a program to uses the RecNo() function to pull records within a given range, say 10,000 records.
You could then programmatically cycle through the large table in chucks of 10,000 records at a time and preform your data load into you MySQL database.
By using the RecNO() function you can be certain not to insert rows more than once, and be able to restart at a know point in the data load process. That by it's self can be very handy in the event you need to stop and restart the load process.
Depending on the number of the returned rows and if you are using .NET Framework you can offset/limit the gotten DataTable on the following way:
dataTable = dataTable.AsEnumerable().Skip(offset).Take(limit).CopyToDataTable();
Remember to add the Assembly System.Data.DataSetExtensions.