There are ways of doing this in optional parts of the standard, but a lot of databases support their own way of doing it.
A really good site that talks about this and other things is http://troels.arvin.dk/db/rdbms/#select-limit.
Basically, PostgreSQL and MySQL supports the non-standard:
SELECT...
LIMIT y OFFSET x
Oracle, DB2 and MSSQL supports the standard windowing functions:
SELECT * FROM (
SELECT
ROW_NUMBER() OVER (ORDER BY key ASC) AS rownumber,
columns
FROM tablename
) AS foo
WHERE rownumber <= n
(which I just copied from the site linked above since I never use those DBs)
Update: As of PostgreSQL 8.4 the standard windowing functions are supported, so expect the second example to work for PostgreSQL as well.
Update: SQLite added window functions support in version 3.25.0 on 2018-09-15 so both forms also work in SQLite.
I see many people use subqueries or else window functions to do this, but I often do this kind of query without subqueries in the following way. It uses plain, standard SQL so it should work in any brand of RDBMS.
SELECT t1.*
FROM mytable t1
LEFT OUTER JOIN mytable t2
ON (t1.UserId = t2.UserId AND t1."Date" < t2."Date")
WHERE t2.UserId IS NULL;
In other words: fetch the row from t1
where no other row exists with the same UserId
and a greater Date.
(I put the identifier "Date" in delimiters because it's an SQL reserved word.)
In case if t1."Date" = t2."Date"
, doubling appears. Usually tables has auto_inc(seq)
key, e.g. id
.
To avoid doubling can be used follows:
SELECT t1.*
FROM mytable t1
LEFT OUTER JOIN mytable t2
ON t1.UserId = t2.UserId AND ((t1."Date" < t2."Date")
OR (t1."Date" = t2."Date" AND t1.id < t2.id))
WHERE t2.UserId IS NULL;
Re comment from @Farhan:
Here's a more detailed explanation:
An outer join attempts to join t1
with t2
. By default, all results of t1
are returned, and if there is a match in t2
, it is also returned. If there is no match in t2
for a given row of t1
, then the query still returns the row of t1
, and uses NULL
as a placeholder for all of t2
's columns. That's just how outer joins work in general.
The trick in this query is to design the join's matching condition such that t2
must match the same userid
, and a greater date
. The idea being if a row exists in t2
that has a greater date
, then the row in t1
it's compared against can't be the greatest date
for that userid
. But if there is no match -- i.e. if no row exists in t2
with a greater date
than the row in t1
-- we know that the row in t1
was the row with the greatest date
for the given userid
.
In those cases (when there's no match), the columns of t2
will be NULL
-- even the columns specified in the join condition. So that's why we use WHERE t2.UserId IS NULL
, because we're searching for the cases where no row was found with a greater date
for the given userid
.
Best Answer
You can't update the table your trigger is firing against, you'll get an ORA-04091 mutating table error. You can update values in the row itself, using the
:NEW
syntax, as long as it's a 'before' trigger.I'm unclear what you mean about the subtotal though; it sounds like you have a running total field on the table; if thats based on other records on the same table (e.g. you have multiple records for the same order, and you want an inserted record to have the sum of all the previous ones). If that is the case then you can't do that either, as you'd hit the same ORA-04901.
If you're updating a row then you could adjust a field, by setting for example
:NEW.subtotal := :OLD.subtotal - :OLD.value + :NEW.value
, but not sure what the benefit of that field would be.It would be helpful to see what your business logic is and how it fits in with the insert/update, and what you want the trigger to do. Often with something like this you really want to be using a wrapper procedure around the insert/update, rather than a trigger, but it's a bit vague at present.
For the subtotal to remain accurate, I'd probably avoid trying to maintain it all, and instead use a view which has an analytic function calculating it for you. Much less hassle, in my experience.