select data based on the next rows and ids - sql

I have the following table and data,
TABLE - DEAL
ID DEALID
1 DEAL1
2 DEAL2
ALL DEAL2
3 DEAL2
4 DEAL5
5 DEAL5
ALL DEAL6
I want to get only the data as below
ID DEALID
1 DEAL1
4 DEAL5
5 DEAL5
ALL DEAL6
I want to select data based on the value of id column and dealid column.
If the value of id is 'ALL' and corresponding dealid repeats, omit all records with that dealid

Based on sample data you posted, see if this helps. Read comments within code.
SQL> with
2 test (id, dealid) as
3 -- sample data
4 (select '1' , 'deal1' from dual union all
5 select '2' , 'deal2' from dual union all
6 select 'ALL', 'deal2' from dual union all
7 select '3' , 'deal2' from dual union all
8 select '4' , 'deal5' from dual union all
9 select '5' , 'deal5' from dual union all
10 select 'ALL', 'deal6' from dual
11 ),
12 all2 as
13 -- DEALIDs that contain different ID values, and MAX of them is ALL
14 (select dealid
15 from test
16 group by dealid
17 having min(id) <> max(id)
18 and max(id) = 'ALL'
19 )
20 select t.id, t.dealid
21 from test t join all2 a on a.dealid <> t.dealid;
ID DEALI
--- -----
1 deal1
4 deal5
5 deal5
ALL deal6
SQL>

Related

Count occurences of values in table, when I treat one value as occurence of all other values

I have a table with one column (just to simplify the problem) with values 0-23 or *.
I want to count occurrences of each value 0-23, but treat * as occurrence of all other values
for example:
column_name
-------------
3
4
5
6
7
*
4
4
3
*
I want to get something like that:
column_name | count
--------------------
1 | 2
2 | 2
3 | 4
4 | 5
5 | 3
6 | 3
7 | 3
.....
I tried experimenting with different count and "group by" methods, but always getting very strange results. Basically the main problem here is to how count rows when I need to have one value in all other groups.
You could use analytic function that counts values where * is replaced by actual value between 0 and 23:
SELECT DISTINCT n.RN "COL_1", Count(REPLACE(t.COL_1, '*', n.RN)) OVER(Partition By n.RN) "CNT"
FROM tbl t
INNER JOIN ( Select To_Char(LEVEL - 1) "RN" From Dual Connect By LEVEL <=24 ) n ON(n.RN = REPLACE(t.COL_1, '*', n.RN))
WHERE n.RN IN(SELECT COL_1 FROM tbl)
ORDER BY To_Number(n.RN)
which with your sample data:
WITH
tbl (COL_1) AS
(
Select '3' From Dual Union All
Select '4' From Dual Union All
Select '5' From Dual Union All
Select '6' From Dual Union All
Select '7' From Dual Union All
Select '*' From Dual Union All
Select '4' From Dual Union All
Select '4' From Dual Union All
Select '3' From Dual Union All
Select '*' From Dual Union All
Select '3' From Dual
)
... results as:
COL_1 CNT
---------------------------------------- ----------
3 5
4 5
5 3
6 3
7 3
... and if you exclude the Where clause you will get all the rows (0 - 23) with number of occurances counted by REPLACE of * with any of the numbers
COL_1 CNT
---------------------------------------- ----------
0 2
1 2
2 2
3 5
4 5
5 3
6 3
7 3
8 2
9 2
10 2
11 2
12 2
13 2
14 2
15 2
16 2
17 2
18 2
19 2
20 2
21 2
22 2
23 2
You can do it using successive WITH's :
First one to calculate number of occurrence of *.
And the second is to calculate number of occurrence of each number.
with cte as (
select count(1) as c
from mytable
where column_name = '*'
),
cte2 as (
select column_name, count(1) as c
from mytable, cte
group by column_name
)
select column_name, cte.c + cte2.c
from cte2, cte;
You can with nested statements too,
SELECT ID,(count_ + (
SELECT COUNT(ID) FROM sql_test_a
WHERE ID = '*')) as count_
FROM (
SELECT ID,COUNT(ID) as count_
FROM sql_test_a WHERE ID != '*' GROUP BY ID);

Filter based on condition in WHERE clause

I have a table where I have to pick one of two if it is present. For example if a ID has ACCEPTED and SETTLED , I have to only pick SETTLED else the remaining. Only ACCEPTED/SETTLED always comes as duplicates
Input:
Output:
Query Tried:
SELECT * FROM TABLE
WHERE CASE WHEN "Status" IN ('ACCEPTED','SETTLED') THEN 'SETTLED'
WHEN "Status" IN ('ACCEPTED') THEN 'ACCEPTED'
ELSE "Status" END In ('SETTLED','ACCEPTED')
If your groups are defined by ID and Amount, you could do something like:
SELECT
t.ID,
MAX(t.Status),
t.Amount
FROM t
GROUP BY t.ID, t.Amount
ORDER BY t.ID
db<>fiddle
This is one option (sample data in lines #1 - 7; query begins at line #8). It ranks statuses so that SETTLED comes first, and then the rest of them.
SQL> with test (id, status, amount) as
2 (select 1, 'ACCEPTED', 13 from dual union all
3 select 1, 'SETTLED' , 13 from dual union all
4 select 2, 'SETTLED' , 155 from dual union all
5 select 3, 'ACCEPTED', 123 from dual union all
6 select 4, 'REJECTED', 140 from dual
7 )
8 select id, status, amount
9 from (select id, status, amount,
10 row_number() over (partition by id
11 order by case when status = 'SETTLED' then 1 else 2 end) rn
12 from test
13 )
14 where rn = 1;
ID STATUS AMOUNT
---------- -------- ----------
1 SETTLED 13
2 SETTLED 155
3 ACCEPTED 123
4 REJECTED 140
SQL>

Conditional union in Oracle query

I have a query which gives me 2 columns,
select
name as name,
code as code
from
table1
UNION
select
name as name,
code as code
from
table2
I would like to apply one more union in the result if from the above query i did not get a row with name as 'Default'. So if above query didnt have a record with default name i need to have 1 more union with above query:
select
'Default' as name,
code as code
from
table1
where condition = condition
I tried putting first query in a view and use not exists function in second query but it gives column not found error.
This is how I understood the question: lines #1 - 9 represent sample data; one of rows contains the Default name, so your resulting query should return the union-ed result as is:
SQL> with
2 table1 (name, code) as
3 (select 'Little', 1 from dual union all
4 select 'Foot' , 2 from dual
5 ),
6 table2 (name, code) as
7 (select 'Default', 3 from dual union all --> Default is here
8 select 'Oracle' , 4 from dual
9 ),
10 -- the "original" union
11 oriun as
12 (select name, code from table1
13 union
14 select name, code from table2
15 )
16 select name, code from oriun
17 union
18 select 'Default' name, null code from table1
19 where not exists (select null from oriun
20 where name = 'Default'
21 )
22 order by code;
NAME CODE
------- ----------
Little 1
Foot 2
Default 3
Oracle 4
SQL>
But, if there's no Default in those tables (see change made in line #7), then you'd get an "extra" Default row:
SQL> with
2 table1 (name, code) as
3 (select 'Little', 1 from dual union all
4 select 'Foot' , 2 from dual
5 ),
6 table2 (name, code) as
7 (select 'xxx', 3 from dual union all --> No more Default here
8 select 'Oracle' , 4 from dual
9 ),
10 -- the "original" union
11 oriun as
12 (select name, code from table1
13 union
14 select name, code from table2
15 )
16 select name, code from oriun
17 union
18 select 'Default' name, null code from table1
19 where not exists (select null from oriun
20 where name = 'Default'
21 )
22 order by code;
NAME CODE
------- ----------
Little 1
Foot 2
xxx 3
Oracle 4
Default
SQL>
When you have order by in union you need to put it inside a view or create a block with select * from (.. order by)

Join two tables with a column with multiple entries for the other table

I have the following problem.
I want to join two tables.
The first table has entries like the following:
T1
PK Info
1 one
2 two
3 three
The second table is build like this:
T2
PK FKT1
1 1,3
2 1,2,3
3 2
My Result should show the following
PK2 FKT1 InfoT1
1 1,3 One,Three
2 1,2,3 One,two,Three
3 2 Two
I just cant get an idea how to solve this.
Is this possible only using sql selects or is a function needed?
kind regards
It's not that difficult, but - as you were told, you'd rather NOT do that.
SQL> with
2 t1 (pk, info) as
3 (select 1, 'one' from dual union
4 select 2, 'two' from dual union
5 select 3, 'three' from dual
6 ),
7 t2 (pk, fkt1) as
8 (select 1, '1,3' from dual union
9 select 2, '1,2,3' from dual union
10 select 3, '2' from dual
11 ),
12 t2rows as
13 (select pk, regexp_substr(fkt1, '[^,]+', 1, column_value) fkt1, column_value rn
14 from t2,
15 table(cast(multiset(select level from dual
16 connect by level <= regexp_count(fkt1, ',') + 1
17 ) as sys.odcinumberlist))
18 )
19 select t2r.pk,
20 listagg(t2r.fkt1, ',') within group (order by t2r.rn) fkt1,
21 listagg(t1.info, ',') within group (order by t2r.rn) infot1
22 from t2rows t2r join t1 on t2r.fkt1 = t1.pk
23 group by t2r.pk
24 order by t2r.pk;
PK FKT1 INFOT1
---------- -------------------- --------------------
1 1,3 one,three
2 1,2,3 one,two,three
3 2 two
SQL>

Group the column value based on selective rows for an id

I have a table which have 4 dimensions for a foreignid.
I want to find unique combination based on 2 dimensions.
TABLE1
-----------------------------
ID NAME VALUE TABLE2ID
-----------------------------
1 TYPE 10 1
2 DIR IN 1
3 STATE MA 1
4 COUNT 100 1
5 TYPE 10 2
6 DIR IN 2
7 STATE SA 2
8 COUNT 200 2
9 TYPE 20 3
10 DIR OUT 3
11 STATE MA 3
12 COUNT 300 3
-----------------------------
Here, I want the TABLE2IDs based on the combination of TYPE and DIR rows which is unique.
So, here if you aggregate the row values based on TYPE and DIR you will get
-----------------------------
TYPE DIR TABLE2ID
-----------------------------
10 IN 1
10 IN 2
20 OUT 3
-----------------------------
Note:
The above question is answered
Additional Question related to this.
I have another table which have the count for table2 id based on hour.
I want to group all the count for all hours in a day for unique combination in table1(Don't worry about table 2 structure).
TABLE3
-----------------------------
ID TIME COUNT TABLE2ID
-----------------------------
1 2016101601 10 1
2 2016101602 20 1
3 2016101603 30 1
4 2016101604 40 1
5 2016101601 10 2
6 2016101602 20 2
7 2016101603 30 2
8 2016101604 40 2
9 2016101601 10 3
10 2016101602 20 3
11 2016101603 30 3
12 2016101604 40 3
-----------------------------
Here, I want the output be grouped based on unique value of table 1 according to type and name(regardless of table2id)
----------------------------------
TYPE DIR DATE COUNT
----------------------------------
10 IN 20161016 200
20 OUT 20161016 100
---------------------------------
Use a PIVOT:
Oracle Setup:
CREATE TABLE table1 ( id, name, value, table2id ) AS
SELECT 1, 'TYPE', '10', 1 FROM DUAL UNION ALL
SELECT 2, 'DIR', 'IN', 1 FROM DUAL UNION ALL
SELECT 3, 'STATE', 'MA', 1 FROM DUAL UNION ALL
SELECT 4, 'COUNT', '100', 1 FROM DUAL UNION ALL
SELECT 5, 'TYPE', '10', 2 FROM DUAL UNION ALL
SELECT 6, 'DIR', 'IN', 2 FROM DUAL UNION ALL
SELECT 7, 'STATE', 'SA', 2 FROM DUAL UNION ALL
SELECT 8, 'COUNT', '200', 2 FROM DUAL UNION ALL
SELECT 9, 'TYPE', '20', 3 FROM DUAL UNION ALL
SELECT 10, 'DIR', 'OUT', 3 FROM DUAL UNION ALL
SELECT 11, 'STATE', 'MA', 3 FROM DUAL UNION ALL
SELECT 12, 'COUNT', '300', 3 FROM DUAL;
Query:
SELECT *
FROM ( SELECT name, value, table2id FROM table1 )
PIVOT ( MAX(value) FOR name IN ( 'TYPE' AS type, 'DIR' AS DIR ) );
Output:
TABLE2ID TYPE DIR
-------- ---- ---
1 10 IN
2 10 IN
3 20 OUT
Or as an alternative:
SELECT table2id,
MAX( CASE WHEN name = 'TYPE' THEN value END ) AS type,
MAX( CASE WHEN name = 'DIR' THEN value END ) AS dir
FROM table1
GROUP BY table2id;
You could join two subqueries, one that selects the types and one that selects the dirs for the same id:
SELECT type, dir, a.table2id
FROM (SELECT value AS type, table2id
FROM table1
WHERE name = 'TYPE') a
JOIN (SELECT value AS dir, table2id
FROM table1
WHERE name = 'DIR') b ON a.table2id = b.table2id