40

I want to get the last row, which I inserted into a table in an Oracle 11g Express database. How can I do this?

William Robertson
  • 15,273
  • 4
  • 38
  • 44
sky scraper
  • 2,044
  • 6
  • 24
  • 26
  • 19
    SELECT * FROM t WHERE id = ( SELECT MAX(id) FROM t ) – Terkel Sep 11 '12 at 21:51
  • 1
    That'll only work if OP's table has id as pk and is an incrementing column. Try "select * from table where rowid in (select max(rowid) from table)" – MichaelN Sep 11 '12 at 21:55
  • 2
    @MichaelN, rowids are not guaranteed to be inserted in any order. – Ben Sep 11 '12 at 21:58
  • 1
    @ALL - I have a PK with a sequence and trigger to automatically generate row ids. – sky scraper Sep 11 '12 at 22:00
  • If you've just inserted a row using a sequnce.nextval and are in the same session you could use the sequnce.currval e.g. VARIABLE seq_num NUMBER; EXEC :seq_num := test_seq.CURRVAL; SELECT * FROM test WHERE seq_num = :seq_num; –  Feb 01 '16 at 13:48
  • @Ben, this question was asking for the last row inserted, and `max(rowid)` will be the ID of the last row inserted. These ARE guaranteed because `rowid` increments when a row is inserted. You can still use this as the highest count of rows as `rownum`, if needed, if you are thinking that the highest `rowid` might have *somehow* gotten assigned out of order, but that likelihood is nil, IMHO. – vapcguy Aug 12 '16 at 14:48

9 Answers9

58

There is no such thing as the "last" row in a table, as an Oracle table has no concept of order.

However, assuming that you wanted to find the last inserted primary key and that this primary key is an incrementing number, you could do something like this:

select *
  from ( select a.*, max(pk) over () as max_pk
           from my_table a
                )
 where pk = max_pk

If you have the date that each row was created this would become, if the column is named created:

select *
  from ( select a.*, max(created) over () as max_created
           from my_table a
                )
 where created = max_created

Alternatively, you can use an aggregate query, for example:

select *
  from my_table
 where pk = ( select max(pk) from my_table )

Here's a little SQL Fiddle to demonstrate.

Ben
  • 51,770
  • 36
  • 127
  • 149
  • I got this error when tried it(the middle query) on a table with ~ 3 billion rows ORA-01652: unable to extend temp segment by 128 in tablespace TEMP 01652. 00000 - "unable to extend temp segment by %s in tablespace %s" *Cause: Failed to allocate an extent of the required number of blocks for a temporary segment in the tablespace indicated. *Action: Use ALTER TABLESPACE ADD DATAFILE statement to add one or more files to the tablespace indicated. – Sambit Tripathy Apr 12 '16 at 19:36
  • You have a 3bn row table and you're trying to find the last row @Sambit? I can guarantee that you that you don't need to find the last row. Reassess the requirements first. If you really, really, do need to find the last row, you need a way of identifying it uniquely, or you need to increase the amount of sort space (which is the cause of your error) – Ben Apr 12 '16 at 21:15
  • You are right, I tried with a table having 1 billion rows and it worked! Unfortunately I want to find the rowid of the last added row and there is no way I can figure out the last timestamp. However I modified the your query a bit and it worked. Instead of "select a.*, max(created)... " I used "select a.rowid, max(created)..) and it worked for the 3 B table. – Sambit Tripathy Apr 12 '16 at 22:53
  • Query which worked for the 3 B table: select rowid from ( select a.rowid, max(created) over () as max_created from my_table a ) where created = max_created and then use select * from my_table where rowid='sdasdasdas' – Sambit Tripathy Apr 12 '16 at 22:54
  • If you're happy with being stuck with Oracle and migration to a different RDBMS being difficult @Sambit the. Use `select max(rowid) keep (dense_rank last order by created)` – Ben Apr 13 '16 at 22:10
  • That first query in this answer, for me, gave "9" as my max ID in my table, where `SELECT ID FROM MyTable WHERE ROWID IN (SELECT MAX(ROWID) FROM MyTable)` gave "75". I fundamentally disagree that there is no concept of "order" in the table when `ROWID` is generated whenever a row is. – vapcguy Aug 12 '16 at 14:38
  • It's well documented that [a heap table does not store data in any order](https://docs.oracle.com/database/121/CNCPT/glossary.htm#GUID-861D8FCE-B86C-46B3-AA01-35066D24F4CF) @vapcguy. It's also documeted that [rowids are not guaranteed to be ascending](https://docs.oracle.com/database/121/SQLRF/pseudocolumns008.htm#SQLRF00254): _"Oracle may reassign its rowid to a new row inserted later"_. If you've got an issue with the first query then I'd need to know the query you ran and the primary key before I would be able to determine where the issue is. – Ben Aug 12 '16 at 16:26
  • @Ben The `ID` column in my example `SELECT` statement above was the primary key. The query I ran was the exact one in the first block in this answer, above, just modified for my column name: `select MY_ID from ( select a.*, max(MY_ID) over () as max_pk from my_table a ) where MY_ID = max_pk`. While *"Oracle may reassign its rowid to a new row inserted later"*, this should be qualified by the fact this only ever can happen if you're updating/deleting pre-existing rows. If all you ever do is an `INSERT` to put in new rows, `ROWID` will maintain the order. – vapcguy Aug 12 '16 at 16:36
  • 1
    I would assume that you're not storing your `MY_ID` in a column with a numeric data-type @vapcguy, a binary sort on strings would explain the behaviour you're seeing. If not it's probably better to ask a new question with a [mvce]. Ping me if you do, I'd be interested to see what the issue is. On rowids, if you only ever do direct path inserts into a unpartitioned heap table, which you never alter in any way (including standard admin), and where you only ever have one data file with free space and never perform any other operation then it's _possible_ that the rowids will be in "ascending"... – Ben Aug 13 '16 at 09:28
  • ... order. It's not worth risk. See also: https://asktom.oracle.com/pls/asktom/f?p=100:11:::::P11_QUESTION_ID:912210644860 – Ben Aug 13 '16 at 09:30
  • Thanks for the affirmation on `ROWID`, so I upvoted the comment, but IMHO the risk is fine if you're the application programmer that's controlling the updates (read as `INSERT`) to the table because you're the one building the app, and with the (big, I admit, in some enterprise environments) assumption no other app is using said table and doing its own updates/deletes to it. As for the `MY_ID` field, I believe it actually was a `NUMBER(*,0)` datatype, but I'll confirm on Monday. – vapcguy Aug 14 '16 at 14:12
  • @Ben Actually you were correct on the datatype being a string... checked it and it was `VARCHAR2`. Thanks. – vapcguy Aug 15 '16 at 15:59
  • If your primary key is using a sequence can't you just use `currval` and then you won't need the `max` sub-query; even though the max on an indexed column is apparently quite fast according to the Oracle experts. Only works for one row insert though, bulk insert of rows at a timestamp would need the `max(timestamp)` still. – Superdooperhero Jul 07 '20 at 12:06
  • 1
    Using `currval` would also [require you to be in the same session as the last use of `nextval`](https://docs.oracle.com/en/database/oracle/oracle-database/12.2/sqlrf/Sequence-Pseudocolumns.html#GUID-55228D7B-9CF1-4496-8524-3CD1DD4773FD) @Superdooperhero. If there's any chance of multiple sessions writing to a table or of a value getting dropped, i.e. through the failure of a statement, it's best to use the data-driven approach. – Ben Jul 07 '20 at 12:43
  • @Ben Didn't know that about `currval`. Can't someone else `insert` inbetween the use of `nextval` and `currval` and then cause an issue? – Superdooperhero Jul 07 '20 at 13:00
  • Yes, but I don't think that's the problem @super. If you're not actively inserting data into the table you can't get the actual current value without incrementing the sequence, meaning you don't know what the current value is. When you are inserting into a table and you can guarantee that's the only session then `currval` is definitely the way to go, but not as a generic solution. – Ben Jul 07 '20 at 17:32
29
SELECT * FROM (
    SELECT * FROM table_name ORDER BY sortable_column DESC
) WHERE ROWNUM = 1;
rtaft
  • 2,139
  • 1
  • 16
  • 32
  • will this actually work? i thought `rownum` is applied before and `order by` clause, meaning it'll ignore the sorting you're doing there. http://www.oracle.com/technetwork/issue-archive/2006/06-sep/o56asktom-086197.html – Alex Moore-Niemi Sep 16 '15 at 19:25
  • 4
    @AlexMoore-Niemi The sort in the parens happens first, so rownum works in this example. You will see this further down in the article you linked. Try testing it, and you should see it works. – Acroyear Oct 19 '15 at 22:46
  • I tried this and got the wrong ID. I have a table I built from another using an `insert into /*+ append */` with an `ORDER BY DESC` on a primary key ID column. When I built the table originally, it put the rows in the correct order from 1-75. When I run this query or do `select * from ( select a.*, max(pk) over () as max_pk from my_table a ) where pk = max_pk`, I get `9`. If I do `SELECT ID FROM MyTable WHERE ROWID IN (SELECT MAX(ROWID) FROM MyTable)`, I get the correct ID of `75`. – vapcguy Aug 12 '16 at 14:44
  • @vapcguy you don't have ROWNUM on the queries you have posted. Maybe you commented on the wrong example. – rtaft Aug 12 '16 at 17:47
  • @rtaft What I meant was if I run your query or the one from Ben's answer above yours, BOTH answers give me `9`. That's what I meant when I said: *When I run **this** query or do*... above. ***This*** was referring to running your query, rtaft. Sorry if that was unclear. – vapcguy Aug 12 '16 at 20:37
  • 2
    @vapcguy which tells me 9 is correct. Your ID's are likely strings and not numbers. – rtaft Aug 15 '16 at 14:39
  • @rtaft You were correct. I checked my field and it was `VARCHAR2`. Thanks. – vapcguy Aug 15 '16 at 15:58
13
select * from table_name ORDER BY primary_id DESC FETCH FIRST 1 ROWS ONLY;

That's the simplest one without doing sub queries

haidarvm
  • 611
  • 8
  • 17
3

The last row according to a strict total order over composite key K(k1, ..., kn):

SELECT  *
FROM    TableX AS o
WHERE   NOT EXISTS (
            SELECT  *
            FROM    TableX AS i
            WHERE   i.k1 > o.k1
                OR  (i.k1 = o.k1 AND i.k2 > o.k2)
                ...
                OR  (i.k1 = o.k1 AND i.k2 = o.k2 AND i.k3 = o.k3 AND ... AND i.kn > o.kn)
        )
;

Given the special case where K is simple (i.e. not composite), the above is shortened to:

SELECT  *
FROM    TableX AS o
WHERE   NOT EXISTS (
            SELECT  *
            FROM    TableX AS i
            WHERE   i.k1 > o.k1
        )
;

Note that for this query to return just one row the key must order without ties. If ties are allowed, this query will return all the rows tied with the greatest key.

rslemos
  • 2,454
  • 22
  • 32
  • 1
    No idea, this is correct. The language you use is quite dense though. Don't lose the accuracy, but the more people who understand your answer the better. – Ben Jun 13 '14 at 06:14
1

You can do it like this:

SELECT * FROM (SELECT your_table.your_field, versions_starttime
               FROM your_table
               VERSIONS BETWEEN TIMESTAMP MINVALUE AND MAXVALUE)
WHERE ROWNUM = 1;

Or:

SELECT your_field,ora_rowscn,scn_to_timestamp(ora_rowscn) from your_table WHERE ROWNUM = 1;
lpfx
  • 1,476
  • 4
  • 18
  • 40
  • 1
    The inner select does not guarantee order unless you specify it...the first row could be anything. – rtaft Aug 15 '16 at 14:35
0
SELECT * FROM 
  MY_TABLE
WHERE 
  <your filters>
ORDER BY PRIMARY_KEY DESC FETCH FIRST ROW ONLY
0
SELECT /*+ index_desc(t pk_index)*/
    pk
  FROM TBL t
 WHERE ROWNUM = 1;
  • 1
    I recommend that you don't post only code as answer, but also provide an explanation what your code does and how it solves the problem of the question. Answers with an explanation are usually more helpful and of better quality, and are more likely to attract upvotes. – Mark Rotteveel May 27 '23 at 11:23
0

Oracle 12.2.0 here,

By ordering by ROWNUM, we can get the last row of a table like that:

SELECT * FROM <TABLE_NAME> ORDER BY ROWNUM DESC FETCH FIRST ROW ONLY
Huntr
  • 59
  • 1
  • 5
-1
$sql = "INSERT INTO table_name( field1, field2 )  VALUES ('foo','bar') 
        RETURNING ID INTO :mylastid";
$stmt = oci_parse($db, $sql);
oci_bind_by_name($stmt, "mylastid", $last_id, 8, SQLT_INT);
oci_execute($stmt);

echo "last inserted id is:".$last_id;

Tip: you have to use your id column name in {your_id_col_name} below...

"RETURNING {your_id_col_name} INTO :mylastid"
melisozmen
  • 11
  • 1