delete row by id - oracle

I'm in the middle of creating a tool similar to the SQL Developer table data viewer. My db is Oracle based.
I simply need to delete eg.: 'row number 3' from a SELECT result. That table doesn't have any PK nor unique records. I've tried various techniques with ROWNUM etc. but no luck.

Oracle has a ROWID pseudocolumn that you can use for this purpose in simple cases.
select rowid, ... from your_table where ... ;
delete from your_table where rowid = <what you got above>;
If your interface allows the user to make complex views/joins/aggregates, then knowing what the user intended to delete (so knowing what set of rowids to gather and what set of tables to delete from) is going to be tricky.
Warning: rowids are unique only within a given table, and, quoting the above documentation:
If you delete a row, then Oracle may reassign its rowid to a new row inserted later.
So be very, very careful if you do this.

Assuming that it is a standard heap-organized table (index-organized tables and clusters potentially introduce additional complexity), if you don't have any other way to identify a row, you can use the ROWID pseudocolumn. This gives you information about the physical location of a row on disk. This means that the ROWID for a particular row can change over time and the ROWID can and will be reused when you delete a row and then a subsequent INSERT operation inserts a new row that happens to be in the same physical location on disk. For most applications, it is reasonable to assume that the ROWID will remain constant between the time that you execute the query and the time that you issue the DELETE but you shouldn't try to store the ROWID for any period of time.
For example, if we create a simple two-column table and a few rows
SQL> create table foo( col1 number, col2 varchar2(10) );
Table created.
SQL> insert into foo values( 1, 'Justin' );
1 row created.
SQL> insert into foo values( 1, 'Justin' );
1 row created.
SQL> insert into foo values( 2, 'Bob' );
1 row created.
SQL> insert into foo values( 2, 'Charlie' );
1 row created.
SQL> commit;
Commit complete.
We can SELECT the ROWID and then DELETE the third row using the ROWID
SQL> select *
2 from foo;
COL1 COL2
---------- ----------
1 Justin
1 Justin
2 Bob
2 Charlie
SQL> select rowid, col1, col2
2 from foo;
ROWID COL1 COL2
------------------ ---------- ----------
AAAfKXAAEAABt7vAAA 1 Justin
AAAfKXAAEAABt7vAAB 1 Justin
AAAfKXAAEAABt7vAAC 2 Bob
AAAfKXAAEAABt7vAAD 2 Charlie
SQL> delete from foo where rowid = 'AAAfKXAAEAABt7vAAC';
1 row deleted.
SQL> select * from foo;
COL1 COL2
---------- ----------
1 Justin
1 Justin
2 Charlie

Try using ROWID instead of ROWNUM.

Related

How to create a unique id for an existing table in PL SQL?

The situation is that, when I import a file into the database, one of the first thing I usually do is to assign an unique ID for each record.
I normally do below in TSQL
ALTER TABLE MyTable
ADD ID INT IDENTITY(1,1)
I am wondering if there is something similar in PL SQL?
All my search result come back with multiple steps.
Then I'd like to know what PL SQL programmer typically do to ID records after importing a file. Do they do that?
The main purpose for me to ID these records is to trace it back after manipulation/copying.
Again, I understand there is solution there, my further question is whether PL SQL programmer actually do that, or there is other alternative which making this step not necessary in PL SQL?
OK then, as you're on Oracle 11g, there's no identity column there so - back to multiple steps. Here's an example:
I'm creating a table that simulates your imported table:
SQL> create table tab_import as
2 select ename, job, sal
3 from emp
4 where deptno = 10;
Table created.
Add the ID column:
SQL> alter table tab_import add id number;
Table altered.
Create a sequence which will be used to populate the ID column:
SQL> create sequence seq_imp;
Sequence created.
Update current rows:
SQL> update tab_import set
2 id = seq_imp.nextval;
3 rows updated.
Create a trigger which will take care about future inserts (if any):
SQL> create or replace trigger trg_bi_imp
2 before insert on tab_import
3 for each row
4 begin
5 :new.id := seq_imp.nextval;
6 end;
7 /
Trigger created.
Check what's in the table at the moment:
SQL> select * from tab_import;
ENAME JOB SAL ID
---------- --------- ---------- ----------
CLARK MANAGER 2450 1
KING PRESIDENT 5000 2
MILLER CLERK 1300 3
Let's import some more rows:
SQL> insert into tab_import (ename, job, sal)
2 select ename, job, sal
3 from emp
4 where deptno = 20;
3 rows created.
The trigger had silently populated the ID column:
SQL> select * From tab_import;
ENAME JOB SAL ID
---------- --------- ---------- ----------
CLARK MANAGER 2450 1
KING PRESIDENT 5000 2
MILLER CLERK 1300 3
SMITH CLERK 800 4
JONES MANAGER 2975 5
FORD ANALYST 3000 6
6 rows selected.
SQL>
Shortly: you need to
alter table and add the ID column
create a sequence
create a trigger
The end.
The answer given by #Littlefoot would be my recommendation too - but still I thought I could mention the following variant which will work only if you do not intend to add more rows to the table later.
ALTER TABLE MyTable add id number(38,0);
update MyTable set id = rownum;
commit;
My test:
SQL> create table tst as select * from all_tables;
Table created.
SQL> alter table tst add id number(38,0);
Table altered.
SQL> update tst set id = rownum;
3815 rows updated.
SQL> alter table tst add constraint tstPk primary key (id);
Table altered.
SQL>
SQL> select id from tst where id < 15;
ID
----------
1
2
3
4
5
6
7
8
9
10
11
ID
----------
12
13
14
14 rows selected.
But as mentioned initially,- this only fixes numbering for the rows you have at the time of the update - your'e not going to get new id values for new rows anytime later - if you need that, go for the sequence solution.
You can add an id column to a table with a single statement (Oracle 11g, see dbfiddle):
alter table test_
add id raw( 16 ) default sys_guid() ;
Example:
-- create a table without an id column
create table test_ ( str )
as
select dbms_random.string( 'x', 16 )
from dual
connect by level <= 10 ;
select * from test_ ;
STR
ULWL9EXFG6CIO72Z
QOM0W1R9IJ2ZD3DW
YQWAP4HZNQ57C2UH
EETF2AXD4ZKNIBBF
W9SECJYDER793MQW
alter table test_
add id raw( 16 ) default sys_guid() ;
select * from test_ ;
STR ID
ULWL9EXFG6CIO72Z 0x782C6EBCAE2D7B9FE050A00A02005D65
QOM0W1R9IJ2ZD3DW 0x782C6EBCAE2E7B9FE050A00A02005D65
YQWAP4HZNQ57C2UH 0x782C6EBCAE2F7B9FE050A00A02005D65
EETF2AXD4ZKNIBBF 0x782C6EBCAE307B9FE050A00A02005D65
W9SECJYDER793MQW 0x782C6EBCAE317B9FE050A00A02005D65
Testing
-- Are the id values unique and not null? Yes.
alter table test_
add constraint pkey_test_ primary key ( id ) ;
-- When we insert more rows, will the id be generated? Yes.
begin
for i in 1 .. 100
loop
insert into test_ (str) values ( 'str' || to_char( i ) ) ;
end loop ;
end ;
/
select * from test_ order by id desc ;
-- last 10 rows of the result
STR ID
str100 0x782C806E16A5E998E050A00A02005D81
str99 0x782C806E16A4E998E050A00A02005D81
str98 0x782C806E16A3E998E050A00A02005D81
str97 0x782C806E16A2E998E050A00A02005D81
str96 0x782C806E16A1E998E050A00A02005D81
str95 0x782C806E16A0E998E050A00A02005D81
str94 0x782C806E169FE998E050A00A02005D81
str93 0x782C806E169EE998E050A00A02005D81
str92 0x782C806E169DE998E050A00A02005D81
str91 0x782C806E169CE998E050A00A02005D81
Regarding your other questions:
{1} Then I'd like to know what PL SQL programmer typically do to ID records after importing a file. Do they do that? The main purpose for me to ID these records is to trace it back after manipulation/copying.
-> As you know, the purpose of an id is: to identify a row. We don't "do anything to IDs". Thus, your usage of IDs seems legit.
{2} Again, I understand there is solution there, my further question is whether PL SQL programmer actually do that, or there is other alternative which making this step not necessary in PL SQL?
-> Not quite sure what you are asking here. Although there is a ROWID() pseudocolumn (see documentation), we should not use it to identify rows.
"You should not use ROWID as the primary key of a table. If you delete
and reinsert a row with the Import and Export utilities, for example,
then its rowid may change. If you delete a row, then Oracle may
reassign its rowid to a new row inserted later."

How to select partition name of specific data?

I want to select a data and wanna see in which partition.
partition column is : code (varchar column)
Select .... -- I want to find partition name
from
partition_table
where to_number(code) = 55;
why I need to this:
I have a data which code is '55' but in that table when I use partition column I do not select it. But there is data which value is '55'
So I want to that data in which partition.
And the data is not in PDEFAULT partition. I ve already check it.
edit
data is in another partition. I think there is a problem with exchange partition process
thanks in advance
There are a couple of ways.
1) The rowid will point to the partition object
SQL> create table t ( x int, y int )
2 partition by range (x )
3 ( partition p1 values less than ( 100 ),
4 partition p2 values less than ( 200 )
5 );
Table created.
SQL>
SQL> insert into t values (34,34);
1 row created.
SQL>
SQL> select rowid from t;
ROWID
------------------
AAA0cqAAHAAAAQ6AAA
1 row selected.
SQL>
SQL> select dbms_rowid.rowid_object(rowid) from t;
DBMS_ROWID.ROWID_OBJECT(ROWID)
------------------------------
214826
1 row selected.
SQL>
SQL> select subobject_name
2 from user_objects
3 where data_Object_id =
4 ( select dbms_rowid.rowid_object(rowid) from t );
SUBOBJECT_NAME
------------------------------------------------------------
P1
2) You can data mine the dictionary to probe the HIGH_VALUE column in USER_TAB_PARTITIONS. I did a video on how to do that here
https://www.youtube.com/watch?v=yKHQQXKdfOM

How to select column value from a nested table

I created 1 object.
create type tab_billing as object(invoice_no number,
customername varchar2(100)
);
Now i created a table with the object as a column.
CREATE TABLE tab1 (col1 number,COL2 tab_billing);
Is there anyway I can ONLY select invoice_no from the tab1.
select col2 from tab1;
Is givng me both invoice_no and customername. Substr function is not working here.
You can query the column value's object field directly, but to avoid confusing the object name resolution steps you have to supply and use a table alias:
select t1.col2.invoice_no from tab1 t1;
This is mentioned in the documentation:
To avoid inner capture and similar problems resolving references, Oracle Database requires you to use a table alias to qualify any dot-notational reference to subprograms or attributes of objects.
Qualifying the column with the the table name isn't enough; using select tab1.col2.invoice_no from tab1 gets ORA-00904. You have to use a table alias - although, slightly bizarrely, it still works if the alias is the same as the table name, so select tab1.col2.invoice_no from tab1 tab1 (i.e. aliasing tab1 as tab1, which is normally redundant) works too.
Quick demo:
create type tab_billing as object(invoice_no number,
customername varchar2(100)
);
/
Type TAB_BILLING compiled
CREATE TABLE tab1 (col1 number,COL2 tab_billing);
Table TAB1 created.
insert into tab1 values (1, tab_billing(42, 'Test'));
1 row inserted.
select t1.col2.invoice_no from tab1 t1;
COL2.INVOICE_NO
---------------------------------------
42
You can use TREAT:
SQL> create type tab_billing as object(invoice_no number,
2 customername varchar2(100)
3 );
4 /
Type created.
SQL> CREATE TABLE tab1 (col1 number,COL2 tab_billing);
Table created.
SQL> insert into tab1 values (1, tab_billing(10, 'ten')) ;
1 row created.
SQL> select col1,
2 TREAT(col2 AS tab_billing).invoice_no as invoice_no,
3 TREAT(col2 AS tab_billing).customername as customername
4 from tab1;
COL1 INVOICE_NO CUSTOMERNAME
------ ---------- --------------------
1 10 ten

Dynamic table name in pl/sql

I need to select columns from a table Table_A, However there is another table which has the same schema Table_B. The query should determine the from table dynamically. For ex. if Table_A has more rows then use Table_A else use Table_B.
Query something like this
select employee, salary, id from (condition to count rows and select the table )table;
Is this possible without using cursors and EXECUTE IMMEDIATE??.
Normally, you would use dynamic SQL for this sort of thing. That would involve either using the DBMS_SQL package, EXECUTE IMMEDIATE or doing an OPEN <<cursor>> FOR <<SQL statement string>>.
If you really want to use static SQL, you could query both tables and only return one set of results. I cannot envision a situation where this would really make sense but you can certainly do it
Create a FOO and a FOO2 table. FOO2 has two rows to the one row from FOO
SQL> create table foo( col1 number );
Table created.
SQL> create table foo2( col1 number );
Table created.
SQL> insert into foo values( 1 );
1 row created.
SQL> insert into foo2 values( 1 );
1 row created.
SQL> insert into foo2 values( 2 );
1 row created.
Run the query. This will return all the data from FOO2
SQL> ed
Wrote file afiedt.buf
1 select col1
2 from (select the_union.*,
3 max(cnt) over () max_cnt
4 from (select col1, count(*) over () cnt from foo
5 union all
6 select col1, count(*) over () from foo2) the_union)
7* where cnt = max_cnt
SQL> /
COL1
----------
1
2
Insert more rows into FOO. Now the same query will return all the data from FOO
SQL> insert into foo values( 3 );
1 row created.
SQL> insert into foo values( 5 );
1 row created.
SQL> commit;
Commit complete.
SQL> select col1
2 from (select the_union.*,
3 max(cnt) over () max_cnt
4 from (select col1, count(*) over () cnt from foo
5 union all
6 select col1, count(*) over () from foo2) the_union)
7 where cnt = max_cnt;
COL1
----------
1
3
5
As I said, though, I cannot fathom a situation where it would actually make sense to do this.
I am completely guessing at what you are really trying to do, but I am thinking you want to use a synonym. I am guessing that there is some sort of event that triggers when you should be using TableA vs. TableB. Create a synonym "my_table" pointing to TableA and have your view select from "my_table". Then whenever you want your view to point to TableB instead, you just switch your synonym to point to TableB, and you don't have to do anything to the view.
For more info, read about synonyms in the Concepts Guide.

Oracle: how to drop a subpartition of a specific partition

I am using an oracle 11 table with interval partitioning and list subpartitioning like this (simplified):
CREATE TABLE LOG
(
ID NUMBER(15, 0) NOT NULL PRIMARY KEY
, MSG_TIME DATE NOT NULL
, MSG_NR VARCHAR2(16 BYTE)
) PARTITION BY RANGE (MSG_TIME) INTERVAL (NUMTOYMINTERVAL (1,'MONTH'))
SUBPARTITION BY LIST (MSG_NR)
SUBPARTITION TEMPLATE (
SUBPARTITION login VALUES ('FOO')
, SUBPARTITION others VALUES (DEFAULT)
)
(PARTITION oldvalues VALUES LESS THAN (TO_DATE('01-01-2010','DD-MM-YYYY')));
How do I drop a specific subpartitition for a specific month without knowing the (system generated) name of the subpartition? There is a syntax "alter table ... drop subpartition for (subpartition_key_value , ...)" but I don't see a way to specify the month for which I am deleting the subpartition. The partition administration guide does not give any examples, either. 8-}
You can use the metadata tables to get the specific subpartition name:
SQL> insert into log values (1, sysdate, 'FOO');
1 row(s) inserted.
SQL> SELECT p.partition_name, s.subpartition_name, p.high_value, s.high_value
2 FROM user_tab_partitions p
3 JOIN
4 user_tab_subpartitions s
5 ON s.table_name = p.table_name
6 AND s.partition_name = p.partition_name
7 AND p.table_name = 'LOG';
PARTITION_NAME SUBPARTITION_NAME HIGH_VALUE HIGH_VALUE
--------------- ------------------ ------------ ----------
OLDVALUES OLDVALUES_OTHERS 2010-01-01 DEFAULT
OLDVALUES OLDVALUES_LOGIN 2010-01-01 'FOO'
SYS_P469754 SYS_SUBP469753 2012-10-01 DEFAULT
SYS_P469754 SYS_SUBP469752 2012-10-01 'FOO'
SQL> alter table log drop subpartition SYS_SUBP469752;
Table altered.
If you want to drop a partition dynamically, it can be tricky to find it with the ALL_TAB_SUBPARTITIONS view because the HIGH_VALUE column may not be simple to query. In that case you could use DBMS_ROWID to find the subpartition object_id of a given row:
SQL> insert into log values (4, sysdate, 'FOO');
1 row(s) inserted.
SQL> DECLARE
2 l_rowid_in ROWID;
3 l_rowid_type NUMBER;
4 l_object_number NUMBER;
5 l_relative_fno NUMBER;
6 l_block_number NUMBER;
7 l_row_number NUMBER;
8 BEGIN
9 SELECT rowid INTO l_rowid_in FROM log WHERE id = 4;
10 dbms_rowid.rowid_info(rowid_in =>l_rowid_in ,
11 rowid_type =>l_rowid_type ,
12 object_number =>l_object_number,
13 relative_fno =>l_relative_fno ,
14 block_number =>l_block_number ,
15 row_number =>l_row_number );
16 dbms_output.put_line('object_number ='||l_object_number);
17 END;
18 /
object_number =15838049
SQL> select object_name, subobject_name, object_type
2 from all_objects where object_id = '15838049';
OBJECT_NAME SUBOBJECT_NAME OBJECT_TYPE
--------------- --------------- ------------------
LOG SYS_SUBP469757 TABLE SUBPARTITION
As it turns out, the "subpartition for" syntax does indeed work, though that seems to be a secret Oracle does not want to tell you about. :-)
ALTER TABLE TB_LOG_MESSAGE DROP SUBPARTITION FOR
(TO_DATE('01.02.2010','DD.MM.YYYY'), 'FOO')
This deletes the subpartition that would contain MSG_TIME 2010/02/01 and MSG_NR FOO. (It is not necessary that there is an actual row with this exact MSG_TIME and MSG_NR. It throws an error if there is no such subpartition, though.)
Thanks for the post - it was very useful for me.
One observation though on the above script to identify the partition and delete it:
The object_id returned by dbms_rowid.rowid_info is not the object_id of the all_objects table. It is actually the data_object_id. It is observed that usually these ids match. However, after truncating the partitioned table several times, these ids diverged in my database. Hence it might be reasonable to instead use the data_object_id to find out the name of the partition:
select object_name, subobject_name, object_type
from all_objects where data_object_id = '15838049';
From the table description of ALL_OBJECTS:
OBJECT_ID Object number of the object
DATA_OBJECT_ID Object number of the segment which contains the object
http://docs.oracle.com/cd/B19306_01/appdev.102/b14258/d_rowid.htm
In the sample code provided in the above link, DBMS_ROWID.ROWID_OBJECT(row_id) is used instead to derive the same information that is given by dbms_rowid.rowid_info. However, the documentation around this sample mentions that it is a data object number from the ROWID.
Examples
This example returns the ROWID for a row in the EMP table, extracts
the data object number from the ROWID, using the ROWID_OBJECT function
in the DBMS_ROWID package, then displays the object number:
DECLARE object_no INTEGER; row_id ROWID; ... BEGIN
SELECT ROWID INTO row_id FROM emp
WHERE empno = 7499; object_no := DBMS_ROWID.ROWID_OBJECT(row_id); DBMS_OUTPUT.PUT_LINE('The obj. # is
'|| object_no); ...

Resources