It's common to have a table where for example the the fields are account, value, and time. What's the best design pattern for retrieving the last value for each account? Unfortunately the last keyword in a grouping gives you the last physical record in the database, not the last record by any sorting. Which means IMHO it should never be used. The two clumsy approaches I use are either a subquery approach or a secondary query to determine the last record, and then joining to the table to find the value. Isn't there a more elegant approach?
could you not do:
select account,last(value),max(time)
from table
group by account
I tested this (granted for a very small, almost trivial record set) and it produced proper results.
Edit:
that also doesn't work after some more testing. I did a fair bit of access programming in a past life and feel like there is a way to do what your asking in 1 query, but im drawing a blank at the moment. sorry.
After literally years of searching I finally found the answer at the link below #3. The sub-queries above will work, but are very slow -- debilitatingly slow for my purposes.
The more popular answer is a tri-level query: 1st level finds the max, 2nd level gets the field values based on the 1st query. The result is then joined in as a table to the main query. Fast but complicated and time-consuming to code/maintain.
This link works, still runs pretty fast and is a lot less work to code/maintain. Thanks to the authors of this site.
http://access.mvps.org/access/queries/qry0020.htm
The subquery option sounds best to me, something like the following psuedo-sql. It may be possible/necessary to optimize it via a join, that will depend on the capabilities of the SQL engine.
select *
from table
where account+time in (select account+max(time)
from table
group by account
order by time)
This is a good trick for returning the last record in a table:
SELECT TOP 1 * FROM TableName ORDER BY Time DESC
Check out this site for more info.
#Tom
It might be easier for me in general to do the "In" query that you've suggested. Generally I do something like
select T1.account, T1.value
from table T as T1
where T1 = (select max(T2.time) from table T as T2 where T1.account = T2.Account)
#shs
yes, that select last(value) SHOULD work, but it doesn't... My understanding although I can't produce an authorative source is that the last(value) gives the last physical record in the access file, which means it could be the first one timewise but the last one physically. So I don't think you should use last(value) for anything other than a really bad random row.
I'm trying to find the latest date in a group using the Access 2003 query builder, and ran into the same problem trying to use LAST for a date field. But it looks like using MAX finds the lates date.
Perhaps the following SQL is clumsy, but it seems to work correctly in Access.
SELECT
a.account,
a.time,
a.value
FROM
tablename AS a INNER JOIN [
SELECT
account,
Max(time) AS MaxOftime
FROM
tablename
GROUP BY
account
]. AS b
ON
(a.time = b.MaxOftime)
AND (a.account = b.account)
;
Related
I know this is an amateur question, but I've searched every resource I can think of, and now I'm at my wit's end. The following query works perfectly on most of my tables, but for some reason it is not working on the tables that I desperately need it for:
SELECT COUNT(*)
FROM radio_r1_own_it
WHERE daypart LIKE 'AM';
The query works exactly how I want it to for nearly all of my tables, but for some reason it is returning a value of "0" on the tables I need it for (even though there are over 20 instances of "AM" in the "daypart" column on this table). I have checked and double-checked everything I can think of.
I'm relatively new to SQL but I've never encountered a problem like this before. Anyone have any ideas or resources that might help? Thanks so much for your time!
EDIT: I don't have enough reputation points to post a screen shot on here... but here's a link where you can see one: http://imgur.com/ZhyEqJY
There are 29 columns in this table. If there's any other info that might help just let me know, thanks!
You need to add the like part as shown below:
where column_name like '%AM%'
when you write like 'AM' it is searching for the full match
Try this.
SELECT COUNT(*) FROM radio_r1_own_it WHERE daypart LIKE '%AM%';
If you want to order it using the count,
SELECT COUNT(*) FROM radio_r1_own_it WHERE daypart LIKE '%AM%' ORDER BY COUNT(*) DESC;
DESC - Descending order
ASC - Ascending order
LIKE statements are really slow in sql. If you have a daypart column then I assume you have a date column. I recommend something like this instead:
SELECT COUNT(*)
FROM radio_r1_own_it
WHERE HOUR(dateColumn) < 13
Unless the string you're matching is exactly AM you need to use wildcard characters to match the string:
SELECT COUNT(*) FROM radio_r1_own_it WHERE daypart LIKE '%AM%';
The % matches any number of characters before and after AM.
See the documentation for more information.
Warning: This is a soft question, where you'll be answering to someone who has just started teaching himself SQL from the ground up. I haven't gotten my database software set up yet, so I can't provide tables to run queries against. Some patience required.
Warnings aside, I'm experimenting with basic SQL but I'm having a little bit of a rough time getting a clear answer about the inner workings of subqueries and their execution order within my query.
Let us say my query looks something like shit:
SELECT * FROM someTable
WHERE someFirstValue = someSecondValue
AND EXISTS (
SELECT * FROM someOtherTable
WHERE someTable.someFirstValue = someOtherTable.someThirdValue
)
;
The reason I'm here, is because I don't think I understand fully what is going on in this query.
Now I don't want to seem lazy, so I'm not going to ask you guys to "tell me what's going on here", so instead, I'll provide my own theory first:
The first row in someTable is checked so see if someFirstValue is the same as someSecondValue in that row.
If it isn't, it goes onto the second row and checks it too. It continues like this until a row passes this little inspection.
If a row does pass, it opens up a new query. If the table produced by this query contains even a single row, it returns TRUE, but if it's empty it returns FALSE.
My theory ends here, and my confusion begins.
Will this inner query now compare only the rows that passed the first WHERE? Or will it check all the items someTable and someOtherTable?
Rephrased; will only the rows that passed the first WHERE be compared in the someTable.someFirstValue = someOtherTable.someThirdValue subquery?
Or will the subquery compare all the elements from someTable to all the elements in someOtherTable regardless of which passed the first WHERE and which didn't?
UPDATE: Assume I'm using MySQL 5.5.32. If that matters.
The answer is that SQL is a descriptive language that describes the result set being produced from a query. It does not specify how the query is going to be run.
In your case the query has several options on how it might run, depending on the database engine, what the tables look like, and indexes. The query itself:
SELECT t.*
FROM someTable t
WHERE t.someFirstValue = t.someSecondValue AND
EXISTS (SELECT *
FROM someOtherTable t2
WHERE t.someFirstValue = t2.someThirdValue
);
Says: "Get me all columns from SomeTable where someFirstValue = someSecondValue and there is a corresponding row in someOtherTable where that's table column someThirdValue is the same as someFirstValue".
One possible way to approach this query would be to scan someTable and first check for the first condition. When the two columns match, then look up someFirstValue in an index on someOtherTable(someThirdValue) and keep the row if the values match. As I say, this is one approach, and there are others.
I have the following query (MS Access 2010) which I'm trying to use to update a table with a running balance:
UPDATE Accounts a SET a.CurrentBalance =
(SELECT sum(iif(c.categoryid = 2,t.Amount * -1, t.Amount)) +
(select a1.openingbalance
from accounts a1 where a1.accountid = a.accountid) AS TotalAmount
FROM transactions t inner join (
transactiontypes tt inner join
categories c on c.categoryid = tt.categoryid)
on t.transactiontypeid = tt.transactiontypeid);
The tables used are:
A work around for the "Query must use an updateable query" is to use a temporary table and then update the final target data based on the aggregated data in the temporary table. In your case, as mwolfe suggests, you have an aggregate function in the inner select query. The workaround could provide a quick fix for this situation, as it has for me.
Ref: http://support.microsoft.com/kb/328828
This article helped me understand the specifics of the situation and provided the work around:
http://www.fmsinc.com/MicrosoftAccess/query/non-updateable/index.html
You cannot use aggregate functions (like SUM) in an update query. See Why is my query read-only? for a full list of conditions that will cause your query to be "non-updateable".
The Access db engine includes support for domain functions (DMax, DSum, DLookup, etc.). And domain functions can often allow you to circumvent non-updateable query problems.
Consider DSum() with these 3 rows of data in MyTable.
id MyNumber
1 2
2 3
3 5
Then in the Immediate window, here are 2 sample DSum() expressions.
? DSum("MyNumber", "MyTable")
10
? DSum("IIf(id=1,MyNumber * -1, MyNumber)", "MyTable")
6
I think you may be able to use something like that second expression as a replacement for the sum(iif(c.categoryid = 2,t.Amount * -1, t.Amount) part of your query.
And perhaps you can use a DLookup() expression to get your TotalAmount value. Unfortunately I got frustrated trying to translate your current SQL to domain functions. And I realize this isn't a complete solution, but hope it will point you to something useful. If you edit your question to show us brief samples of the starting data and what you hope to achieve from your UPDATE statement based on that sample data, I would be willing to have another look at this.
Finally, consider whether you absolutely must store CurrentBalance in a table. As a rule of thumb, avoid storing derived values. Instead, use a SELECT query to compute the derived value when you need it. That approach would guarantee CurrentBalance is always up-to-date whenever you retrieve it. It would also spare you the effort to create a working UPDATE statement.
I have the following query..
SELECT Flights.flightno,
Flights.timestamp,
Flights.route
FROM Flights
WHERE Flights.adshex = '400662'
ORDER BY Flights.timestamp DESC
Which returns the following screenshot.
However I cannot use a simple group by as for example BCS6515 will appear a lot later in the list and I only want to "condense" the rows that are the same next to each other in this list.
An example of the output (note BCS6515 twice in this list as they were not adjacent in the first query)
Which is why a GROUP BY flightno will not work.
I don't think there's a good way to do so in SQL without a column to help you. At best, I'm thinking it would require a subquery that would be ugly and inefficient. You have two options that would probably end up with better performance.
One would be to code the logic yourself to prune the results. (Added:) This can be done with a procedure clause of a select statement, if you want to handle it on the database server side.
Another would be to either use other information in the table or add new information to the table for this purpose. Do you currently have something in your table that is a different value for each instance of a number of BCS6515 rows?
If not, and if I'm making correct assumptions about the data in your table, there will be only one flight with the same number per day, though the flight number is reused to denote a flight with the same start/end and times on other days. (e.g. the 10a.m. from NRT to DTW is the same flight number every day). If the timestamps were always the same day, then you could use DAY(timestamp) in the GROUP BY. However, that doesn't allow for overnight flights. Thus, you'll probably need something such as a departure date to group by to identify all the rows as belonging to the same physical flight.
GROUP BY does not work because 'timestamp' value is different for 2 BCS6515 records.
it will work only if:
SELECT Flights.flightno,
Flights.route
FROM Flights
WHERE Flights.adshex = '400662'
GROUP BY (Flights.flightno)
So I'm having serious speed problems using a left join to count ticket comments from another table. I've tried using a sub-select in the count field and had precisely the same performance.
With the count, the query takes about 1 second on maybe 30 tickets, and 5 seconds for 19000 tickets (I have both a production and a development server so times are skewed a tad). I'm trying to optimize this as four variations of the query need to be run each time per page refresh.
Without the count, I see execution time fall from 1 second to 0.03 seconds, so certainly this count is being run across all tickets and not just the ones which are selected.
Here's a trimmed down version of the query in question:
SELECT tickets.ticket_id,
ticket_severity,
ticket_short_description,
ticket_full_description,
count(*) as commentCount,
FROM tickets (LEFT JOIN tickets_comment on ticket_id = tickets_comment.ticket_id)
WHERE ticket_status='Open'
and ticket_owner_id='133783475'
GROUP BY
everything,
under,
the,
sun
Now, not all tickets have comments, so I can't just do a right or standard join. When doing that the speed is fairly good (1/10th the current), but any tickets without comments aren't included.
I see three fixes for this, and would like any and all advice you have.
Create a new column comment_count and use a trigger/update query on new comment
Work with the UI and grab comments on the fly (not really wanted)
Hope stackoverflow folks have a more elegant solution :รพ
Ideas?
A co-worker has come to the rescue. The query was just using join improperly.
What must be done here is create a second table with a query like:
select count(*) from tickets_comment group by ticket_id where (clause matches other)
which will create a table with counts for each ticket id. Then join that table with the ticket table where the ticket ids match. It's not as wicked fast as creating a new column, but it's at least 1/10th the speed it was, so I'm pleased as punch.
Last step is converting nulls (on tickets where there were no comments) into zeros
Is by far the fastest solution and you'll see it done in Rails all the time because it really is that fast.
count(*) is really only used when you aren't selecting any other attributes. Try count(ticket_id) and see if that helps. I can't run explain so I can't test it myself but if your analysis is correct it should help.
Try running explain on the query to make sure the correct indexes are being used. If there are no indexes being used, create another one