About dual table structure in Oracle SQL *Plus - sql

We know that "Dual" is a temporary table which exactly contains 1 column whose name is "dummy" which is of "varchar2(1)" type that has 1 single row. The value of that record is "X". The varchar2 has size of 1 which means it should not allow more than a single character.
Now my question is: If it is of varchar2 type, then why it can hold any datatype temporarily AND If we insert characters more than 1 (size), how this is possible for it (dual) to accept it ? Example:
SQL> desc dual
Name Null? Type
------------------------------- -------- ----
DUMMY VARCHAR2(1)
SQL> select sysdate from dual;
SYSDATE
---------
22-JAN-13
SQL> select 5*5 result from dual;
RESULT
---------
25
SQL> select 'Ankita' as "Name" from dual;
Name
------
Ankita
Clearly "SYSDATE" is of DATE type (not varchar2), "RESULT" is of NUMBER type (not varchar2). Also 'Ankita' is more than 1 single character, i.e., 6 characters. This should contradict the structure that varchar2 is holding only 1, and supporting 6 too..

The types and sizes of columns only come into play when you extract the data from them. It's true that if you select the dummy column, it will only give you what is in that column.
But, when you're selecting something unrelated to the column data, such as 5*5 or 'Ankita', they have no such restrictions.
I think your misunderstanding stems from your snippet:
why it can hold any datatype temporarily AND If we insert characters more than 1 (size), how this is possible for it (dual) to accept it ?
The truth is, the column doesn't accept it. Selecting 5*5 does not attempt to place that data into the table somewhere, it just evaluates it as-is. It's no different to having a table with:
users:
id
name
and performing:
select 42, 3.14159, id, name from users
For every row selected from, you will see the constant values 42 and 3.14159, alongside the data extracted from the two specified columns.

Related

How can I alter data type and datas in it, numbers separated with comma

I have table , that has field named AMOUNT , amount takes number, table has 1.4m records, i need to upgrade them all . I would like the change NUMBER to varchar and make look like amount datas comma separated , eg: 76543-> 76,543. How can I able to do it?
1 - Create the new column at the end of the table.
2 - Run an update to populate the new table column
(in this step for thousand seperataor look Thousand Seperator function in oracle? )
3 - Drop the old table column
4 - Re-name the new column to the original column name
i need to upgrade them all
Don't; if you have a numeric value then store it as a NUMBER.
I would like the change NUMBER to varchar and make look like amount datas comma separated , eg: 76543-> 76,543. How can I able to do it?
Just change how you are displaying the value rather than changing how you are storing the value.
If you have the table and data:
CREATE TABLE table_name ( amount NUMBER(12,0) );
INSERT INTO table_name ( amount ) VALUES ( 76543 );
If you want to do it in a SELECT statement then use TO_CHAR and include sufficient digits to format the largest number you can hold:
SELECT amount,
TO_CHAR(amount, 'FM999G999G999G990') AS formatted_amount
FROM table_name;
Outputs:
AMOUNT
FORMATTED_AMOUNT
76543
76,543
If you want to do that in the table then add a virtual column:
ALTER TABLE table_name
ADD formatted_amount VARCHAR2(16)
GENERATED ALWAYS AS ( TO_CHAR(amount, 'FM999G999G999G990') );
Then, after adding the virtual column:
SELECT * FROM table_name;
Outputs:
AMOUNT
FORMATTED_AMOUNT
76543
76,543
db<>fiddle here
You can use to_char():
select to_char(col, 'FM999,990;')

How to lookup based on ranged values

I have a table like:
id name
001to005 ABC
006to210 PQR
211to300 XYZ
This is not the final table i can make it any how i want...so i would like to lookup on this data on id and extract name like if id is in range of 001-005 then ABC and if id is in range 006-010 .... then name XYZ.
My approach would be, store id as regular expression in table like this:
id name
[0][0][1-5] ABC
[0-2][0-9][0-9] PQR
[2-3][0-9][0-9] XYZ
and then query:
select * from table where '004' ~ id
This query will return ABC which is correct but when range gets bigger my input value can lie on both 2nd and 3rd row.
For Eg:
select * from table where '299' ~ id
this query will result in 2 rows,so my question is what reg exp to use to make it more restrictive or is there any other approach to solve this:
Do not store regular expressions for simple ranges, that would be extremely expensive and cannot use an index: every single expression in the table would have to be evaluated for every query to satisfy conditions.
You could use range types like #a_horse commented. But while you don't need the added functionality for range types this simple layout is smaller and faster:
CREATE TABLE tbl (
id_lo int NOT NULL
, id_hi int NOT NULL
, name text NOT NULL
);
INSERT INTO t VALUES
( 1, 5, 'ABC')
, ( 6, 210, 'PQR')
, (211, 300, 'XYZ');
CREATE UNIQUE INDEX foo ON t (id_lo, id_hi DESC);
Two integer occupy 8 bytes, int4range value occupies 17 bytes. Size matters in tables and indexes.
Query:
SELECT * FROM tbl
WHERE 4 BETWEEN id_lo AND id_hi;
Lower (id_lo) and upper (id_hi) bounds are included in the range like your sample data suggests.
Note that range types exclude the upper bound by default.
Also assuming that leading zeros are insignificant, so we can operate with plain integer.
Related:
PostgreSQL daterange not using index correctly
Optimizing queries on a range of timestamps (two columns)
Find overlapping date ranges in PostgreSQL
To enforce distinct ranges in the table:
Preventing adjacent/overlapping entries with EXCLUDE in PostgreSQL
You still don't need a range type in the table for this:
Postgres: How to find nearest tsrange from timestamp outside of ranges?

How to create alphanumeric sequence using date and sequence number

I want to create an alphanumeric sequence in oracle. Table name is rel_details it consists of four columns.
rel_id
rel_name
rel_modified_date
rel_desc
In rel_id i want to generate ID like REL230420151001
REL is a string ,
23042015 is today's date,
1001 is a starting number.
How to create this type sequence.
If you are on 12c, then here is one way using IDENTITY column and VIRTUAL column.
Identity column was introduced in version 12c, and virtual column was introduced in version 11g.
SQL> CREATE TABLE t
2 (
3 ID NUMBER GENERATED ALWAYS AS IDENTITY
4 START WITH 1000 INCREMENT BY 1,
5 text VARCHAR2(50),
6 dt DATE DEFAULT SYSDATE,
7 my_text varchar2(1000) GENERATED ALWAYS AS (text||to_char(dt, 'DDMMYYYY')||ID) VIRTUAL
8 );
Table created.
SQL>
SQL> INSERT INTO t(text) VALUES ('REL');
1 row created.
SQL>
SQL> SELECT text, my_text FROM t;
TEXT MY_TEXT
----- ------------------------------
REL REL230420151000
SQL>
I created identity column to start with 1000, you could customize the way you want.
There is one small trick about the VIRTUAL column. You will have to explicitly cast it as varchar2 with fixed size, else the implicit conversion will make it up to maximum size. See this for more details Concatenating numbers in virtual column expression throws ORA-12899: value too large for column
if I were you, I wouldn't bother storing such a sequence in a column; I would store the columns containing the relevant information separately and then either have a virtual column that concatenates them together or do the concatenating in a view.
Check this , you may not able to create seq , but you can use select as below.
create sequence mysec
minvalue 0
start with 10001
increment by 1
nocache;
select 'REL'||to_char(sysdate,'DDMMYYYY')||mysec.nextval from dual;

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;

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

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