compare within groups to fetch common value - sql

I have a table with the follwing data
Case 1:
table1
-------------
id type
-------------
1 X
1 Y
2 X
3 Z
3 X
-------------
Now as you see X is common to all the id ,so i need to return X in this case
Case2 :
table1
-------------
id type
-------------
1 X
1 Y
2 X
2 Y
3 X
3 Y
--------------
In this case both X and Y are common,then i need to return both Xand Y comma seperated (X,y)
Case 3
table1
-------------
id type
-------------
1 X
1 Y
2 X
2 Y
3 X
3 Y
4 NULL
------------------
If a null came to any of the record , i need to return NULL
Actually ,the data i have shouwn you , is been populated from 3 tables , so i have already written the query for that ,but now i need to compare the groups for the common data within groups ,that is confusing me ,how to compare the groups ?
Note :Here group is based on ID
Any help would be appriciated

you could count the occurances compared to the count of the IDs?
with data as (select rownum id, 'X' type from dual connect by level <= 3
union all
select rownum id, 'Y' type from dual connect by level <= 3
union all
select 3 id, 'Z' type from dual)
select wm_concat(distinct type)
from (select type, count(*) over (partition by type) cnt, count(distinct id) over () total_ids
from data)
where cnt = total_ids;
in 11g you have LISTAGG instead of WM_CONCAT of course. if for each id, the same type occurs many times, you can change count(*) over (partition by type) to count(distinct id) over (partition by type)
edit:
If you had
3, Z
3, NULL
(rather than 4, NULL) and also want to return a NULL rather than a delimited list in that case then you could add a check (with the 4, NULL above it would return a null even on the prior SQL version as the counts would'nt tie up):
with data as (select rownum id, 'X' type from dual connect by level <= 3
union all
select rownum id, 'Y' type from dual connect by level <= 3
union all
select 3 id, 'Z' type from dual)
select wm_concat(distinct type)
from (select type, count(*) over (partition by type) cnt, count(distinct id) over
() total_ids,
max(case when type is null then 'Y' else 'N' end) over () has_null_in_set
from data)
where cnt = total_ids
and has_null_in_set = 'N';

Related

How do i select all columns, plus the result of the sum

I have this select:
"Select * from table" that return:
Id
Value
1
1
1
1
2
10
2
10
My goal is create a sum from each Value group by id like this:
Id
Value
Sum
1
1
2
1
1
2
2
10
20
2
10
20
I Have tried ways like:
SELECT Id,Value, (SELECT SUM(Value) FROM Table V2 WHERE V2.Id= V.Id GROUP BY IDRNC ) FROM Table v;
But the is not grouping by id.
Id
Value
Sum
1
1
1
1
1
1
2
10
10
2
10
10
Aggregation aggregates rows, reducing the number of records in the output. In this case you want to apply the result of a computation to each of your records, task carried out by the corresponding window function.
SELECT table.*, SUM(Value) OVER(PARTITION BY Id) AS sum_
FROM table
Check the demo here.
Your attempt looks correct.
Can you try the below query :
It works for me :
SELECT Id, Value,
(SELECT SUM(Value) FROM Table V2 WHERE V2.Id= V.Id GROUP BY ID) as sum
FROM Table v;
You can do it using inner join to join with selection grouped by id :
select t.*, sum
from _table t
inner join (
select id, sum(Value) as sum
from _table
group by id
) as s on s.id = t.id
You can check it here
Your select is ok if you adjust it just a little:
SELECT Id,Value, (SELECT SUM(Value) FROM Table V2 WHERE V2.Id= V.Id GROUP BY IDRNC ) FROM Table v;
GROUP BY IDRNC is a mistake and should be GROUP BY ID
you should give an alias to a sum column ...
subquery selecting the sum does not have to have self table alias to be compared with outer query that has one (this is not a mistake - works either way)
Test:
WITH
a_table (ID, VALUE) AS
(
Select 1, 1 From Dual Union All
Select 1, 1 From Dual Union All
Select 2, 10 From Dual Union All
Select 2, 10 From Dual
)
SELECT ID, VALUE, (SELECT SUM(VALUE) FROM a_table WHERE ID = v.ID GROUP BY ID) "ID_SUM" FROM a_table v;
ID VALUE ID_SUM
---------- ---------- ----------
1 1 2
1 1 2
2 10 20
2 10 20

How to calculate leave-out-median using PL/SQL analytic function

I'm trying to write an analytic function in PL/SQL that, when applied to a column within a table, returns for each row in the table, the median of the column excluding the given row.
An example to clarify: Suppose I have a table TABLE consisting of one column X that takes on the following values:
1
2
3
4
5
I want to define an analytic function LOOM() such that:
SELECT LOOM(X)
FROM TABLE
delivers the following:
3.5
3.5
3
2.5
2.5
i.e., for each row, the median of X, excluding the given row. I've been struggling to build the desired LOOM() function.
I'm not sure if there is a "clever" way to do this. You can do the calculation with a correlated subquery.
Assuming the x values are unique -- as in your example --
with t as (
select 1 as x from dual union all
select 2 as x from dual union all
select 3 as x from dual union all
select 4 as x from dual union all
select 5 as x from dual
)
select t.*,
(select median(x)
from t t2
where t2.x <> t.x
) as loom
from t;
EDIT:
A more efficient method uses analytic functions but requires more direct calculation of the median. For instance:
with t as (
select 1 as x from dual union all
select 2 as x from dual union all
select 3 as x from dual union all
select 4 as x from dual union all
select 5 as x from dual
)
select t.*,
(case when mod(cnt, 2) = 0
then (case when x <= candidate_1 then candidate_2 else candidate_1 end)
else (case when x <= candidate_1 then (candidate_2 + candidate_3)/2
when x = candidate_2 then (candidate_1 + candidate_3)/2
else (candidate_1 + candidate_2) / 2
end)
end) as loom
from (select t.*,
max(case when seqnum = floor(cnt / 2) then x end) over () as candidate_1,
max(case when seqnum = floor(cnt / 2) + 1 then x end) over () as candidate_2,
max(case when seqnum = floor(cnt / 2) + 2 then x end) over () as candidate_3
from (select t.*,
row_number() over (order by x) as seqnum,
count(*) over () as cnt
from t
) t
) t

Select values from different rows of same column INTO multiple variables in Oracle SQL

Here's the example:
ID | value
1 51
2 25
3 11
4 27
5 21
I need to get first three parameters and place them into variables e.g. out_x, out_y, out_z.
Is it possible to do it without multiple selects?
You can do something like this:
select max(case when id = 1 then value end),
max(case when id = 2 then value end),
max(case when id = 3 then value end)
into out_x, out_y, out_z
from t
where id in (1, 2, 3);
However, I think three queries of the form:
select value into out_x
from t
where id = 1;
is a cleaner approach.
You can use a PIVOT:
SELECT x, y, z
INTO out_x, out_y, out_z
FROM your_table
PIVOT ( MAX( value ) FOR id IN ( 1 AS x, 2 AS y, 3 AS z ) )
Or, if you do not know which IDs you need (but just want the first 3) then:
SELECT x, y, z
INTO out_x, out_y, out_z
FROM (
SELECT value, ROWNUM AS rn
FROM ( SELECT value FROM your_table ORDER BY id )
WHERE ROWNUM <= 3
)
PIVOT ( MAX( value ) FOR rn IN ( 1 AS x, 2 AS y, 3 AS z ) )

Enumerating rows in a inner join

My table
id name num
1 a 3
2 b 4
I need to return every row num number of times. I do it this way.
select DB.BAN_KEY as BAN_KEY, DB.CUST_FULLNAME as CUST_FULLNAME
from TST_DIM_BAN_SELECTED DB
inner join (select rownum rn from dual connect by level < 10) a
on a.rn <= DB.N
There resulting table looks like this.
id name
1 a
1 a
1 a
2 b
2 b
2 b
2 b
But I also need every row in the group to be numbered like this.
id name row_num
1 a 1
1 a 2
1 a 3
2 b 1
2 b 2
2 b 3
2 b 4
How can I do it?
You don't need an inner join to a dummy table or an analytic function to generate the row numbers; you could just use connect by (and its corresponding level function) on the table itself, like so:
WITH tst_dim_ban_selected AS (SELECT 1 ban_key, 'a' cust_fullname, 3 n FROM dual UNION ALL
SELECT 2 ban_key, 'b' cust_fullname, 4 n FROM dual)
-- end of mimicking your table with data in it. See SQL below
SELECT db.ban_key,
db.cust_fullname,
LEVEL row_num
FROM tst_dim_ban_selected db
CONNECT BY LEVEL <= db.n
AND PRIOR db.ban_key = db.ban_key -- assuming this is the primary key
AND PRIOR sys_guid() IS NOT NULL;
BAN_KEY CUST_FULLNAME ROW_NUM
---------- ------------- ----------
1 a 1
1 a 2
1 a 3
2 b 1
2 b 2
2 b 3
2 b 4
If you have other columns than ban_key in the table's primary key, you need to make sure they are included in the connect by clause's list of prior <column> = <column>s. This is so the connect by can identify each row uniquely, meaning that it's looping just over that row and no others. The PRIOR sys_guid() IS NOT NULL is required to prevent connect by loops from occurring.
You can use analytic function for this:
Select id, name,
row_number() over (partition by id, name order by id, name)
From(/* your query */) t;
This can be done without subquery:
Select id, name,
row_number() over (partition by id, name order by id, name)
From /* joins */
You could use this:
SELECT db.ban_key AS ban_key, db.cust_fullname AS cust_fullname,
ROW_NUMBER() OVER (PARTITION BY db.n ORDER BY db.ban_key) AS row_num
FROM tst_dim_ban_selected db
INNER JOIN (SELECT rownum rn FROM dual CONNECT BY level < 10) a
ON a.rn <= db.n;
Use a recursive sub-query factoring clause:
WITH split ( id, name, rn, n ) AS (
SELECT BAN_KEY, CUST_FULLNAME, 1, N
FROM TST_DIM_BAN_SELECTED
UNION ALL
SELECT id, name, rn + 1, n
FROM split
WHERE rn < n
)
SELECT id, name, rn
FROM split;

how to assign a rank for null values with previous first non-null value in oracle

I need to assign a rank to some null values over ordered rows.
My query is like this :
with sub as
(
select 10 as id, 1 as inx,2 as num from dual
union all
select 10 as id, 2 as inx,null as num from dual
union all
select 10 as id, 3 as inx,8 as num from dual
union all
select 10 as id, 4 as inx,null as num from dual
)
select *
from sub order by inx
and result set is like this :
id inx num
----------
10 1 2
10 2 null
10 3 8
10 4 null
i'm tring to set null values with previous first non-null value
for example : num null value should be "2" where inx = 2
and num null value should be "8" where inx = 4 and so on.
thx for any idea..
If you know that the values are increasing, you can just use max():
select id, inx, max(num) over (partition by id order by inx) as num
If they are not increasing and multiple nulls never appear in a sequence, you can use lag():
select id, inx,
(case when num is null
then lag(num) over (partition by id order by inx)
else num
end)as null;
If nulls do appear in a sequence, you can use the ignore nulls option to lag():
select id, inx,
(case when num is null
then lag(num ignore nulls) over (partition by id order by inx)
else num
end)as null