ORACLE 12C - ORA-02287: sequence number not allowed here - sql

I have created sequnce prac_seq:
CREATE SEQUENCE prac_seq
START WITH 300
INCREMENT BY 10;
And I want to use it the following:
UPDATE pracownicy
SET placa_dod = prac_seq.CURRVAL
WHERE id_prac = prac_seq.CURRVAL;
And then I get an error called:
Error report - SQL Error: ORA-02287: sequence number not allowed here
02287. 00000 - "sequence number not allowed here"
*Cause: The specified sequence number (CURRVAL or NEXTVAL) is inappropriate
here in the statement.
*Action: Remove the sequence number.
Why I cannot use sequence like above?

First, the documentation is quite clear on this point:
Restrictions on Sequence Values
You cannot use CURRVAL and NEXTVAL in the following constructs:
The WHERE clause of a SELECT statement
I do understand why NEXTVAL can't be used. You think the WHERE clause is being executed once, but it is (conceptually) executed for each row. Having a side-effecting operation in the WHERE introduces a host of problems. The results of the query using an index, for instance, would be different from the results not using an index.
My guess is that the reason is because both values could change while the query is running, and in some cases, the result of the query is not deterministic.

Related

Oracle Trigger Error : missing left parenthesis

can anyone help me out for this trigger.
CREATE or replace trigger check_limit_to_Y
AFTER INSERT OR UPDATE ON api_user for each row
WHEN EXISTS (SELECT '1' FROM profile b WHERE NEW.mvno_limit!='Y' and b.mvno_id = NEW.mvno_id)
BEGIN
raise_application_error (-20999,'MVNO LIMIT MUST BE SET Y FOR ANY REAL MVNO_ID');
END;
I got the error
Error report -
ORA-00906: missing left parenthesis
00906. 00000 - "missing left parenthesis"
*Cause:
*Action:
This is from the Oracle site: https://docs.oracle.com/cd/E11882_01/appdev.112/e25519/create_trigger.htm#LNPLS01374
WHEN (condition)
Specifies a SQL condition that the database evaluates for each row that the triggering statement affects. If the value of condition is TRUE for an affected row, then tps_body runs for that row; otherwise, tps_body does not run for that row. The triggering statement runs regardless of the value of condition.
The condition can contain correlation names (see referencing_clause ::=). In condition, do not put a colon (:) before the correlation name NEW, OLD, or PARENT (in this context, it is not a placeholder for a bind variable).
See Also:
Oracle Database SQL Language Reference for information about SQL conditions
Restrictions on WHEN (condition)
If you specify this clause, then you must also specify at least one of these timing points:
BEFORE EACH ROW
AFTER EACH ROW
INSTEAD OF EACH ROW
The condition cannot include a subquery or a PL/SQL expression (for example, an invocation of a user-defined function).
Your code doesn't seem to satisfy the last two restrictions (also: the entire condition after the WHEN keyword must be enclosed in parenthesis).
But, you seem to need to use the 'compound trigger syntax' because you want to process both inserts and updates. Check the documentation carefully.
Update: Try this:
CREATE TRIGGER t
BEFORE INSERT OR UPDATE ON api_user FOR EACH ROW
DECLARE CNT integer;
BEGIN
SELECT count(*) into CNT FROM profile b where :NEW.mvno_limit!='Y' and b.mvno_id = :NEW.mvno_id;
IF CNT > 0 THEN
RAISE_APPLICATION_ERROR(-20999,'MVNO LIMIT MUST BE SET Y FOR ANY REAL MVNO_ID');
END IF;
END;

How to store the result of select statement into the temporary table in Oracle?

We can write select column1,column2 into #temp from tableName in SQL Server. But I am unable to write the same query in an Oracle database.
I want to store the result of select/insert/delete/update or any result set into a local temporary table in oracle database. How I can do this?
I am executing below query in my Oracle sql developer tool:
select * into #temp
from bmi;
but I am getting the error as follow please help to find this error.
when I execute the same query in Microsoft SQL Server it get executed & #temp table get created which is not present in the database but it can hold the data for that particular session. so i want same scenario in ORACLE database.
ORA-00911: invalid character
00911. 00000 - "invalid character"
*Cause: identifiers may not start with any ASCII character other than
letters and numbers. $#_ are also allowed after the first
character. Identifiers enclosed by doublequotes may contain
any character other than a doublequote. Alternative quotes
(q'#...#') cannot use spaces, tabs, or carriage returns as
delimiters. For all other contexts, consult the SQL Language
Reference Manual.
*Action:
Error at Line: 1 Column: 15
I want to store the result of select/insert/delete/update or any result set into a local temporary table in oracle database,How I can Do This?
You can't. Oracle doesn't have local temporary tables, it doesn't work like that. But it doesn't need to. Oracle has a very different internal model from SQL Server which means a lot of SQL Server practices are unnecessary in Oracle. (To be fair SQL Server has neat things which Oracle doesn't, like ANSI 92 Joins for DML.)
The key insight is: you don't want to store the result of select/insert/delete/update or any result set into a local temporary table. That is something you had to do in T-SQL to achieve the end goal of implementing some business logic. But what you actually wanted to do in SQL Server and what you want to do in Oracle is write some code which delivers value to your organisation.
So, with that mindset in place, what do you need to do?
If you want to loop round a result set then perhaps a Cursor Loop is what you're looking for?
for rec in ( select * from some_table
where the_date = date '2018-02-01' )
loop
...
If you want to work on some data prior to inserting it into a data then perhaps you should use a PL/SQL collection:
type l_recs is table of some_table%rowtype;
But maybe you just need to understand Oracle's Transaction Management model. A lot of things are possible in pure SQL without any need for procedural framework.
Create temporary table :
create global temporary table
results_temp (column1, column2)
on commit preserve rows;
and then insert to it from your table:
insert into results_temp (column1, column2 )
SELECT column1,column2
FROM source_table
create global temporary table temp_table_name
on commit preserve rows as select column1,column2,columnN from your_table;

Validate dates before conversion, aka. ISDATE() equivalent

DB2 version is 9.7.0.7
I have a flat file, and need to validate fully prior to insert into a production table. For analysis, I've parsed it into a table where all columns are VARCHAR.
One of the tasks is to validate dates. I need to be able to locate the specific invalid dates, to report on the scope (frequency) and solution (reason).
I use ISDATE() in Sybase and SQL Server, which returns a 1 for a valid date, and a 0 for an invalid date. In Teradata, I left join to the SYS_CALENDAR table in the system catalog. It's been about 15 years since I've last been in a DB2 environment, but I believe analogs to either do not exist.
In this DB2 environment my role is limited to QA, meaning I cannot create T-SQL procedures or UDFs.
This thread is clever and makes me think there may be some Common Table Expression logic that could be employed in a query:
ISDATE equivalent of DB2
That one falls short as a solution, however, because it only considers format - the presence of an invalid (but properly formatted) date like '2016-04-31' or '2016-02-30' will raise an error and the query will return no rows.
I need to return all rows, identifying if each is valid or invalid (or just return the invalid rows for investigation, even) - so doing a CAST or CONVERT, or inserting into a formatted table in a test environment won't work.
Is there an analog to ISDATE(), SYS_CALENDAR, or another solution that gets to the same end product of a row-wise presentation of dates that can't be cast to DATE, prior to performing that conversion/insert?
You can do it with the PureXML extension as follows:
SELECT
XMLCAST(XMLQUERY('string($D) castable as xs:date' PASSING mycolumn as D ) AS INT)
FROM
mytable
which will return 1 or 0.

Mutating Table in Oracle 11 caused by a function

We've recently upgraded from Oracle 10 to Oracle 11.2. After upgrading, I started seeing a mutating table error caused by a function rather than a trigger (which I've never come across before). It's old code that worked in prior versions of Oracle.
Here's a scenario that will cause the error:
create table mutate (
x NUMBER,
y NUMBER
);
insert into mutate (x, y)
values (1,2);
insert into mutate (x, y)
values (3,4);
I've created two rows. Now, I'll double my rows by calling this statement:
insert into mutate (x, y)
select x + 1, y + 1
from mutate;
This isn't strictly necessary to duplicate the error, but it helps with my demonstration later. So the contents of the table now look like this:
X,Y
1,2
3,4
2,3
4,5
All is well. Now for the fun part:
create or replace function mutate_count
return PLS_INTEGER
is
v_dummy PLS_INTEGER;
begin
select count(*)
into v_dummy
from mutate;
return v_dummy;
end mutate_count;
/
I've created a function to query my table and return a count. Now, I'll combine that with an INSERT statement:
insert into mutate (x, y)
select x + 2, y + 2
from mutate
where mutate_count() = 4;
The result? This error:
ORA-04091: table MUTATE is mutating, trigger/function may not see it
ORA-06512: at "MUTATE_COUNT", line 6
So I know what causes the error, but I am curious as to the why. Isn't Oracle performing the SELECT, retrieving the result set, and then performing a bulk insert of those results? I would only expect a mutating table error if records were already being inserted before the query finished. But if Oracle did that, wouldn't the earlier statement:
insert into mutate (x, y)
select x + 1, y + 1
from mutate;
start an infinite loop?
UPDATE:
Through Jeffrey's link I found this in the Oracle docs:
By default, Oracle guarantees statement-level read consistency. The
set of data returned by a single query is consistent with respect to a
single point in time.
There's also a comment from the author in his post:
One could argue why Oracle doesn't ensure this 'statement-level read
consistency' for repeated function calls that appear inside a SQL
statement. It could be considered a bug as far as I'm concerned. But
this is the way it currently works.
Am I correct in assuming that this behavior has changed between Oracle versions 10 and 11?
Firstly,
insert into mutate (x, y)
select x + 1, y + 1
from mutate;
Does not start an infinite loop, because the query will not see the data that was inserted - only data that existed as of the start of the statement. The new rows will only be visible to subsequent statements.
This explains it quite well:
When Oracle steps out of the SQL-engine that's currently executing the
update statement, and invokes the function, then this function -- just
like an after row update trigger would -- sees the intermediate states
of EMP as they exist during execution of the update statement. This
implies that the return value of our function invocations heavily
depend on the order in which the rows happen to be updated.
Statement-Level Read Consistency and Transaction-Level Read Consistency".
From the manual:
"If a SELECT list contains a function, then the database applies
statement-level read consistency at the statement level for SQL run
within the PL/SQL function code, rather than at the parent SQL
level. For example, a function could access a table whose data is
changed and committed by another user. For each execution of the
SELECT in the function, a new read consistent snapshot is
established".
Both concepts are explained in the "Oracle® Database Concepts" :
http://download.oracle.com/docs/cd/B19306_01/server.102/b14220/consist.htm#sthref1955
->>> UPDATE
->>>*Section added after the OP was closed
The rule
The technical rule , well linked by Mr Kemp(#jeffrey-kemp) and well explained by Toon Koppelaars here, is reported in "Pl/Sql language reference - Controlling Side Effects of PL/SQL Subprograms"(your function violates RNDS reads no database state):
When invoked from an INSERT, UPDATE, or DELETE statement, the function
cannot query or modify any database tables modified by that statement.
If a function either queries or modifies a table, and a DML statement
on that table invokes the function, then ORA-04091 (mutating-table
error) occurs.
PL/SQL Functions that SQL Statements Can Invoke

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.