SQL grouping by one value in 2 columns - sql

SQL grouping by one value in 2 columns
Source data table:
P2 P3
----------
1 2
2 1
2 3
4 1
I want a query that counts a's and b's in each column, producing something like:
num conut
-------------
1 3
2 3
3 1
4 1

You can do this using union all and group by:
select num, sum(cnt) as conut
from (select p2 as num, count(*) as cnt from source group by p2
union all
select p3 as num, count(*) as cnt from source group by p3
) p
group by num;

Related

Find records on group level which are connected to all other record within the group

I have a scenario where I have to find IDs within each group which are connected to all other IDs in the same group. So basically we have to treat each group separately.
In the table below, the group A has 3 IDs 1, 2 and 3. 1 is connected to both 2 and 3, 2 is connected to both 1 and 3, but 3 is not connected to 1 and 2. So 1 and 2 should be output from group A. Similarly in group B only 5 is connected to all other IDs namely 4 and 6 within group B, so 5 should be output. Similarly from group C, that should be 8, and from group D no records should be output.
So the output of the select statement should be 1, 2, 5, 8.
GRP
ID
CONNECTED_TO
A
1
2
A
1
3
A
2
3
A
2
1
A
3
5
B
4
5
B
5
4
B
5
6
B
6
4
C
7
21
C
7
25
C
8
7
D
9
31
D
10
35
D
11
37
I was able to do this if group level was not required, by below SQL:
SELECT ID FROM <table>
where CONNECTED_TO in (select ID from <table>)
group by ID
having count(*) = <number of records - 1>
But not able to find correct SQL for my scenario. Any help is appreciated.
You may use count and count(distinct) functions as the following:
select id
from tbl T
where connected_to in
(
select id from tbl T2
where T2.grp = T.grp
)
group by grp, id
having count(connected_to) =
(
select count(distinct D.id) - 1
from tbl D
where T.grp = D.grp
)
When count(connected_to) group by grp, id equals to the count(distinct id) - 1 with the same grp, this means that the ID is connected to all other IDs.

Oralce sql:I want to select the TOP 3 Records [duplicate]

This question already has answers here:
How do I limit the number of rows returned by an Oracle query after ordering?
(14 answers)
Closed 8 months ago.
I want to select the TOP 3 Records ordered desc by 'cnt'
this is top 4
a b c cnt
99 YC 市購件異常 3
99 LY 漏油 2
99 QT16 其他異常 2
99 JGSH 機構損壞 1
then
select * from ()where rownum<= 3 order by cnt desc
get data
99 YC 市購件異常 3
99 LY 漏油 2
99 JGSH 機構損壞 1
i want to get
99 YC 市購件異常 3
99 LY 漏油 2
99 QT16 其他異常 2
Try this:
SELECT T.a, T.b, T.c, T.cnt
FROM
(
SELECT *, RANK() OVER(PARTITION BY a ORDER BY cnt DESC) RNK
FROM TEST_TBL
) T
WHERE T.RNK <= 3
It looks like you want to keep "duplicates" (in the cnt column) in the result.
In that case, I'd say that it is row_number analytic function that helps:
Sample data:
SQL> with test (a, b, cnt) as
2 (select 99, 'yc' , 3 from dual union all
3 select 99, 'ly' , 2 from dual union all
4 select 99, 'qt16', 2 from dual union all
5 select 99, 'jgsh', 1 from dual union all
6 --
7 select 99, 'abc' , 2 from dual --> yet another row with CNT = 2
8 ),
Query begins here: first rank rows (line #11), and then return the top 3 (line #15):
9 temp as
10 (select a, b, cnt,
11 row_number() over (partition by a order by cnt desc) rnk
12 from test
13 )
14 select * from temp
15 where rnk <= 3;
A B CNT RNK
---------- ---- ---------- ----------
99 yc 3 1
99 ly 2 2
99 abc 2 3
SQL>
Because, if you use rank analytic function (as Hana suggested), you might get more than desired 3 rows (see the rnk column's values) (depending on data you work with, of course; rank works with data you posted, but - if there are more rows that share the same cnt value, it won't work any more):
<snip>
9 temp as
10 (select a, b, cnt,
11 rank() over (partition by a order by cnt desc) rnk
12 from test
13 )
14 select * from temp
15 where rnk <= 3;
A B CNT RNK
---------- ---- ---------- ----------
99 yc 3 1
99 ly 2 2
99 abc 2 2
99 qt16 2 2
SQL>

Select quantity on a 1st table based on a total quantity the 2nd table

Table 1
ID
Grp
Qty
1
A
5
2
A
4
3
B
5
4
B
3
5
B
2
6
C
14
7
D
1
8
D
1
9
E
2
10
E
2
11
E
1
12
E
1
Table 2
ID
Grp
Qty
1
A
7
2
B
9
3
C
13
4
D
1
5
E
4
Select/Output
ID
Grp
Qty
1
A
0
2
A
2
3
B
0
4
B
0
5
B
1
6
C
1
7
D
0
8
D
1
9
E
0
10
E
0
11
E
1
12
E
1
I want to select a row on a 1st table with a specific quantity based on the total quantity of the 2nd table. The result is on the 3rd table. Please see sample tables above, I really appreciate a help, thank you so much and sorry it was my first time asking a question here.
I have tried this code on both 2 tables
WITH tbl AS(
SELECT ID,
Qty,
Grp,
ROW_NUMBER() OVER (PARTITION BY Grp)AS Rown,
SUM(Qty) OVER (PARTITION BY Grp)AS Total
FROM Table1
)
SELECT * FROM tbl WHERE Rown = 1
But I am not able to select the specific rows on Table 1 because it only select the 1st row and total the quantity. Every row on table 1 has its own quantity.
You could use a cumulative windowed aggregates and then a CASE expression to achieve this:
--Saple Data
WITH Table1 AS(
SELECT *
FROM (VALUES(1,'A',5),
(2,'A',4),
(3,'B',5),
(4,'B',3),
(5,'B',2),
(6,'C',14))V(ID,Grp,Qty)),
Table2 AS(
SELECT *
FROM (VALUES(1,'A',7),
(2,'B',9),
(3,'C',13))V(ID,Grp,Qty)),
--Solution
CTE AS(
SELECT T1.ID,
T1.Grp,
T1.Qty,
SUM(T1.Qty) OVER (PARTITION BY T1.Grp ORDER BY T1.Id
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) AS RunningQty,
T2.Qty AS T2Qty
FROM Table1 T1
JOIN Table2 T2 ON T1.Grp = T2.Grp)
SELECT C.ID,
C.Grp,
CASE WHEN C.RunningQty <= C.T2Qty THEN C.Qty
ELSE C.T2Qty - LAG(C.RunningQty,1,0) OVER (PARTITION BY C.Grp ORDER BY C.ID)
END AS Qty
FROM CTE C;

Count the number of unique values with at least k occurrences per group in postgres

I have a table with 3 columns that looks like this :
ID | obs_type | Value
1 A 0.1
1 A 0.2
1 B 0.4
2 B 0.5
2 C 0.2
2 C 0.3
3 B 0.1
I want to have the count of IDs with at least k observations in each group Type.
In the example above, if k = 2 (at least 2 observations of the same ID to be counted), I would like to have :
obs_type | count
A 1
B 0
C 1
As there is a single ID with two observations of type A and single ID with two observations of type C.
There are no ID with two observations of type B.
For k = 1, I just do :
SELECT obs_type, COUNT(DISTINCT ID ) FROM table_x GROUP BY obs_type;
But I'm looking for a solution that would work for arbitrary k.
Thanks !!!!
Do the aggregation in two steps:
k = 2 here:
select count(case when cnt >= 2 then cnt end), obs_type
from
(
select count(*) cnt, obs_type
from table_x
group by id, obs_type
) dt
group by obs_type
The derived table (subquery) returns:
cnt obs_type
================ ========
2 A
1 B
1 B
2 C
1 B
Then use a case expression to do conditional aggregation, and you'll get:
SQL>select count(case when cnt >= 2 then cnt end), obs_type
SQL&from
SQL&(
SQL& select count(*) cnt, obs_type
SQL& from table_x
SQL& group by id, obs_type
SQL&) dt
SQL&group by obs_type;
obs_type
==================== ========
1 A
0 B
1 C
3 rows found

Filter out entire group based on item ranking in SQL

I have a table as shown below:
group item rank
1 A 1
1 B 2
1 C 3
2 A 2
2 B 1
3 A 1
3 C 2
I want those groups data only, where item A has rank 1 as shown below:
group item rank
1 A 1
1 B 2
1 C 3
3 A 1
3 C 2
In group 2, A has rank 2, therefore not a part of output.
One way is using an IN clause
select *
from yourTable
where id in (select id from yourtable where item='A' and rank = 1)
you could use a subquery for get the involved id and the join
select * from my_table m
inner join (
select distinct id
from my_table
where item = 'A'
and rank = 1
) t on t.id = m.id