How to retrieve the current value of an oracle sequence without increment it? - sql

Is there an SQL instruction to retrieve the value of a sequence that does not increment it.
Thanks.
EDIT AND CONCLUSION
As stated by Justin Cave It's not useful to try to "save" sequence number so
select a_seq.nextval from dual;
is good enough to check a sequence value.
I still keep Ollie answer as the good one because it answered the initial question. but ask yourself about the necessity of not modifying the sequence if you ever want to do it.

SELECT last_number
FROM all_sequences
WHERE sequence_owner = '<sequence owner>'
AND sequence_name = '<sequence_name>';
You can get a variety of sequence metadata from user_sequences, all_sequences and dba_sequences.
These views work across sessions.
EDIT:
If the sequence is in your default schema then:
SELECT last_number
FROM user_sequences
WHERE sequence_name = '<sequence_name>';
If you want all the metadata then:
SELECT *
FROM user_sequences
WHERE sequence_name = '<sequence_name>';
EDIT2:
A long winded way of doing it more reliably if your cache size is not 1 would be:
SELECT increment_by I
FROM user_sequences
WHERE sequence_name = 'SEQ';
I
-------
1
SELECT seq.nextval S
FROM dual;
S
-------
1234
-- Set the sequence to decrement by
-- the same as its original increment
ALTER SEQUENCE seq
INCREMENT BY -1;
Sequence altered.
SELECT seq.nextval S
FROM dual;
S
-------
1233
-- Reset the sequence to its original increment
ALTER SEQUENCE seq
INCREMENT BY 1;
Sequence altered.
Just beware that if others are using the sequence during this time - they (or you) may get
ORA-08004: sequence SEQ.NEXTVAL goes below the sequences MINVALUE and cannot be instantiated
Also, you might want to set the cache to NOCACHE prior to the resetting and then back to its original value afterwards to make sure you've not cached a lot of values.

select MY_SEQ_NAME.currval from DUAL;
Keep in mind that it only works if you ran select MY_SEQ_NAME.nextval from DUAL; in the current sessions.

The follows is often used:
select field_SQ.nextval from dual; -- it will increase the value by 1 for each run
select field_SQ.currval from DUAL;
However the following is able to change the sequence to what you expected. The 1 can be an integer (negative or positive)
alter sequence field_SQ increment by 1 minvalue 0

This is not an answer, really and I would have entered it as a comment had the question not been locked. This answers the question:
Why would you want it?
Assume you have a table with the sequence as the primary key and the sequence is generated by an insert trigger. If you wanted to have the sequence available for subsequent updates to the record, you need to have a way to extract that value.
In order to make sure you get the right one, you might want to wrap the INSERT and RonK's query in a transaction.
RonK's Query:
select MY_SEQ_NAME.currval from DUAL;
In the above scenario, RonK's caveat does not apply since the insert and update would happen in the same session.

I also tried to use CURRVAL, in my case to find out if some process inserted new rows to some table with that sequence as Primary Key. My assumption was that CURRVAL would be the fastest method. But a) CurrVal does not work, it will just get the old value because you are in another Oracle session, until you do a NEXTVAL in your own session. And b) a select max(PK) from TheTable is also very fast, probably because a PK is always indexed. Or select count(*) from TheTable. I am still experimenting, but both SELECTs seem fast.
I don't mind a gap in a sequence, but in my case I was thinking of polling a lot, and I would hate the idea of very large gaps. Especially if a simple SELECT would be just as fast.
Conclusion:
CURRVAL is pretty useless, as it does not detect NEXTVAL from another session, it only returns what you already knew from your previous NEXTVAL
SELECT MAX(...) FROM ... is a good solution, simple and fast, assuming your sequence is linked to that table

If your use case is that some backend code inserts a record, then the same code wants to retrieve the last insert id, without counting on any underlying data access library preset function to do this, then, as mentioned by others, you should just craft your SQL query using SEQ_MY_NAME.NEXTVAL for the column you want (usually the primary key), then just run statement SELECT SEQ_MY_NAME.CURRVAL FROM dual from the backend.
Remember, CURRVAL is only callable if NEXTVAL has been priorly invoked, which is all naturally done in the strategy above...

My original reply was factually incorrect and I'm glad it was removed. The code below will work under the following conditions a) you know that nobody else modified the sequence b) the sequence was modified by your session. In my case, I encountered a similar issue where I was calling a procedure which modified a value and I'm confident the assumption is true.
SELECT mysequence.CURRVAL INTO v_myvariable FROM DUAL;
Sadly, if you didn't modify the sequence in your session, I believe others are correct in stating that the NEXTVAL is the only way to go.

Related

How to use a temp sequence within a Postgresql function

I have some lines of SQL which will take a set of IDs from the same GROUP_ID that are not contiguous (ex. if some rows got deleted) and will make them contiguous again. I wanted to turn this into a function for reusability purposes. The lines work if executed individually but when I try to create the function I get the error
ERROR: relation "id_seq_temp" does not exist
LINE 10: UPDATE THINGS SET ID=nextval('id_se...
If I create a sequence outside of the function and use that sequence in the function instead then the function is created successfully (schema qualified or unqualified). However I felt like creating the temp sequence inside of the function rather than leaving it in the schema was a cleaner solution.
I have seen this question: Function shows error "relation my_table does not exist"
However, I'm using the public schema and schema qualifying the sequence with public. does not seem to help.
I've also seen this question: How to create a sql function using temp sequences and a SELECT on PostgreSQL8. I probably could use generate_series but this adds a lot of complexity that SERIES solves such as needing to know how big of a series to generate.
Here is my function, I anonymized some of the names - just in case there's a typo.
CREATE OR REPLACE FUNCTION reindex_ids(IN BIGINT) RETURNS VOID
LANGUAGE SQL
AS $$
CREATE TEMPORARY SEQUENCE id_seq_temp
MINVALUE 1
START WITH 1
INCREMENT BY 1;
ALTER SEQUENCE id_seq_temp RESTART;
UPDATE THINGS SET ID=ID+2000 WHERE GROUP_ID=$1;
UPDATE THINGS SET ID=nextval('id_seq_temp') WHERE GROUP_ID=$1;
$$;
Is it possible to use a sequence you create within a function later in the function?
Answer to question
The reason is that SQL functions (LANGUAGE sql) are parsed and planned as one. All objects used must exist before the function runs.
You can switch to PL/pgSQL, (LANGUAGE plpgsql) which plans each statement on demand. There you can create objects and use them in the next command.
See:
Why can PL/pgSQL functions have side effect, while SQL functions can't?
Since you are not returning anything, consider a PROCEDURE. (FUNCTION works, too.)
CREATE OR REPLACE PROCEDURE reindex_ids(IN bigint)
LANGUAGE plpgsql AS
$proc$
BEGIN
IF EXISTS ( SELECT FROM pg_catalog.pg_class
WHERE relname = 'id_seq_temp'
AND relnamespace = pg_my_temp_schema()
AND relkind = 'S') THEN
ALTER SEQUENCE id_seq_temp RESTART;
ELSE
CREATE TEMP SEQUENCE id_seq_temp;
END IF;
UPDATE things SET id = id + 2000 WHERE group_id = $1;
UPDATE things SET id = nextval('id_seq_temp') WHERE group_id = $1;
END
$proc$;
Call:
CALL reindex_ids(123);
This creates your temp sequence if it does not exist already.
If the sequence exists, it is reset. (Remember that temporary objects live for the duration of a session.)
In the unlikely event that some other object occupies the name, an exception is raised.
Alternative solutions
Solution 1
This usually works:
UPDATE things t
SET id = t1.new_id
FROM (
SELECT pk_id, row_number() OVER (ORDER BY id) AS new_id
FROM things
WHERE group_id = $1 -- your input here
) t1
WHERE t.pk_id = t1.pk_id;
And only updates each row once, so half the cost.
Replace pk_id with your PRIMARY KEY column, or any UNIQUE NOT NULL (combination of) column(s).
The trick is that the UPDATE typically processes rows according to the sort order of the subquery in the FROM clause. Updating in ascending order should never hit a duplicate key violation.
And the ORDER BY clause of the window function row_number() imposes that sort order on the resulting set. That's an undocumented implementation detail, so you might want to add an explicit ORDER BY to the subquery. But since the behavior of UPDATE is undocumented anyway, it still depends on an implementation detail.
You can wrap that into a plain SQL function.
Solution 2
Consider not doing what you are doing at all. Gaps in sequential numbers are typically expected and not a problem. Just live with it. See:
Serial numbers per group of rows for compound key

SELECT the last ID after INSERT in Oracle

I'm aware of using RETURNING in an INSERT statement but need a way to either have it returned as a result of the INSERT statement (or enclosing transaction) or be able to retrieve that value using SELECT. Is there a way to do this? I cannot use DBMS_OUTPUT. Thanks!
RETURNING clause is the easiest way to guarantee getting the value of the ID generated by an INSERT statement. Querying for max(id) is unreliable in a multi-user environment, especially if you're using RAC.
If the ID is populated from a sequence you can get the current value of the sequence by running this in the same session as the INSERT:
select your_sequence.currval from dual;
This means you need to know the name of the sequence, which isn't always obvious, even more so if you're using 12c Identity columns.
Basically, if you want the ID use RETURNING.

how to find the start with value of a db sequence

I was trying to find a way to know the start with value of given sequence in oracle sql.
Is there a way to do that?
Further, I wanted to list that with the below for the current max value as
select object_name.NEXTVAL
from DUAL
where object_name IN
(select object_name from all_objects where object_type like 'SEQUENCE')
but this results into syntax error.
select sequence_name,
LAST_NUMBER as NEXT_VAL,
MIN_VALUE as START_WITH
from dba_sequences
where sequence_name ='S';
P.S.
By the time of the result, chances are likely that the sequence could have been increment by another process
LAST_NUMBER will not be accurate if your sequence is defined with a CACHE > 1 ! (sequences would be generated in advance and cached for performance) So only NEXT_VAL can be used to find it. But calling NEXT_VAL even for printing purpose would increment it.
MIN_VALUE will be equal to START_WITH only if START_WITH is not specified while creation! And There's NO way to find it, except for checking the DDL.
Query to get the DDL of Sequence:
dbms_metadata.get_ddl('SEQUENCE', 'SEQ_NAME')
Credits to Nicholas Krasnov

postgresql -- Curval not working, using PHP PDO

So I'm trying to run some SQL here through PHP's PDO (which I don't believe should be the problem) like such:
INSERT INTO example (
d_id,
s_id
)
VALUES (
currval('d_id_seq'),
currval('s_id_seq')
);
I have two sequences called d_id_seq and s_id_sec (lets pretend I have a table named d and a table named s, and this sequence is a column called ID and serial type).
Now, obviously I'm doing this wrong, as I get an error about the sequence not being used in this session:
Object not in prerequisite state: 7 ERROR: currval of sequence "d_id_seq" is not yet defined in this session
So, how should I write this?
Problem can be solved via the following command:
SELECT last_value FROM d_id_seq;
Note that I'm using PostgreSQL 9.1.9, I do not know about other or older versions.
The error means you did not "use" the sequence in this session (postgres connection). For instance you did not do any INSERTs on the table d.
Perhaps you have a bug in your code and reconnect to postgres after each query ?
A more convenient way to do it is to use INSERT RETURNING on your INSERTs. Then you get the ids.
From the fine manual:
currval
Return the value most recently obtained by nextval for this sequence in the current session. (An error is reported if nextval has never been called for this sequence in this session.) Because this is returning a session-local value, it gives a predictable answer whether or not other sessions have executed nextval since the current session did.
You use currval to get the last value that was pulled out of the sequence in the current session. The usual pattern is to do an INSERT that uses a sequence and then you call currval to figure out what value the INSERT used. If you haven't called nextval with the sequence in question in the current session then there is nothing for currval to return.
Maybe you're actually looking for select max(id) from d and select max(id) from s:
INSERT INTO example (d_id, s_id)
SELECT MAX(d.id), MAX(s.id)
FROM d, s;
Or maybe you need to wrap your d and s inserts in a stored procedure that takes care of inserting in all three tables at once.

Oracle SQL: How to read-and-increment a field

I'm refactoring the data import procedure for an enterprise application and came across a snippet I'd like to find a better solution. When importing data we have to create a unique entity for each data set and there is a counter in a field to be used to assign this id sequentially. You read the field to get the next free id and increment it afterwards to prepare for the next time.
At the moment this is done in two steps in the original app, written in 'C':
SELECT idnext FROM mytable;
UPDATE mytable SET idnext = idnext + 1;
Obviously there is a race condition here, if multiple processes do the same thing.
Edit: Important corequisite: I can not touch the database/field definition, this rules out a sequence.
We are rewriting in perl, and I'd like to do the same thing, but better. An atomic solution would be nice. Unfortunately my SQL skills are limited, so I'm turning to collective wisdom :-)
In this particular case, a sequence is the right solution as mentioned. But if in some future situation you need to both update something and return a value in the same statement, you can use the RETURNING clause:
UPDATE atable SET foo = do_something_with(foo) RETURNING foo INTO ?
If the calling code is PL/SQL, replace the ? with a local PL/SQL variable; otherwise you can bind it as an output parameter in your program.
Edit: Since you mentioned Perl, something like this ought to work (untested):
my $sth = $dbh->prepare('UPDATE mytable SET idnext = idnext + 1 returning idnext into ?');
my $idnext;
$sth->bind_param_inout(1, \$idnext, 8);
$sth->execute; # now $idnext should contain the value
See DBI.
Why not use a sequence?
Create the sequence one time, using whatever START WITH value you want:
CREATE SEQUENCE mysequence
START WITH 1
MAXVALUE 999999999999999999999999999
MINVALUE 1
NOCYCLE
NOCACHE
NOORDER;
Then in your application code at runtime you can use this statement to get the next value:
SELECT mysequence.NEXTVAL
INTO idnext
FROM DUAL;
Update: Using a sequence would be the preferred method, but since you can't change the database then I agree that using RETURNING should work for your situation:
UPDATE mytable
SET idnext = idnext + 1
RETURNING idnext
INTO mylocalvariable;
Use SELECT FOR UPDATE statement. It guarantees mutually exclusive rights to the record :
"SELECT
FOR UPDATE;
A sequence will do the job, have a look at e.g. Oracle sequences