PLSQL code requirement for partition comparison in oracle - oracle

Is there any way to compare values for one partition with another partition of the same table? Requirement is like I have a table and suppose there are 5 partitions, table having two columns(not null). Suppose Col1 having all the distinct values and in col2 there can be a duplicate values. So while comparing one partition with other or we can say rest of the 4 partitions on the basis of distinct col2 values according to the partition name, if the value match between two partition then a new table will create with union of the two partition.
And if there is no match between the col2 values of one partition and with rest of the partition then new table will create of same structure(without any union).
Note:
I want to automate this process through PLSQL code.
Currently what I am doing manually:
I have one table having five partition, for example Table structure:
create table PART_TEST1
(col1 int not null,
col2 int not null)
partition by range (col2)
(partition part1 values less than (10),
partition part2 values less than (20),
partition part3 values less than (30),
partition part4 values less than (40),
partition part5 values less than (maxvalue));
Data distribution:
col1 having distinct values like- 1, 2, 3....so on.
col2 having values like- 1, 2, -1, 1, 2, 3, 4, 1...so on
col2 has duplicate values and my goal is to find the distinct values according to the name of the partition like:
select distinct col2 from PART_TEST1 partition (part1);
For example output of above query is:
Col2
1
2
Again I am querying for finding matching values in other partition:
select distinct col2 from PART_TEST1 partition (part2);
For example output of above query is:
Col2
2
3
So now part 1 and part2 has one same value '2' and two non common values 1 and 3.
so my final query is:
create table 'TABLE_NAME' as select * from part_test1 where col2 = 1;
create table 'TABLE_NAME' as select * from part_test1 where col2 = 3;
create table 'TABLE_NAME' as
(select * from part_test1 where col2 = 2
union
select * from part_test1 where col2 = 2);
Hopefully now you will get some clarity about my problem. I am new to PLSQL and not able to compare the partition values. Also if I am able to compare the values then how can I store the output of the comparison query and then finally create the table? And also I am thinking that I need to compare one partition with rest of the partition like some kind of loop operation.

Related

Convert MERGE to UPDATE

I have the following MERGE statement.Is there a way to convert this into an update statement without using MERGE?
MERGE INTO tab1
USING (SELECT tab1.col1, tab2.col2
FROM tab1, tab2
WHERE tab1.col1 = tab2.col1) tab3
ON (tab1.col1 = tab3.col1)
WHEN MATCHED THEN UPDATE SET col2 = tab3.col2
What you are asking about is called "update through join", and contrary to a widely held belief, it is possible in Oracle. But there is a catch.
Obviously, the update - no matter how you attempt to perform it - is not well defined unless column col1 is unique in table tab2. That column is used for lookup in the update process; if its values are not unique, the update will be ambiguous. I ignore here idiotic retorts such as "uniqueness is needed only for those values also found in tab1.col1", or "there is no ambiguity as long as all values in tab2.col2 are equal when the corresponding values in tab2.col1 are equal".
The "catch" is this. The uniqueness of tab2.col1 may be a matter of data (you know it when you inspect the data), or a matter of metadata (there is a unique constraint, or a unique index, or a PK constraint, etc., on tab2.col1, which the parser can inspect without ever looking at the actual data).
merge will work even when uniqueness is only known by inspecting the data. It will still throw an error if uniqueness is violated - but that will be a runtime error (only after the data in tab2 is accessed from disk). By contrast, updating through a join requires the same uniqueness to be known ahead of time, through the metadata (or in other ways: for example if the second rowset - not a table but the table-like result of a query - is the result of an aggregation grouping on the join column; then the uniqueness is guaranteed by the definition of "aggregation").
Here is a brief example to show the difference.
Test data:
create table tab1 (col1 number, col2 number);
insert into tab1 (col1, col2) values (1, 3);
create table tab2 (col1 number, col2 number);
insert into tab2 (col1, col2) values (1, 6);
commit;
merge statement (with check at the end):
merge into tab1
using(
select tab1.col1,
tab2.col2
from tab1,tab2
where tab1.col1 = tab2.col1) tab3
on(tab1.col1 = tab3.col1)
when matched then
update
set col2 = tab3.col2;
1 row merged.
select * from tab1;
COL1 COL2
---------- ----------
1 6
Now let's restore table tab1 to its original data for the next test(s):
rollback;
select * from tab1;
COL1 COL2
---------- ----------
1 3
Update through join - with no uniqueness guaranteed in the metadata (will result in error):
update
( select t1.col2 as t1_c2, t2.col2 as t2_c2
from tab1 t1 join tab2 t2 on t1.col1 = t2.col1
)
set t1_c2 = t2_c2;
Error report -
SQL Error: ORA-01779: cannot modify a column which maps to a non key-preserved table
01779. 00000 - "cannot modify a column which maps to a non key-preserved table"
*Cause: An attempt was made to insert or update columns of a join view which
map to a non-key-preserved table.
*Action: Modify the underlying base tables directly.
Now let's add a unique constraint on the lookup column:
alter table tab2 modify (col1 unique);
Table TAB2 altered.
and try the update again (with the same update statement), plus verification:
update
( select t1.col2 as t1_c2, t2.col2 as t2_c2
from tab1 t1 join tab2 t2 on t1.col1 = t2.col1
)
set t1_c2 = t2_c2;
1 row updated.
select * from tab1;
COL1 COL2
---------- ----------
1 6
So - you can do it, if you use the correct syntax (as I have shown here) AND - very important - you have a unique or PK constraint or a unique index on column tab2.col1.

SQL delete rows not in another table

I'm looking for a good SQL approach (Oracle database) to fulfill the next requirements:
Delete rows from Table A that are not present in Table B.
Both tables have identical structure
Some fields are nullable
Amount of columns and rows is huge (more 100k rows and 20-30 columns to compare)
Every single field of every single row needs to be compared from Table A against table B.
Such requirement is owing to a process that must run every day as changes will come from Table B.
In other words: Table A Minus Table B => Delete the records from the Table A
delete from Table A
where (field1, field2, field3) in
(select field1, field2, field3
from Table A
minus
select field1, field2, field3
from Table B);
It's very important to mention that a normal MINUS within DELETE clause fails as does not take the nulls on nullable fields into consideration (unknown result for oracle, then no match).
I also tried EXISTS with success, but I have to use NVL function to replace the nulls with dummy values, which I don't want it as I cannot guarantee that the value replaced in NVL will not come as a valid value in the field.
Does anybody know a way to accomplish such thing? Please remember performance and nullable fields as "a must".
Thanks ever
decode finds sameness (even if both values are null):
decode( field1, field2, 1, 0 ) = 1
To delete rows in table1 not found in table2:
delete table1 t
where t.rowid in (select t1.rowid
from table1 t1
left outer join table2 t2
on decode(t1.field1, t2.field1, 1, 0) = 1
and decode(t1.field2, t2.field2, 1, 0) = 1
and decode(t1.field3, t2.field3, 1, 0) = 1
/* ... */
where t2.rowid is null /* no matching row found */
)
to use existing indexes
...
left outer join table2 t2
on (t1.index_field1=t2.index_field1 or
t1.index_field1 is null and t2.index_field1 is null)
and ...
Use a left outer join and test for null in your where clause
delete a
from a
left outer join b on a.x = b.x
where b.x is null
Have you considered ORALCE SQL MERGE statement?
Use Bulk operation for huge number of records. Performance wise it will be faster.
And use join between two table to get rows to be delete. Nullable columns can be compared with some default value.
Also, if you want Table A to be similar as Table B, why don't you truncate table A and then insert data from table b
Assuming you the same PK field available on each table...(Having a PK or some other unique key is critical for this.)
create table table_a (id number, name varchar2(25), dob date);
insert into table_a values (1, 'bob', to_date('01-01-1978','MM-DD-YYYY'));
insert into table_a values (2, 'steve', null);
insert into table_a values (3, 'joe', to_date('05-22-1989','MM-DD-YYYY'));
insert into table_a values (4, null, null);
insert into table_a values (5, 'susan', to_date('08-08-2005','MM-DD-YYYY'));
insert into table_a values (6, 'juan', to_date('11-17-2001', 'MM-DD-YYYY'));
create table table_b (id number, name varchar2(25), dob date);
insert into table_b values (1, 'bob', to_date('01-01-1978','MM-DD-YYYY'));
insert into table_b values (2, 'steve',to_date('10-14-1992','MM-DD-YYYY'));
insert into table_b values (3, null, to_date('05-22-1989','MM-DD-YYYY'));
insert into table_b values (4, 'mary', to_date('12-08-2012','MM-DD-YYYY'));
insert into table_b values (5, null, null);
commit;
-- confirm minus is working
select id, name, dob
from table_a
minus
select id, name, dob
from table_b;
-- from the minus, re-query to just get the key, then delete by key
delete table_a where id in (
select id from (
select id, name, dob
from table_a
minus
select id, name, dob
from table_b)
);
commit;
select * from table_a;
But, if at some point in time, tableA is to be reset to the same as tableB, why not, as another answer suggested, truncate tableA and select all from tableB.
100K is not huge. I can do ~100K truncate and insert on my laptop instance in less than 1 second.
> DELETE FROM purchase WHERE clientcode NOT IN (
> SELECT clientcode FROM client );
This deletes the rows from the purchase table whose clientcode are not in the client table. The clientcode of purchase table references the clientcode of client table.
DELETE FROM TABLE1 WHERE FIELD1 NOT IN (SELECT CLIENT1 FROM TABLE2);

Selecting duplicate rows from a table (ignoring the primary key)

I have an Oracle table where I want to find out if there are any duplicate rows (i.e. where all column values are equal). The problem is that the rows have unique primary keys so I want to exclude them since they are basically preventing me doing this.
Is there a way to ignore the primary key when doing such a task (instead of listing all columns except the primary key column) so that I can find out the duplicate rows?
No, just list all columns except the primary key columns in the GROUP BY clause:
CREATE TABLE mytable (
pk NUMBER PRIMARY KEY,
c1 NUMBER NOT NULL,
c2 NUMBER
);
INSERT INTO mytable (pk, c1, c2) VALUES (100, 1, 1);
INSERT INTO mytable (pk, c1, c2) VALUES (101, 1, 1);
INSERT INTO mytable (pk, c1, c2) VALUES (102, 2, 1);
INSERT INTO mytable (pk, c1, c2) VALUES (103, 2, null);
INSERT INTO mytable (pk, c1, c2) VALUES (104, 2, null);
SELECT c1, c2
FROM mytable
GROUP BY c1, c2
HAVING COUNT(*) > 1;
C1 C2
----- -----
1 1
2 (null)
To find out the non-primary key columns, you could use the following query. For most tables it will be quicker to type the columns instead of pasting/running the query:
SELECT column_name
FROM user_tab_columns co
WHERE co.table_name = 'MYTABLE'
AND NOT EXISTS (
SELECT *
FROM user_constraints pk
JOIN user_cons_columns pc USING (owner, constraint_name)
WHERE pk.table_name = co.table_name
AND constraint_type='P'
AND co.column_name = pc.column_name)
ORDER BY co.column_id;
You're going to have to list the other columns explicitly.
Potentially, you could use dynamic SQL to generate the query that you want. But that is unlikely to be terribly helpful if this is just for a single table. If you were trying to automate a process of comparing dozens or hundreds of tables, a dynamic SQL approach would potentially be easier to manage.

linq create average from different columns

I am having a simple table with 5 columns
id
id_user
col1
col2
col3
how can make Linq query to select an id_user and sum up all the col1, col2, col3 integers to make an average of those 3 columns ?
Thanks
Assuming you already have your table something along these lines should do it:
from row in table
let avgTotal = (new [] {row.col1, row.col2, row.col3}).Average()
select new {row.user_id, avgTotal}

Oracle Count across two tables

I have two tables that have identical columns. One table houses imported data, the other table houses data specific to my application:
IMPORT_TABLE MY_TABLE
COL1 COL2 COL1 COL2
"A" "1" "A" "2"
"B" "1" "B" "1"
What I need to do is write a single query that will tell me, for a given value in COL1, I have differing values in COL2 across the tables. So, when I run the query I woud get back the value "A" for COL1. This tells me that I need to insert "A" "1" into MY_TABLE.
How can I accomplish the query? I know how to do a Group By on a single table but not across tables.
If you just want to get the rows in IMPORT_TABLE that don't exist in MY_TABLE
SELECT col1, col2
FROM import_table
MINUS
SELECT col1, col2
FROM my_table
If col1 is unique, you could also do
SELECT import.col1, import.col2 imported_col2, mytbl.col2 my_col2
FROM import_table import
FULL OUTER JOIN my_table mytbl ON (mytbl.col1 = import.col1)

Resources