Difference of SUM while doing multiple group by - sql

I have table like below
Pid
qid
count
sum
11
13
5
800
12
13
7
300
11
14
3
100
12
14
5
200
I need to get the difference of sum while doing group by using pid and qid
Like
SELECT
/* ??? */
FROM
tbl
GROUP BY
pid,
qid
Id1
id2
qid
difference
11
12
13
500

Use conditional aggregation:
SELECT 11 AS id1,
12 AS id2,
qid,
ABS(SUM(CASE pid WHEN 11 THEN value WHEN 12 THEN -value END)) AS diff
FROM tbl
GROUP BY qid;
Which, for the sample data:
CREATE TABLE tbl (Pid, qid, value) AS
SELECT 11, 13, 160 FROM DUAL CONNECT BY LEVEL <= 5 UNION ALL
SELECT 12, 13, 43 FROM DUAL CONNECT BY LEVEL <= 6 UNION ALL
SELECT 12, 13, 42 FROM DUAL CONNECT BY LEVEL <= 1 UNION ALL
SELECT 11, 14, 30 FROM DUAL CONNECT BY LEVEL <= 2 UNION ALL
SELECT 11, 14, 40 FROM DUAL CONNECT BY LEVEL <= 1 UNION ALL
SELECT 12, 14, 40 FROM DUAL CONNECT BY LEVEL <= 5;
Outputs:
ID1
ID2
QID
DIFF
11
12
13
500
11
12
14
100
fiddle

Looks like you want a difference of sum between all pairs of different pid per each qid. The easiest and most reliable solution in this case would be to use a simple self join, like this:
with agg as (
select pid, qid
, count(*) as cnt
, sum(?) as sum1
from your_table
group by pid, qid
)
select
t1.pid as id1
,t2.pid as id2
,abs(t2.sum1-t1.sum1) as diff
from agg t1
join agg t2
on t1.qid=t2.qid
and t1.pid<t2.pid
Obviously, we could optimize it if we were sure that you have just 2 different PIDs per each qid, but you haven't mention it

Related

SQL logic to fail a check if any of the related customers has failed

I have the requirement to flag the customers Y only when all the related customers have also passed the check.
below are the two tables:
relationship table :
customer_id related_customer
1 1
1 2
1 3
2 1
2 2
2 3
3 1
3 2
3 3
11 11
11 22
22 11
22 22
Check table
customer_id check_flag
1 y
2 y
3 n
11 y
22 y
I want output like below:
customer_id paas_fail_flag
1 n
2 n
3 n
11 y
22 y
output justification: since 1,2,3 are related customers and since one of them (3) has n in table 2 , so all the related customers should also have n.
11,22 are related customers and both have y in table 2.so in output both should have y.
You need to join relationship to check and use conditional aggregation:
SELECT r.customer_id,
COALESCE(MAX(CASE WHEN c.check_flag = 'n' THEN c.check_flag END), 'y') paas_fail_flag
FROM relationship r INNER JOIN "check" c
ON c.customer_id = r.related_customer
GROUP BY r.customer_id
ORDER BY r.customer_id
See the demo.
Something like this? Sample data in lines #1 - 40; query begins at line #41:
SQL> WITH
2 -- sample data
3 rel (customer_id, related_customer)
4 AS
5 (SELECT 1, 1 FROM DUAL
6 UNION ALL
7 SELECT 1, 2 FROM DUAL
8 UNION ALL
9 SELECT 1, 3 FROM DUAL
10 UNION ALL
11 SELECT 2, 1 FROM DUAL
12 UNION ALL
13 SELECT 2, 2 FROM DUAL
14 UNION ALL
15 SELECT 2, 3 FROM DUAL
16 UNION ALL
17 SELECT 3, 1 FROM DUAL
18 UNION ALL
19 SELECT 3, 2 FROM DUAL
20 UNION ALL
21 SELECT 3, 3 FROM DUAL
22 UNION ALL
23 SELECT 11, 11 FROM DUAL
24 UNION ALL
25 SELECT 11, 22 FROM DUAL
26 UNION ALL
27 SELECT 22, 11 FROM DUAL
28 UNION ALL
29 SELECT 22, 22 FROM DUAL),
30 chk (customer_id, check_flag)
31 AS
32 (SELECT 1, 'y' FROM DUAL
33 UNION ALL
34 SELECT 2, 'y' FROM DUAL
35 UNION ALL
36 SELECT 3, 'n' FROM DUAL
37 UNION ALL
38 SELECT 11, 'y' FROM DUAL
39 UNION ALL
40 SELECT 22, 'y' FROM DUAL),
41 temp
42 AS
43 -- minimum CHECK_FLAG per customer and related customer
44 ( SELECT r.customer_id, r.related_customer, MIN (c.check_flag) mcf
45 FROM rel r JOIN chk c ON c.customer_id = r.related_customer
46 GROUP BY r.customer_id, r.related_customer)
47 SELECT customer_id, MIN (mcf) flag
48 FROM temp
49 GROUP BY customer_id
50 ORDER BY customer_id;
CUSTOMER_ID FLAG
----------- ----
1 n
2 n
3 n
11 y
22 y
SQL>
Assuming that your relationship data could be sparse, for example:
CREATE TABLE relationship ( customer_id, related_customer ) AS
SELECT 2, 3 FROM DUAL UNION ALL
SELECT 3, 1 FROM DUAL UNION ALL
SELECT 3, 2 FROM DUAL UNION ALL
SELECT 11, 22 FROM DUAL;
CREATE TABLE "CHECK" ( customer_id, check_flag ) AS
SELECT 1, 'y' FROM DUAL UNION ALL
SELECT 2, 'y' FROM DUAL UNION ALL
SELECT 3, 'n' FROM DUAL UNION ALL
SELECT 11, 'y' FROM DUAL UNION ALL
SELECT 22, 'y' FROM DUAL;
(Note: The below query will also work on your dense data, where every relationship combination is enumerated.)
Then you can use a hierarchical query:
SELECT customer_id,
MIN(check_flag) AS check_flag
FROM (
SELECT CONNECT_BY_ROOT(c.customer_id) AS customer_id,
c.check_flag AS check_flag
FROM "CHECK" c
LEFT OUTER JOIN relationship r
ON (r.customer_id = c.customer_id)
WHERE CONNECT_BY_ISLEAF = 1
CONNECT BY NOCYCLE
( PRIOR r.related_customer = c.customer_id
OR PRIOR c.customer_id = r.related_customer )
AND PRIOR c.check_flag = 'y'
)
GROUP BY
customer_id
ORDER BY
customer_id
Which outputs:
CUSTOMER_ID
CHECK_FLAG
1
n
2
n
3
n
11
y
22
y
db<>fiddle here

GROUP BY ID and select MAX

Good Evening,
I am working on a table like this in Oracle:
ID
BALANCE
SEQ
1
102
13
1
119
15
2
50
4
3
20
11
3
15
10
3
45
9
4
90
5
5
67
20
5
12
19
6
20
1
I want to select, for each ID, the BALANCE having MAX(SEQ).
So final result would be:
ID
BALANCE
SEQ
1
119
15
2
50
4
3
20
11
4
90
5
5
67
20
6
20
1
How can I do that?
I've tried several Group by queries but with no success.
Thanks for any help
One method is aggregation using keep:
select id,
max(balance) keep (dense_rank first order by seq desc) as balance,
max(seq)
from t
group by id;
You may use normal rank()
SELECT ID, BALANCE, SEQ FROM (
select
ID, BALANCE, SEQ, RANK() OVER (PARTITION BY ID ORDER BY SEQ DESC) ranks
from t
) WHERE ranks = 1
sample demo
SELECT ID, BALANCE, SEQ FROM (
SELECT ID, BALANCE, SEQ, RANK() OVER (PARTITION BY ID ORDER BY SEQ DESC) ranks
FROM (
SELECT 1 ID, 102 BALANCE, 13 SEQ FROM dual UNION all
SELECT 1, 119, 15 FROM dual UNION all
SELECT 2, 50, 4 FROM dual UNION all
SELECT 3, 20, 11 FROM dual UNION all
SELECT 3, 15, 10 FROM dual UNION all
SELECT 3, 45, 9 FROM dual UNION all
SELECT 4, 90, 5 FROM dual UNION all
SELECT 5, 67, 20 FROM dual UNION all
SELECT 5, 12, 19 FROM dual UNION all
SELECT 6, 20, 1 FROM dual
)
) WHERE ranks = 1
you can add it in your Big query as below
SELECT ID, BALANCE, SEQ FROM (
select
ID, BALANCE, SEQ, RANK() OVER (PARTITION BY ID ORDER BY SEQ DESC) ranks
from (**YOUR BIG QUERY HERE**)
) WHERE ranks = 1

Oracle query to display count and start, end of sequence

I know basics of Oracle and I am a java developer, I can do the following operation/task in java by fetching the data and iterate over it. But I would like to know that is there any way to show start and end of the sequence and the difference in between start and end using SQL(Oracle) query.
Let's say I have a table TB1 with a column seq which contains some sequential numbers
SEQ
------
1
2
3
7
8
9
14
19
20
Is there any way to display the sequence start, end and the count as follows.
Start | end | count
---------------------
1 3 3
7 9 3
14 14 1
19 20 2
Please give me some pointer if it is achievable or not.
Thanks in advance.
Yes. You could do it easily using TABIBITOSAN method.
SELECT MIN(seq)
,MAX(seq)
,count(*)
FROM (
SELECT seq
,seq - row_number() OVER (
ORDER BY seq
) grp
FROM t
)
GROUP BY grp
ORDER BY 1;
Demo
You should write a procedure, and loop in through cursor. Keep three temp variable, wherever there is break in seq(make sure you do Order by on Seq column), and insert the start,end and count into other table.
See similar example that may be helpful to you.
SQL> WITH cte_table (seq) AS (
2 SELECT 1 FROM dual UNION ALL
3 SELECT 2 FROM dual UNION ALL
4 SELECT 3 FROM dual UNION ALL
5 SELECT 7 FROM dual UNION ALL
6 SELECT 8 FROM dual UNION ALL
7 SELECT 9 FROM dual UNION ALL
8 SELECT 14 FROM dual UNION ALL
9 SELECT 19 FROM dual UNION ALL
10 SELECT 20 FROM dual),
11 table_ AS (
12 SELECT seq, seq - row_number() OVER (ORDER BY seq) grp FROM cte_table)
13 SELECT MIN(seq) "START",
14 MAX(seq) "END",
15 COUNT(*) "COUNT"
16 FROM table_
17 GROUP BY grp
18 ORDER BY 1;
Output:
START END COUNT
---------- ---------- ----------
1 3 3
7 9 3
14 14 1
19 20 2
Using your table, the query will be
WITH table_ AS (
SELECT seq, seq - row_number() OVER (ORDER BY seq) grp FROM tb1)
SELECT MIN(seq) "START",
MAX(seq) "END",
COUNT(*) "COUNT"
FROM table_
GROUP BY grp
ORDER BY 1;

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>

How to get Nth big item [duplicate]

This question already has answers here:
Finding Nth Minimum of a Varchar value in Oracle
(2 answers)
Closed 9 years ago.
I am getting 11th older person from users table with the following SQL statement
select MAX(age)
from ( select *
from (select *
from users
order by age asc)
where rownum <12)
is there a simplified and efficient query to get 11th older person with full information?
USING
Oracle 11G
WITH AgeOrderedPersons AS (
SELECT usr.*
,ROW_NUMBER() OVER (ORDER BY Age) AS Number
FROM Users usr
)
SELECT *
FROM AgeOrderedPersons
WHERE Number = 11
If you want all users with same age use DENSE_RANK() instead of ROW_NUMBER()
SQL Fiddle
Oracle 11g R2 Schema Setup:
CREATE TABLE users ( age, x ) AS
SELECT 20, 1 FROM DUAL
UNION ALL SELECT 80, 2 FROM DUAL
UNION ALL SELECT 47, 3 FROM DUAL
UNION ALL SELECT 33, 4 FROM DUAL
UNION ALL SELECT 24, 5 FROM DUAL
UNION ALL SELECT 7, 6 FROM DUAL
UNION ALL SELECT 102, 7 FROM DUAL
UNION ALL SELECT 99, 8 FROM DUAL
UNION ALL SELECT 90, 9 FROM DUAL
UNION ALL SELECT 28, 10 FROM DUAL
UNION ALL SELECT 46, 11 FROM DUAL
UNION ALL SELECT 54, 12 FROM DUAL
UNION ALL SELECT 67, 13 FROM DUAL
UNION ALL SELECT 17, 14 FROM DUAL
UNION ALL SELECT 34, 15 FROM DUAL
UNION ALL SELECT 32, 16 FROM DUAL
UNION ALL SELECT 39, 17 FROM DUAL
UNION ALL SELECT 26, 18 FROM DUAL
UNION ALL SELECT 15, 19 FROM DUAL
UNION ALL SELECT 12, 20 FROM DUAL;
Query 1:
SELECT DISTINCT
NTH_VALUE( age, 11 ) IGNORE NULLS OVER ( ORDER BY age ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS age,
NTH_VALUE( x, 11 ) IGNORE NULLS OVER ( ORDER BY age ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS x
FROM users
Results:
| AGE | X |
|-----|----|
| 34 | 15 |
Query 2:
Include an ordering clause in the statement with ROW_NUMBER():
WITH ranked AS (
SELECT u.*,
ROW_NUMBER() OVER ( ORDER BY age ) AS rn
FROM users u
ORDER BY age
)
SELECT age, x
FROM ranked
WHERE rn = 11
Results:
| AGE | X |
|-----|----|
| 34 | 15 |
Query 3:
WITH ordered AS (
SELECT *
FROM users
ORDER BY age
),
ranked AS (
SELECT o.*,
ROWNUM AS rn
FROM ordered o
WHERE ROWNUM <= 11
)
SELECT age, x
FROM ranked
WHERE rn = 11
Results:
| AGE | X |
|-----|----|
| 34 | 15 |