pseudo columns & DUAL table - what do they actually mean? - sql

Dual table is used to select pseudo columns.
it has one row and one column DUMMY which has a value X.
I have two questions
What actually does a pseudo
column mean?
How is the dual able to give the
value for example:
select sysdate from dual
will result in current datetime. How is this possible?

A pseudo-column is a function which returns a system generated value. sysdate is a function which returns the current datetime; rownum is a pseudo-column that returns the row number in a result set.
The nomenclature dates from the earlier days of Oracle, before we had PL/SQL. It just means that we can use these functions in the projection of a SELECT statement, just like the columns of a table. Nowadays we can write our own functions and use them in SQL statements without blinking, and so the phrase "pseudo-column" is a tad confusing.
The feature which distinguishes a function from a pseudo-column is that the pseudo-column returns a different value for each row in the resultset whereas a function returns the same value (unless some column in the table is passed as a parameter to derive the value).
Dual is another venerable slice of Oracle history. It is a table which contains one row, and which the database knows contains one row. So the select statement you quote is just saying "give me the current datetime". It is functionally equivalent to
select sysdate
from emp
where rownum = 1
/
In PL/SQL the select from dual is nugatory. We can just code this:
l_date := sysdate;
One common use for DUAL used to be getting the next value of a sequence in a trigger. Since 11g we can do ...
:new.id := my_seq.nextval;
Under the covers this still executes select my_seq.nextval into :new.id from dual;

2. How does selecting from DUAL give the system time?
SQL has a number of built-in functions which don't need parentheses after them to invoke them. One such function in Oracle is SYSDATE.
Remember, if you have a table, a SELECT statement with no restriction condition (WHERE clause) normally returns one row of data for each row in the table. So, given a table:
CREATE TABLE Ex1(Dummy CHAR(10) NOT NULL);
INSERT INTO Ex1 VALUES('Abacus');
INSERT INTO Ex1 VALUES('Sedentary');
INSERT INTO Ex1 VALUES('Caucasus');
Running the SELECT statement:
SELECT Dummy FROM Ex1;
will return 3 rows. Now, suppose I write the statement as:
SELECT 'ABC', Dummy, SYSDATE FROM Ex1;
This will also return 3 rows:
ABC, Abacus, 2010-03-03
ABC, Sedentary, 2010-03-03
ABC, Caucasus, 2010-03-03
If I omit the Dummy column:
SELECT 'ABC', SYSDATE FROM Ex1;
I get:
ABC, 2010-03-03
ABC, 2010-03-03
ABC, 2010-03-03
And if I omit the string literal:
SELECT SYSDATE FROM Ex1;
I get:
2010-03-03
2010-03-03
2010-03-03
And I delete two rows and rerun the query, I get:
DELETE FROM Ex1 WHERE Dummy > 'B';
SELECT SYSDATE FROM Ex1;
I get:
2010-03-03
Because there's just the one row of data in the table Ex1.
Nominally, I could do:
UPDATE Ex1 SET Dummy = 'X';
RENAME TABLE Ex1 AS Dual;
Of course, you can't do that - I'm not sure whether Oracle supports a RENAME TABLE statement and it probably wouldn't let you rename your table so it could be confused with the built-in DUAL table. But conceptually, the table Ex1 with a single row in it is isomorphic with DUAL.
1. What is a Pseudo-Column?
Unless Oracle has a specific special meaning for the term, then a pseudo-column is a column that appears to be part of the table but that is not actually stored as data in the table. A classic example is a row number:
SELECT ROWNUM, * FROM SomeTable
The output appears to have a column ROWNUM (ROWID in Informix, with which I'm most familiar) but that is not directly stored in the DBMS. Different DBMS have other different pseudo-columns, for different purposes.
It is sometimes difficult to distinguish between a pseudo-column and a function.

A pseudo-column is an Oracle assigned value (pseudo-field) but not stored on disk.
Pseudocolumns are not actual columns in a table but they behave like columns.
For example, you can select values from a pseudocolumn. However, you cannot insert into, update, or delete from a pseudocolumn. Also note that pseudocolumns are allowed in SQL statements, but not in procedural statements.
NOTE:- pseudocolumns are allowed in SQL statements, but not in procedural statements.
SQL> SELECT sysdate, systimestamp FROM dual;
SYSDATE SYSTIMESTAMP
13-DEC-07 13-DEC-07 10.02.31.956842 AM +02:00

pseudo Columns : ROWID , ROWNUM , LEVEL

Related

What is dual in mybatis?

I am just trying to understand some old code without help which uses this mybatis statement. I did not find any proper answer. So in the below statement of myBatis what is dual?
SELECT mySeq.currVal FROM dual
Wrong question, I'd say. DUAL is related to Oracle, not MyBatis.
It is a single-row, single-column table, owned by SYS. It looks like this:
SQL> desc dual
Name Null? Type
----------------------------- -------- --------------------
DUMMY VARCHAR2(1)
SQL> select * from dual;
DUMMY
-----
X
SQL>
To users, it is available as a public synonym:
SQL> select object_name, owner, object_type
2 from all_objects
3 where object_name = 'DUAL';
OBJECT_NAME OWNER OBJECT_TYPE
-------------------- -------------------- -------------------
DUAL SYS TABLE
DUAL PUBLIC SYNONYM
SQL>
How was it created and why does it have that strange name, "dual"?
Charles Weiss:
I created the DUAL table as an underlying object in the Oracle Data Dictionary. It was never meant to be seen itself, but instead used inside a view that was expected to be queried. The idea was that you could do a JOIN to the DUAL table and create two rows in the result for every one row in your table. Then, by using GROUP BY, the resulting join could be summarized to show the amount of storage for the DATA extent and for the INDEX extent(s). The name, DUAL, seemed apt for the process of creating a pair of rows from just one.
Where do we use it? Everywhere!!!
In Oracle's SQL, you have to select from something, and that's frequently dual. For example, which date is it today, or who am I?
SQL> select sysdate from dual;
SYSDATE
-------------------
04.08.2022 11:43:08
SQL> select 'My name is Littlefoot' who_am_i from dual;
WHO_AM_I
---------------------
My name is Littlefoot
SQL>
Or, in your case, you're selecting sequence's current value. Here's a demo:
Create the sequence:
SQL> create sequence myseq;
Sequence created.
Your query (won't work, though - see error description):
SQL> select myseq.currval from dual;
select myseq.currval from dual
*
ERROR at line 1:
ORA-08002: sequence MYSEQ.CURRVAL is not yet defined in this session
OK, so let's first fetch nextval:
SQL> select myseq.nextval from dual;
NEXTVAL
----------
1
Now we also have the currval:
SQL> select myseq.currval from dual;
CURRVAL
----------
1
SQL>
From the Oracle documentation:
DUAL is a table automatically created by Oracle Database along with the data dictionary. DUAL is in the schema of the user SYS but is accessible by the name DUAL to all users. It has one column, DUMMY, defined to be VARCHAR2(1), and contains one row with a value X. Selecting from the DUAL table is useful for computing a constant expression with the SELECT statement. Because DUAL has only one row, the constant is returned only once.
So that statement is querying the current value of the sequence mySeq, within your session (so there must have been a call to nextval preceding it for it to work).
In Oracle SQL you always have to select from something; dual is useful when there is no actual table you want to get data from - either for a constant or literal or function (select sysadte from dual) etc.

Does Oracle allow an SQL INSERT INTO using a SELECT statement for VALUES if the destination table has an GENERATE ALWAYS AS IDENTITY COLUMN

I am trying to insert rows into an Oracle 19c table that we recently added a GENERATED ALWAYS AS IDENTITY column (column name is "ID"). The column should auto-increment and not need to be specified explicitly in an INSERT statement. Typical INSERT statements work - i.e. INSERT INTO table_name (field1,field2) VALUES ('f1', 'f2'). (merely an example). The ID field increments when typical INSERT is executed. But the query below, that was working before the addition of the IDENTITY COLUMN, is now not working and returning the error: ORA-00947: not enough values.
The field counts are identical with the exception of not including the new ID IDENTITY field, which I am expecting to auto-increment. Is this statement not allowed with an IDENTITY column?
Is the INSERT INTO statement, using a SELECT from another table, not allowing this and producing the error?
INSERT INTO T.AUDIT
(SELECT r.IDENTIFIER, r.SERIAL, r.NODE, r.NODEALIAS, r.MANAGER, r.AGENT, r.ALERTGROUP,
r.ALERTKEY, r.SEVERITY, r.SUMMARY, r.LASTMODIFIED, r.FIRSTOCCURRENCE, r.LASTOCCURRENCE,
r.POLL, r.TYPE, r.TALLY, r.CLASS, r.LOCATION, r.OWNERUID, r.OWNERGID, r.ACKNOWLEDGED,
r.EVENTID, r.DELETEDAT, r.ORIGINALSEVERITY, r.CATEGORY, r.SITEID, r.SITENAME, r.DURATION,
r.ACTIVECLEARCHANGE, r.NETWORK, r.EXTENDEDATTR, r.SERVERNAME, r.SERVERSERIAL, r.PROBESUBSECONDID
FROM R.STATUS r
JOIN
(SELECT SERVERSERIAL, MAX(LASTOCCURRENCE) as maxlast
FROM T.AUDIT
GROUP BY SERVERSERIAL) gla
ON r.SERVERSERIAL = gla.SERVERSERIAL
WHERE (r.LASTOCCURRENCE > SYSDATE - (1/1440)*5 AND gla.maxlast < r.LASTOCCURRENCE)
) )
Thanks for any help.
Yes, it does; your example insert
INSERT INTO table_name (field1,field2) VALUES ('f1', 'f2')
would also work as
INSERT INTO table_name (field1,field2) SELECT 'f1', 'f2' FROM DUAL
db<>fiddle demo
Your problematic real insert statement is not specifying the target column list, so when it used to work it was relying on the columns in the table (and their data types) matching the results of the query. (This is similar to relying on select *, and potentially problematic for some of the same reasons.)
Your query selects 34 values, so your table had 34 columns. You have now added a 35th column to the table, your new ID column. You know that you don't want to insert directly into that column, but in general Oracle doesn't, at least at the point it's comparing the query with the table columns. The table has 35 columns, so as you haven't said otherwise as part of the statement, it is expecting 35 values in the select list.
There's no way for Oracle to know which of the 35 columns you're skipping. Arguably it could guess based on the identity column, but that would be more work and inconsistent, and it's not unreasonable for it to insist you do the work to make sure it's right. It's expecting 35 values, it sees 34, so it throws an error saying there are not enough values - which is true.
Your question sort of implies you think Oracle might be doing something special to prevent the insert ... select ... syntax if there is an identity column, but in facts it's the opposite - it isn't doing anything special, and it's reporting the column/value count mismatch as it usually would.
So, you have to list the columns you are populating - you can't automatically skip one. So you statement needs to be:
INSERT INTO T.AUDIT (IDENTIFIER, SERIAL, NODE, ..., PROBESUBSECONDID)
SELECT r.IDENTIFIER, r.SERIAL, r.NODE, ..., r.PROBESUBSECONDID
FROM ...
using the actual column names of course if they differ from the query column names.
If you can't change that insert statement then you could make the ID column invisible; but then you would have to specify it explicitly in queries, as select * won't see it - but then you shouldn't rely on * anyway.
db<>fiddle

JOOQ: extract value from Field

I have a table with PK and another column for other id. In some cases i need to insert record with equal values in both columns. For primary key values i'm using sequence, which gives a Field<Long> from Sequences.MY_SEQ.nextval().
How can i extract value from a Field<Long> for guaranteed insert same ids in both columns? Using Field<Long> in insert clause generates 2 different ids in columns.
Here is the solution:
Long id = dsl.select(Sequences.MY_SEQ.nextval()).fetchOne().value1();
Your own solution works, of course, but it will generate two round trips to the database. One for fetching the sequence value and another one for the insert. If that's not a problem, perfect. Otherwise, you can still do it in one single query using INSERT .. SELECT:
In SQL:
(using Oracle syntax. Your SQL syntax may vary...)
INSERT INTO my_table (col1, col2, val)
SELECT t.id, t.id, 'abc'
FROM (
SELECT my_seq.nextval AS id
FROM dual
) t
With jOOQ
Table<?> t = table(select(MY_SEQ.nextval().as("id"))).as("t");
dsl.insertInto(MY_TABLE)
.columns(MY_TABLE.COL1, MY_TABLE.COL2, MY_TABLE.VAL)
.select(
select(t.field("id"), t.field("id"), val("abc"))
.from(t))
.execute();

Oracle: copy row while updating one field for table with many columns

Is there a way to generically copy a row, in particular WITHOUT specifying all the columns.
In my situatoin I have a large table where I would like to copy all the columns except the ID and one other column. In fact data is copied from year to year at the start of the year. The table has 50+ columns so it would be more flexible and robust to change in schema if I did not have to specify all the columns.
This is closely related to the question : copy row while updating one field
In that question Kevin Cline's comment essentially asks my question, but no solution was actually provided for this more general case.
EDIT to provide more detail as requested, here is an example of what is needed:
-- setup test table
create table my_table(pk, v1,v2,v3,v4 ... v50) as
select 17 pk, 1 v1,2 v2,3 v3... 50 v50 from dual;
On the above table copy the row and set pk to 18 and v2 to 10.
An easy way to do this is an anonymous PL/SQL block and the usage of ROWTYPE:
-- setup test table
create table my_table(pk, value) as
select 17 pk, 'abc' value from dual;
declare
l_data my_table%rowtype;
begin
-- fetch the row we want to copy
select * into l_data from my_table tbl where tbl.pk = 17;
-- update all fields that need to change
l_data.pk := 18;
-- note the lack of parens around l_data in the next line
insert into my_table values l_data;
end;

Column Name of PL/SQL Table-Type

I have a PL/SQL table-type/associative-array in Oracle
CREATE OR REPLACE TYPE STRING_TBL IS TABLE OF VARCHAR2(3000);
on which I can perform select queries like this
...
strings STRING_TBL;
...
SELECT * FROM TABLE(strings);
But what is the column name of that one column in the result set? That is, what would I need to put into <name_of_only_column> to make this query work:
SELECT rowid, p.<name_of_only_column>
FROM TABLE(strings) p;
Alternatively, if I can't do that, is there any way to alias that one column through a subselect in Oracle?
But what is the column name of that one column in the result set?
Pseudocolumn named column_value.
-- nested table schema object type
create type t_list as table of number
select column_value
from table(T_List(1,2,3))
COLUMN_VALUE
------------
1
2
3
Note: In this case it's not allowed to use rowid pseudocolumn, simply because in-memory nested table does not have one.
It's column_value, the following should work:
SELECT rowid, p.column_value
FROM TABLE(strings) p;