Insert unique values after addition of a column in a table - sql

We have a table with two columns and have added another column recently (named sequence_no) , Is there a way to insert unique values like , 1,2,3 for every row in the table ?
eg
table name : test
desc test
name varchar2
value varchar2
--> n_seq_no number
select * from test
Name value n_Seq_no
test1 100
test2 200
test3 300
test4 500
The table already had name, and value as the columns of the table, I need to add unique values for the n_Seq_no column with the existing data,
Output format:
select * from test
Name value n_Seq_no
test1 100 1
test2 200 2
test3 300 3
test4 500 4
and so on for all the rows in table.

You could simply set the new column as ROWNUM.
Something like,
SQL> CREATE TABLE t(
2 A NUMBER,
3 b NUMBER);
Table created.
SQL>
SQL> INSERT INTO t(A) VALUES(100);
1 row created.
SQL> INSERT INTO t(A) VALUES(200);
1 row created.
SQL> INSERT INTO t(A) VALUES(300);
1 row created.
SQL>
SQL> SELECT * FROM t;
A B
---------- ----------
100
200
300
SQL>
SQL> UPDATE t SET b = ROWNUM;
3 rows updated.
SQL> SELECT * FROM T;
A B
---------- ----------
100 1
200 2
300 3
SQL>
If you are on 12c, you could use an IDENTITY COLUMN.

Assuming that your table is really big it's better to recreate and repopulate:
rename test to old_test;
create table new_test
as
select t.*, rownum as n_seq_no
from old_test t
order by value;
Don't forget to migrate grants, indexes, triggers and etc if any.
UPDATE: ordering is optional. It is required only if you want to assign n_seq_no value using some predefine ordering.

Related

how to LOOKUP on a TABLE per SET of ID and END_DTTM_FLG

I hope you could help me!
The requirement is to clean-up/delete the INACTIVE records from TABLE_2.
INACTIVE record criteria is when ID records have END_DTTM IS NOT NULL and there’s NO partner record of the same ID with END_DTTM IS NULL.
Meaning if an ID has NULL END_DTTM, that ID is still ACTIVE.
I made a query first to get the INACTIVE records then I'll just add the DELETE syntax when I got the correct data.
SAMPLE QUERY:
WITH SRC1 AS (
SELECT ID
FROM TABLE_1
WHERE END_DTTM IS NOT NULL
)
SELECT ID
FROM TABLE_2
WHERE TABLE_2.ID = SRC1.ID
;
Please check attached sample data.
Issue is that, the query still gets the ACTIVE records (ID with END_DTTM IS NULL) because there's the same ID with END_DTTM IS NOT NULL.
I have also tried to create a END_DTTM_FLG --> case when END_DTTM IS NOT NULL THEN 1 ELSE 2 END END_DTTM_FLG. My idea is to lookup the ID per set, if ID has END_DTTM_FLG =2 then the ID corresponding to that should not be fetched.
How can I lookup the TABLE_1 and check the ID per SET of END_DTTM_FLG?
Please help me solve this problem!
Thank you! ^_^
This is how I understood it.
For sample data (altering session just to set date format; you don't have to do that):
SQL> alter session set nls_date_format = 'dd.mm.yyyy';
Session altered.
SQL> select * from table_1 order by id, end_dttm nulls last;
ID END_DTTM
---------- ----------
4157061694 21.06.2021
4157061694 21.06.2021
4157061694 23.06.2021
4157061694
4157064676 19.01.2021
4157064676 20.01.2021
6 rows selected.
SQL> select * from table_2;
ID
----------
4157061694
4157064676
Delete rows from table_2 whose IDs are "inactive" in table_1:
SQL> delete from table_2 b
2 where not exists (select null
3 from table_1 a
4 where a.id = b.id
5 and a.end_dttm is null
6 );
1 row deleted.
Result:
SQL> select * from table_2;
ID
----------
4157061694
SQL>
Alternatively:
SQL> rollback;
Rollback complete.
SQL> delete from table_2 b
2 where b.id not in (select distinct a.id
3 from table_1 a
4 where a.end_dttm is null
5 );
1 row deleted.
SQL> select * from table_2;
ID
----------
4157061694
SQL>

Update values of one table from other table

For Example I have 2 tables
Table 1:
id number name
-----------------------
1 1684 abc
2 9666 pqr
3 1234 adf
Table 2:
id number name
-----------------------
1 9109 xyz
2 9564 pqr
How do i get output like this?
Table 1:
id number name
-----------------------
1 9109 xyz
2 9564 pqr
3 1234 adf
I want to merge table 2 in table.
Also if the column-"name" value is updated/changed from this merge, I need to update status column and i need to call a procedure of smtp mail, how can i handle this?
The status column has different status transitions(eg: x->y, a->b).
and if the record is not in table1 it should be inserted.
These all operations are inside a procedure used in batch job.
Please advice me with this.
Thank you in advance for help.
There are different ways to do this, say, using MERGE statement, but most simple is
Connected to Oracle Database 12c Enterprise Edition Release 12.1.0.2.0
SQL> create table t$1 ("id" integer, "number" integer, "name" varchar2(3));
Table created
SQL> create table t$2 ("id" integer, "number" integer, "name" varchar2(3));
Table created
SQL> insert into t$1 values (1, 1684, 'abc');
1 row inserted
SQL> insert into t$1 values (2, 9666, 'pqr');
1 row inserted
SQL> insert into t$1 values (3, 1234, 'adf');
1 row inserted
SQL> insert into t$2 values (1, 9109, 'xyz');
1 row inserted
SQL> insert into t$2 values (2, 9564, 'pqr');
1 row inserted
SQL> update t$1 set
2 ("number", "name") = (select "number", "name" from t$2 where t$2."id" = t$1."id")
3 where
4 "id" in (select "id" from t$2);
2 rows updated
SQL> select * from t$1;
id number name
-------- -------- --------
1 9109 xyz
2 9564 pqr
3 1234 adf
You can use MERGE to do it very simply:
MERGE INTO table1 dst
USING table2 src
ON ( src.id = dst.id )
WHEN MATCHED THEN
UPDATE SET number = src.number,
name = src.name;

SQL create empty table and fill some rows with info

Is it possible to create an empty table with a fixed number of rows (the number is defined in another table) and defined columns(col1 and col2) and then replace some of the rows in this empty table with the ones I get from a select?
I want the final structure to be like this :
col1 col2
----------------
empty empty
val11 val21
val12 val22
empty empty
val13 val23
empty empty
where I take val11, val21.... from another select. I want this table with empty values to be only local aka not create it anywhere else.
You can create a table with a variable number (say 5) of rows with empty fields;
about the ordering, you can not use any internal ordering, so you need to add a field to hold the position of the row.
For example:
SQL> create table tab_5(id number, col1 varchar2(50), col2 varchar2(50))
2 /
Table created.
SQL> insert into tab_5
2 select level, null, null
3 from dual
4 connect by level <= 5
5 /
5 rows created.
SQL> select * from tab_5 order by id;
ID COL1 COL2
---------- -------------------- --------------------
1
2
3
4
5
If you need to update some records in interlaced way, you can rely on your id
SQL> update tab_5
2 set col1 = 'something',
3 col2 = 'something else'
4 where mod(id, 2) = 0;
2 rows updated.
SQL> select * from tab_5 order by id;
ID COL1 COL2
---------- -------------------- --------------------
1
2 something something else
3
4 something something else
5
SQL>
I don't see any point in inserting a fixed number of empty records only to "replace" them later. Rather, a better idea would be to just INSERT the records you want and then add some null records afterwards.
First INSERT the records you want from the other table:
INSERT INTO yourTable (col1, col2)
SELECT col1, col2
FROM anotherTable
Then INSERT the "empty" records:
INSERT ALL
INTO yourTable (col1, col2) VALUES (null, null)
INTO yourTable (col1, col2) VALUES (null, null)
INTO yourTable (col1, col2) VALUES (null, null)
...
SELECT 1 FROM DUAL;

I am trying to return a single row per record, but am receiving multiple

I am trying to query a table. The table contains a barcode and activity related to the barcode, specifically when it is moved and where. I am trying to return one record per barcode containing the last date and time it was moved along with the location. I currently have MAX around both move date and time. Here is a sample of the duplicated rows.
BarCode Location Date Time
000055279 1 EWC 03-APR-14 12:30:44
000055279 1 G-T 05-AUG-14 08:16:10
000055279 1 TBD 20-AUG-14 08:32:14
I only want the last row in this example (000055279 1 TBD 20-AUG-14 08:32:14). Current query is basically:
SELECT DISTINCT "BarCode", "Location", MAX("Date"), MAX("Time")
FROM TABLE1
As soon as I add the Location, the duplicates happen.
Thanks in advance.
There are several options for this. One is to use row_number():
select *
from (
select barcode, location, date, time,
row_number() over (partition by barcode order by date desc, time desc) rn
from table1
) t
where rn = 1
Based on your idea, but fixing a flaw in the design, you could (re)generate the correct datetime column using a CTE, and then query that view:
WITH V AS (SELECT "BarCode", "Location",
TO_DATE("Date"|| ' ' || "Time", 'DD-MON-YY HH:MI:SS') "datetime"
FROM TABLE1)
SELECT "BarCode", "Location", MAX("datetime")
FROM V
GROUP BY "BarCode", "Location"
Untested. Beware of typos!
That being said, as of myself, I would push toward having only one DATE column in the table. If it is not feasible for some reason, maybe you could propose to add a virtual column instead:
ALTER TABLE TABLE1
ADD ("datetime" DATE GENERATED ALWAYS
AS (TO_DATE("Date"|| ' ' || "Time", 'DD-MON-YY HH:MI:SS')) VIRTUAL);
The Oracle ROW_NUMBER analytic function is what you need. The Oracle documentation explains it well and here's a worked example using your data plus some additional barcodes to illustrate it working across a more realistic data set. I'm concatenating the date & time columns and using TO_DATE to get a proper date. Since these are reserved words they are enclosed in double quotes.
SQL> create table barcode_activity (
2 barcode varchar2(20),
3 location varchar2(10),
4 "date" varchar2(10),
5 "time" varchar2(10)
6 );
Table created.
SQL>
SQL> insert into barcode_activity
2 values
3 ( '000055279 1','EWC', '03-APR-14','12:30:44')
4
SQL> insert into barcode_activity
2 values
3 ( '000055279 1','G-T', '05-APR-14','08:16:10');
1 row created.
SQL>
SQL> insert into barcode_activity
2 values
3 ( '000055279 1','TBD', '20-APR-14', '08:32:14');
1 row created.
SQL>
SQL> insert into barcode_activity
2 values
3 ( '000009999 1','TBD', '20-APR-14', '07:42:32');
1 row created.
SQL>
SQL> insert into barcode_activity
2 values
3 ( '000001234 1','TBD', '29-APR-14', '17:22:18');
1 row created.
SQL>
SQL> insert into barcode_activity
2 values
3 ( '000001234 1','TBD', '29-APR-14', '17:22:18');
1 row created.
SQL>
SQL> insert into barcode_activity
2 values
3 ( '000001234 1','TBD', '29-APR-14', '17:22:18');
1 row created.
SQL> commit;
Commit complete.
SQL>
SQL> SELECT * FROM barcode_activity ORDER BY 1,2,3;
BARCODE LOCATION date time
-------------------- ---------- ---------- ----------
000001234 1 TBD 29-APR-14 17:22:18
000001234 1 TBD 29-APR-14 17:22:18
000001234 1 TBD 29-APR-14 17:22:18
000009999 1 TBD 20-APR-14 07:42:32
000055279 1 G-T 05-APR-14 08:16:10
000055279 1 TBD 20-APR-14 08:32:14
6 rows selected.
SQL>
SQL> /* rank solution
SQL> this will return one row
SQL> for all barcodes in the table where there are collisions in rank th
SQL> multiple rows are returned
SQL> */
SQL> SELECT barcode, location, "date", "time"
2 FROM
3 (SELECT barcode,
4 location,
5 "date",
6 "time",
7 RANK() OVER (
8 PARTITION BY barcode
9 ORDER BY TO_DATE("date"||' '||"time",'DD-MON-YYYY HH24:MI:SS') DESC
10 ) AS activity_order
11 FROM barcode_activity)
12 WHERE activity_order = 1;
BARCODE LOCATION date time
-------------------- ---------- ---------- ----------
000001234 1 TBD 29-APR-14 17:22:18
000001234 1 TBD 29-APR-14 17:22:18
000001234 1 TBD 29-APR-14 17:22:18
000009999 1 TBD 20-APR-14 07:42:32
000055279 1 TBD 20-APR-14 08:32:14
SQL>
SQL> /* row_number solution - works regardless of collisions */
SQL> SELECT barcode, location, "date", "time"
2 FROM
3 (SELECT barcode,
4 location,
5 "date",
6 "time",
7 row_number() OVER (
8 PARTITION BY barcode
9 ORDER BY TO_DATE("date"||' '||"time",'DD-MON-YYYY HH24:MI:SS') DESC
10 ) AS activity_order
11 FROM barcode_activity)
12 WHERE activity_order = 1;
BARCODE LOCATION date time
-------------------- ---------- ---------- ----------
000001234 1 TBD 29-APR-14 17:22:18
000009999 1 TBD 20-APR-14 07:42:32
000055279 1 TBD 20-APR-14 08:32:14

how to implement multivalued attribute in oracle?

I want to store more than one Email IDs in the Email id column of a table, as a multivalued attribute. How can I do this in oracle?
The traditional, relational way of doing this would be with a child heap table:
create table emails
(id number
, email_address varchar2(254)
, constraint em_t23_fk foreign key (id)
references t23 (id)
)
/
However, you are hinting at a nested table:
create type email_t as object
(email_address varchar2(254))
/
create type email_nt as table of email_t
/
alter table t23
add emails email_nt
nested table emails store as emails_table
/
Here's how it works:
SQL> update t23
2 set emails = email_nt (email_t('sam_i_am#example.com')
3 , email_t('green_eggs_n_ham#yahoo.co.uk'))
4 where id = 222
5 /
1 row updated.
SQL> select * from t23
2 where id = 222
3 /
ID NAME DOB
---------- ------------------------------ ---------
EMAILS(EMAIL_ADDRESS)
----------------------------------------------------------------------------------
222 Sam-I-Am 06-AUG-02
EMAIL_NT(EMAIL_T('sam_i_am#example.com'), EMAIL_T('green_eggs_n_ham#yahoo.co.uk'))
SQL>
Edit
The solution with VARRAY is basically the same:
SQL> alter table t23
2 drop column emails
3 /
Table altered.
SQL> create type email_va as varray(5) of varchar2(254)
2 /
Type created.
SQL> alter table t23
2 add emails email_va
3 /
Table altered.
SQL> update t23
2 set emails = email_va ('sam_i_am#example.com'
3 , 'green_eggs_n_ham#yahoo.co.uk')
4 where id = 222
5 /
1 row updated.
SQL> select t23.name
2 , e.*
3 from t23
4 , table (t23.emails) e
5 where t23.id = 222
6 /
NAME COLUMN_VALUE
------------------------------ ---------------------------------
Sam-I-Am sam_i_am#example.com
Sam-I-Am green_eggs_n_ham#yahoo.co.uk
SQL>
The standard way to do this is to define a second table, where you can store one email per row.
Oracle also supports nested tables so a single attribute column can contain multiple values.