SELECT query based on mentioned result table - Oracle SQL - sql

I have 3 tables.
TABLE1 with one column ROLL_NO
CREATE TABLE TABLE1 (ROLL_NO NUMBER(3) NOT NULL);
TABLE2 with 3 columns ROLL_NO, CLASS, SEC
CREATE TABLE TABLE2 (ROLL_NO NUMBER(3) NOT NULL, CLASS NUMBER(3) NOT NULL, SEC NUMBER(3) NOT NULL);
TABLE3 with 3 columns ROLL_NO, CODE, AMT
CREATE TABLE TABLE3 (ROLL_NO NUMBER(3) NOT NULL, CODE VARCHAR2(3) NOT NULL, AMT NUMBER(3) NOT NULL);
INSERT INTO TABLE1 VALUES (101);
INSERT INTO TABLE1 VALUES (102);
INSERT INTO TABLE1 VALUES (103);
INSERT INTO TABLE1 VALUES (104);
----------------------------------
INSERT INTO TABLE2 VALUES (101,1, 12);
INSERT INTO TABLE2 VALUES (102,1, 12);
INSERT INTO TABLE2 VALUES (103,1, 12);
INSERT INTO TABLE2 VALUES (104,1, 12);
--------------------------------------
INSERT INTO TABLE3 VALUES (101, 'A2', 100);
INSERT INTO TABLE3 VALUES (101, '10', 100);
INSERT INTO TABLE3 VALUES (102, 'B3', 200);
INSERT INTO TABLE3 VALUES (102, '10', 200);
INSERT INTO TABLE3 VALUES (103, '04', 300);
The SQL query which mentioned below:
SELECT T1.ROLL_NO,
T2.CLASS,
T2.SEC,
NVL(T3.CODE,0) AS CODE,
NVL(T3.AMT, 0) AS AMT
FROM TABLE1 T1
JOIN TABLE2 T2 ON T1.ROLL_NO = T2.ROLL_NO
LEFT JOIN TABLE3 T3 ON T1.ROLL_NO = T3.ROLL_NO
WHERE T1.ROLL_NO IN (101,102,103,104);
If we don't find any record i.e. CODE and AMT for particular ROLL_NO, by default we are assigning as 0.
The result for above query:
ROLL_NO CLASS SEC CODE AMT
-------------------------------------
101 1 12 A2 100
101 1 12 10 100
102 1 12 B3 200
102 1 12 10 200
103 1 12 4 300
104 1 12 0 0
I am looking for a query in such a way that
a) if particular ROLL_NO has CODE 10 and also additional CODE values other than 10 then get that row which has CODE as 10 in the result table.
b) if particular ROLL_NO don't have CODE 10 but has other additional CODE values then get that row in the result table.
In previous table, ROLL_NO 101 and 102 comes under case 'a' and 103, 104 comes under case 'b'
Final result should be
ROLL_NO CLASS SEC CODE AMT
-------------------------------------
101 1 12 10 100
102 1 12 10 200
103 1 12 4 300
104 1 12 0 0
I am looking for a query to get the above result but I am not able to get it. Till now, I tried using RANK function by partitioning on ROLL_NO and order by CODE in descending and select 1st row in each partition but it doesn't work if particular ROLL_NO have additional code greater than 10.

You can use analytic function ROW_NUMBER() and it should work:
select roll_no, class, sec, code, amt
from (SELECT T1.ROLL_NO,
T2.CLASS,
T2.SEC,
NVL(T3.CODE,0) AS CODE,
NVL(T3.AMT, 0) AS AMT,
--analytic function
row_number() over (partition by t1.roll_no order by NVL(T3.CODE,0) desc) as row_num
FROM TABLE1 T1
JOIN TABLE2 T2 ON T1.ROLL_NO = T2.ROLL_NO
LEFT JOIN TABLE3 T3 ON T1.ROLL_NO = T3.ROLL_NO
WHERE T1.ROLL_NO IN (101,102,103,104)) t
where t.row_num = 1;

You simply need a ROW_NUMBER() function to achieve your desired result -
SELECT T1.ROLL_NO,
T2.CLASS,
T2.SEC,
NVL(T3.CODE,0) AS CODE,
NVL(T3.AMT, 0) AS AMT
FROM TABLE1 T1
JOIN TABLE2 T2 ON T1.ROLL_NO = T2.ROLL_NO
LEFT JOIN (SELECT ROLL_NO, CODE, AMT,
ROW_NUMBER() OVER(PARTITION BY ROLL_NO ORDER BY CODE DESC) RN
-- If you have code greater than 10 also you have to use a CASE statement instead of simple order by clause
FROM TABLE3) T3 ON T1.ROLL_NO = T3.ROLL_NO
AND RN = 1
WHERE T1.ROLL_NO IN (101,102,103,104);

Related

Find number of non-unique column values within a group where a second column value is all the same

I have two tables.
Table 1:
id_a,
id_b,
id_t
Table 2:
id_t,
name
If Table 2 name starts with a, I need to find out of anything
with matching id_ts also have matching id_as.
If Table 2 name starts with b, I need to find out of any row
with matching id_ts also have matching id_bs.
I need to know how many times these matches occur.
Table 1
id_a
id_b
id_t
1
0
123
1
0
123
2
0
123
0
4
456
0
4
456
0
5
456
0
5
456
0
5
456
0
6
456
0
7
456
Table 2
id_t
name
123
aaq
456
bws
So in this example, I want to see a result like
id_t
name
num_non_unique
123
aaq
1
456
bws
2
My current code is this:
SELECT
t2.id_t, t2.name, count(t1.*) AS num_non_unique
FROM
Table 2 AS t2
JOIN Table 1 as t1 ON t2.id_t = t1.id_t
WHERE
(t2.name like 'a%' and t1.id_a in (SELECT id_a FROM t1 GROUP BY id_a, id_t HAVING count(*) > 1))
OR (t2.name like 'b%' AND t1.id_b IN (SELECT id_b FROM t1 GROUP BY id_b, id_t HAVING count(*) > 1))
GROUP BY t1.name, t1.id_t
This doesn't currently give me the results I want.
With this code I seem to get the count of all available rows for id_b, and 1 + non_uniques for id_a (so with one non-unique, the value is 2, otherwise the column has a 1).
Any help is appreciated!
Schema and insert statements:
create table table_1(id_a int, id_b int, id_t int);
insert into table_1 values(1, 0, 123);
insert into table_1 values(1, 0, 123);
insert into table_1 values(2, 0, 123);
insert into table_1 values(0, 4, 456);
insert into table_1 values(0, 4, 456);
insert into table_1 values(0, 5, 456);
insert into table_1 values(0, 5, 456);
insert into table_1 values(0, 5, 456);
insert into table_1 values(0, 6, 456);
insert into table_1 values(0, 7, 456);
create table table_2 (id_t int, name varchar(50));
insert into table_2 values(123, 'aaq');
insert into table_2 values(456, 'bws');
Query:
with case1 as
(
select id_t, id_a
from table_1
group by id_t, id_a
having count(*)=1
),
case2 as
(
select id_t,id_b
from table_1
group by id_t,id_b
having count(*)=1
)
select id_t,name,
(case when t2.name like 'a%' then (select count(*) from case1 where case1.id_t=t2.id_t) when t2.name like 'b%' then (select count(*) from case2 where case2.id_t=t2.id_t) end)num_non_unique
from table_2 as t2
Output:
id_t
name
num_non_unique
123
aaq
1
456
bws
2
db<fiddle here
If I understand correctly, you want the count -- for each row in table_2 -- of the corresponding rows in table_1 where id_b only appears once.
One method is:
select t2.*, coalesce(cnt, 0)
from table_2 t2 left join
(select id_a, count(*) as cnt
from (select id_a, id_b
from table_1
group by id_a, id_b
having count(*) = 1
) t1
group by id_a
) t1
on t1.id_a = t2.id_a;
Try this Code:
select name,id_ts,sum(count_) "num_non_unique" from
(
select t1.id_a,t1.id_b,t1.id_ts,t2.name, count(*) "count_"
from tab1 t1
inner join tab2 t2 on t1.id_ts=t2.id_t
group by 1,2,3,4
having count(*)=1
)tab
group by 1,2
Explanation:
As you have mentioned in your question that one column value will always be same based of name starting character. So no need to check the starting of the name in your query.
Simply we will check the count of unique row and filter it by using condition having count(*)=1.
After getting the unique row simply group it to get the count of rows group by name and id_ts
DEMO

SQL query to return matching and non matching rows in the same table

I have a single table and want to do an SQL query to return matching rows and also non matching rows in Postgres. It would be great if the SQL also worked for Informix, but this is not a requirement.
create temp table t1 (cust integer, product char(16), qty numeric(16,2));
insert into t1 values(1000, 11, 100);
insert into t1 values(1000, 11, 200);
insert into t1 values(1000, 22, 300);
insert into t1 values(1001, 22, 400);
insert into t1 values(1002, 33, 500);
insert into t1 values(1003, 44, 600);
insert into t1 values(1004, 55, 700);
insert into t1 values(1004, 55, 800);
select cust,
product,
sum(qty)
from t1
where product = '11'
group by 1,2
union all
select cust,
null,
null
from t1
where product != '11'
and cust not in (select cust from t1 where product = '11')
order by cust;
cust | product | sum
------+------------------+--------
1000 | 11 | 300.00
1001 | |
1002 | |
1003 | |
1004 | |
1004 | |
I want all rows where cust is equal to the specified product, but I also want all cust numbers to be returned with null for product and qty that do not have the product. I have two problems. Column cust should be unique, there are two duplicate rows returned for cust 1004 and if I use distinct in the query it gives an error about data type on the qty field. The second problem is that this SQL take a very long time when run against a larger data set, like 10,000 rows.
You can use a conditional aggregation using a filter() clause:
select t1.cust,
max(t1.product) filter (where t1.product = '11') as product,
sum(t1.qty) filter (where t1.product = '11')
from t1
group by t1.cust, t1.product
order by t1.cust, t1.product
The max(t1.product) ... could also be written as case when t1.product = '11' then t1.product else null end as product and is only there to hide the actual product number. If you don't mind seeing the product number, then you can simply use t1.product there
Ypou could try using an aggregation filter eg:
select cust, product, sum( case when product = '11' then qty else 0 end) sum
from t1
group by 1,2
order by cust

sql data where not exists in a table and not duplicate

its a bit tricky please focus on my requirments, I have 2 tables , I want to get data from first table where not exists in the second AND the data in first column are not duplicate for sub id and child id.
example: I have this table
tab1
id subid childid
1 11 77
2 22 55
3 33 66
4 11 77
7 22 55
8 33 60
9 99 98
10 33 60
11 97 98
tab2
id
1
4
7
10
the first thing I want is the id in tab1 doesnt exists in tab2 which will be
2,3,8,9,11 however some of those id have duplicate subid and chilid so i have to exclude them therefore I should have id 3, 9, 11
I tried this query but it returne me also 3 ,9 ,11, 8 , I dont want 8 how to fix the query ?
select *
from tab1 a
where not exists (select 1 from tab2 b where a.id = b.id)
and a.sub_id in (select c.sub_id
from tab1 c
group by c.sub_id,c.evt_id
having count(1) = 1)
For multiple database vendors I think the easiest solution would be a couple of not exists queries:
select *
from tab1 a
where not exists (
select 1
from tab2 b
where a.id = b.id
)
and not exists (
select 1
from tab1 c
where c.sub_id = a.sub_id
and c.evt_id = a.evt_id
and c.id <> a.id
)
i think below query will work
select a.*
from tab1 a
where not exists (select 1 from tab2 b where a.id = b.id)
and not exists (select 1 from tab1 c
where c.sub_id = a.sub_id
and c.childid = a.childid
group by c.sub_id,childid
having count(*)> = 2
)
Just to add an approach using a CTE, you can first determine the unique childid,subid pairs and then join that table with your main table:
DB Fiddle
Schema (PostgreSQL v9.6)
create table tab1 (
id integer primary key unique not null
, subid integer not null
, childid integer not null
);
insert into tab1 (id,subid,childid) values (1, 11, 77);
insert into tab1 (id,subid,childid) values (2, 22, 55);
insert into tab1 (id,subid,childid) values (3, 33, 66);
insert into tab1 (id,subid,childid) values (4, 11, 77);
insert into tab1 (id,subid,childid) values (7, 22, 55);
insert into tab1 (id,subid,childid) values (8, 33, 60);
insert into tab1 (id,subid,childid) values (9, 99, 98);
insert into tab1 (id,subid,childid) values (10, 33,60);
insert into tab1 (id,subid,childid) values (11, 97 ,98);
create table tab2 (
id integer primary key unique not null
);
insert into tab2 (id) values (1);
insert into tab2 (id) values (4);
insert into tab2 (id) values (7);
insert into tab2 (id) values (10);
Query #1
with tab1_unique as (
select subid, childid, count(*) as duplicate
from tab1
group by subid, childid
having count(*) = 1
)
select *
from tab1 a
join tab1_unique u on a.subid = u.subid and a.childid = u.childid
where not exists (select 1 from tab2 b where a.id = b.id);
| id | subid | childid | subid | childid | duplicate |
| --- | ----- | ------- | ----- | ------- | --------- |
| 11 | 97 | 98 | 97 | 98 | 1 |
| 9 | 99 | 98 | 99 | 98 | 1 |
| 3 | 33 | 66 | 33 | 66 | 1 |
not exists should do this:
select t1.*
from (select t1.*, count(*) over (partition by subid, childid) as cnt
from tab1 t1
) t1
where not exists (select 1 from tab2 t2 where t2.id = t1.id) and
cnt = 1;
You can use not exists as well for the subid/childid with the assumption that the rows are unique in the first table. Without this assumption, window functions are best solution -- and possibly the best solution anyway.

Oracle SQL to compare data and retrieve un-matching rows between two tables in Oracle DB

I am trying to compare the data between two tables using Oracle SQL. The SQL must compare the data and must return the non-matching data from the table. Does anyone have any idea to perform this operation? I might do this using SQL or use Informatica for performing the same. Please do help me out. Thanks in advance.
NOTE:They might have the same structure since I will be creating a TEMP table A with ID, Qty,Price. A will be compared with B (ID, Qty,Price). A is from EBS and TEMP A is created in Oracle. B is from Data-warehouse and exists in Oracle DB.
You can compare rows from 2 tables using sets: take the union of both tables and subtract from it the intersection of them.
(
select id, qty, price from table_a
union
select id, qty, price from table_b
)
minus
(
select id, qty, price from table_a
intersect
select id, qty, price from table_b
);
That will give you what rows do not match the opposite table, but you won't be able to tell from that query what row came from which table. That should be easy to get, joining the result with the original table (or intersect again, although join should be cheaper given the table has a primary key).
with t as (
(
select id, qty, price from table_a
union
select id, qty, price from table_b
)
minus
(
select id, qty, price from table_a
intersect
select id, qty, price from table_b
))
select *
from t
join table_a a on t.id = a.id;
Same will work joining table_b, for finding out what rows there don't match table_a.
Running sample:
SQL> create table table_a (id number, qty number, price number);
Table created
SQL> insert into table_a values (1, 100, 1.1);
1 row inserted
SQL> insert into table_a values (2, 200, 2.2);
1 row inserted
SQL> insert into table_a values (3, 300, 3.3);
1 row inserted
SQL> create table table_b (id number, qty number, price number);
Table created
SQL> insert into table_b values (1, 100, 1.1);
1 row inserted
SQL> insert into table_b values (2, 200, 2.2);
1 row inserted
SQL> insert into table_b values (4, 300, 3.3);
1 row inserted
SQL> insert into table_b values (5, 500, 5.5);
1 row inserted
SQL> (
2 select id, qty, price from table_a
3 union
4 select id, qty, price from table_b
5 )
6 minus
7 (
8 select id, qty, price from table_a
9 intersect
10 select id, qty, price from table_b
11 );
ID QTY PRICE
---------- ---------- ----------
3 300 3,3
4 300 3,3
5 500 5,5
SQL> with t as (
2 (
3 select id, qty, price from table_a
4 union
5 select id, qty, price from table_b
6 )
7 minus
8 (
9 select id, qty, price from table_a
10 intersect
11 select id, qty, price from table_b
12 ))
13 select *
14 from t
15 join table_a a on t.id = a.id;
ID QTY PRICE ID QTY PRICE
---------- ---------- ---------- ---------- ---------- ----------
3 300 3,3 3 300 3,3
SQL>

Trying to get my delete statement to work

I want to remove rows from a table, based on a column from another table as such:
Table1: Table2:
value value, i
If table2.i is less than 1, delete corresponding row from table1 (but keep it in table2).
The problem is that value isn't unique, so if I have this for exampe:
Table1 table2
+-------+ +-----------+
| value | | value | i |
+-------+ +-----------+
| 5 | | 5 | 0 |
| 3 | | 5 | 3 |
+-------+ | 3 | 0 |
| 3 | 0 |
+-----------+
Value 3 should be deleted from table1 (since all occurrences in table2 has i<1) but value 5 should stay(because of i=3 row in table2)
My code so far (doesn't work):
DELETE FROM Table1, Table2
WHERE (SELECT MIN(Table2.i) FROM Table1, Table2
WHERE Table1.value = Table2.value) < 1;
Problem is: since my subquery returns min for ALL rows, everything gets deleted.
And I can't use "group by" in my subquery because then my comparison isn't allowed.
Try this one:
DELETE FROM Table1
WHERE NOT EXISTS(SELECT 1
FROM Table2
WHERE Table2.i > 0
AND Table2.value = Table1.value)
I dont know why you are using min, instead u should use max:
try this
DELETE FROM Table1
WHERE Table1.value1 = Table2.value1
and (SELECT MAX(Table2.i) FROM Table2
WHERE Table1.value1 = Table2.value1) < 1;
Ok, I'd start by writing a query that selects the rows you want to delete,
SELECT
*
FROM
Table1
EXCEPT
(
SELECT
t1.value
FROM
Table1 t1
JOIN
Table2 t2
ON t2.value = t1.value
WHERE
t2.i > 0
);
See Fiddle
Then change the SELECT to a DELETE
DELETE Table1
FROM
Table1 t1
WHERE
t1.value NOT IN
(
SELECT
t1.value
FROM
Table1 t1
JOIN
Table2 t2
ON t2.value = t1.value
WHERE
t2.i > 0
);
See Fiddle
How about:
delete from table1 where value in
(select value from table2 group by value having max(i) < 1)
Grouping table 2 by value and using having to detect where the maximum is less than 1 allows you to select the correct values for deletion from table 1.
having is basically a where clause which comes into play after aggregation so can be used with max and so on.
Here's a script to show it in action:
DROP TABLE TABLE1;
DROP TABLE TABLE2;
CREATE TABLE TABLE1 (VALUE INTEGER);
CREATE TABLE TABLE2 (VALUE INTEGER, I INTEGER);
INSERT INTO TABLE1 VALUES (5);
INSERT INTO TABLE1 VALUES (3);
INSERT INTO TABLE2 VALUES (5, 0);
INSERT INTO TABLE2 VALUES (5, 3);
INSERT INTO TABLE2 VALUES (3, 0);
INSERT INTO TABLE2 VALUES (3, 0);
SELECT * FROM TABLE1;
SELECT * FROM TABLE2;
DELETE FROM TABLE1 WHERE VALUE IN
(SELECT VALUE FROM TABLE2 GROUP BY VALUE HAVING MAX(I) = 0);
SELECT * FROM TABLE1;
SELECT * FROM TABLE2;
The output of this script is shown below. First, the setting up of all the tables:
DROP TABLE TABLE1; DROP TABLE TABLE2;
TABLE1 DROPPED
TABLE2 DROPPED
CREATE TABLE TABLE1 (VALUE INTEGER);
TABLE1 CREATED
CREATE TABLE TABLE2 (VALUE INTEGER, I INTEGER);
TABLE2 CREATED
INSERT INTO TABLE1 VALUES ((5), (3));
INSERTED 2 ROWS
INSERT INTO TABLE2 VALUES ((5, 0), (5, 3), (3,0), 3, 0));
INSERTED 4 ROWS
And display them to ensure they're as expected:
SELECT * FROM TABLE1; SELECT * FROM TABLE2;
VALUE
-----
5
3
VALUE I
----- -
5 0
5 3
3 0
3 0
Then run the command to get rid of the relevant rows:
DELETE FROM TABLE1 WHERE VALUE IN
(SELECT VALUE FROM TABLE2 GROUP BY VALUE HAVING MAX(I) = 0);
DELETED 1 ROW
And you can see that the 3` row has disappeared from table 1 as desired:
SELECT * FROM TABLE1; SELECT * FROM TABLE2;
VALUE
-----
5
VALUE I
----- -
5 0
5 3
3 0
3 0