Oracle Aggregate(SUM function) after self-join - sql

I have a table which contains ID, PARENT_ID AND COUNT.
EX)
+-----+-----------------------+--------------------------+
| ID | PARENT_ID | COUNT |...
+-----+-----------------------+--------------------------+
| 1 | NULL | 40 |...
| 2 | 1 | 10 |...
| 3 | 1 | 20 |...
| 4 | NULL | 35 |...
+-----+-----------------------+--------------------------+
And, i want result the sum of parent and sibling's count.
ID 1's count = ID 1's count + ID 2's count + ID 3's count
RESULT)
+-----+-----------------------+--------------------------+
| ID | PARENT_ID | COUNT |...
+-----+-----------------------+--------------------------+
| 1 | NULL | 70 |...
| 2 | 1 | 10 |...
| 3 | 1 | 20 |...
| 4 | NULL | 35 |...
+-----+-----------------------+--------------------------+
I used connect by to get the desired result, but I want to change the method as the above method uses too much oracle cpu.
Is there any way I can do this using sum function?

You can use the self join as follows:
SQL> with dataa (ID, PARENT_ID, CNT) as
2 (SELECT 1 , NULL, 40 FROM DUAL UNION ALL
3 SELECT 2 , 1 , 10 FROM DUAL UNION ALL
4 SELECT 3 , 1 , 20 FROM DUAL UNION ALL
5 SELECT 4 , NULL, 35 FROM DUAL)
6 -- your query starts from here
7 SELECT D1.ID, D1.PARENT_ID, D1.CNT + COALESCE(SUM(D2.CNT),0)
8 FROM DATAA D1 LEFT JOIN DATAA D2
9 ON D1.ID = D2.PARENT_ID
10 GROUP BY D1.ID, D1.PARENT_ID, D1.CNT
11 ORDER BY D1.ID;
ID PARENT_ID D1.CNT+COALESCE(SUM(D2.CNT),0)
---------- ---------- ------------------------------
1 70
2 1 10
3 1 20
4 35
SQL>

Related

How to get columns when using buckets (width_bucket)

I would like to know which row were moved to a bucket.
SELECT
width_bucket(s.score, sl.mins, sl.maxs, 9) as buckets,
COUNT(*)
FROM scores s
CROSS JOIN scores_limits sl
GROUP BY 1
ORDER BY 1;
My actual return:
buckets | count
---------+-------
1 | 182
2 | 37
3 | 46
4 | 15
5 | 29
7 | 18
8 | 22
10 | 11
| 20
What I expect to return:
SELECT buckets FROM buckets_table [...] WHERE scores.id = 1;
How can I get, for example, the column 'id' of table scores?
I believe you can include the id in an array with array_agg. If I recreate your case with
create table test (id serial, score int);
insert into test(score) values (10),(9),(5),(4),(10),(2),(5),(7),(8),(10);
The data is
id | score
----+-------
1 | 10
2 | 9
3 | 5
4 | 4
5 | 10
6 | 2
7 | 5
8 | 7
9 | 8
10 | 10
(10 rows)
Using the following and aggregating the id with array_agg
SELECT
width_bucket(score, 0, 10, 11) as buckets,
COUNT(*) nr_ids,
array_agg(id) agg_ids
FROM test s
GROUP BY 1
ORDER BY 1;
You get
buckets | nr_ids | agg_ids
---------+--------+----------
3 | 1 | {6}
5 | 1 | {4}
6 | 2 | {3,7}
8 | 1 | {8}
9 | 1 | {9}
10 | 1 | {2}
12 | 3 | {1,5,10}

Oracle : SQL Request with a Group By and a Percentage on two differents tables

I'm currently blocked on an complex request... (with a join) :
I have this table "DATA":
order | product
----------------
1 | A
1 | B
2 | A
2 | D
3 | A
3 | C
4 | A
4 | B
5 | Y
5 | Z
6 | W
6 | A
And this table "DICO":
order | couple | first | second
-------------------------------
1 | A-B | A | B
2 | A-D | A | D
3 | A-C | A | C
4 | A-B | A | B
5 | Y-Z | Y | Z
6 | W-A | W | A
I would like to obtain, on one line :
order | count | total1stElem | %1stElem | total2ndElem | %1ndElem
------------------------------------------------------------------
A-B | 2 | 5 | 40% | 2 | 100%
A-D | 1 | 5 | 20% | 1 | 100%
A-C | 1 | 5 | 20% | 1 | 100%
Y-Z | 1 | 1 | 100% | 1 | 100%
W-A | 1 | 1 | 100% | 5 | 20%
I'm totally blocked on the jointure part of my request. Somebody can help me ?
Without any joins - just using UNPIVOT and PIVOT:
Oracle Setup:
CREATE TABLE DICO ( "order", couple, first, second ) AS
SELECT 1, 'A-B', 'A', 'B' FROM DUAL UNION ALL
SELECT 2, 'A-D', 'A', 'D' FROM DUAL UNION ALL
SELECT 3, 'A-C', 'A', 'C' FROM DUAL UNION ALL
SELECT 4, 'A-B', 'A', 'B' FROM DUAL UNION ALL
SELECT 5, 'Y-Z', 'Y', 'Z' FROM DUAL UNION ALL
SELECT 6, 'W-A', 'W', 'A' FROM DUAL;
Query:
SELECT "order",
"count",
"1stElem_TOTAL" AS Total1stElem,
100*"count"/"1stElem_TOTAL" AS "%1stElem",
"2ndElem_TOTAL" AS Total2ndElem,
100*"count"/"2ndElem_TOTAL" AS "%2ndElem"
FROM (
SELECT couple AS "order",
key,
COUNT(*) OVER ( PARTITION BY COUPLE )/2 AS "count",
COUNT(*) OVER ( PARTITION BY VALUE ) AS num_value
FROM DICO
UNPIVOT ( Value FOR Key IN ( first AS 1, second AS 2 ) )
)
PIVOT ( MAX( NUM_VALUE ) AS Total FOR key IN ( 1 AS "1stElem", 2 AS "2ndElem" ) );
Results:
order count TOTAL1STELEM %1stElem TOTAL2NDELEM %2ndElem
----- ----- ------------ -------- ------------ --------
A-D 1 5 20 1 100
A-B 2 5 40 2 100
A-C 1 5 20 1 100
Y-Z 1 1 100 1 100
W-A 1 1 100 5 20

select only tuples where second column always has same value

I have a similar table to this one
ID | CountryID
1 | 22
1 | 22
2 | 19
3 | 0
3 | 14
3 | 18
3 | 21
3 | 22
3 | 23
4 | 19
5 | 9
5 | 9
6 | 14
and I want to group by the first ID column but select only rows, where the CountryID has the same value throughout an ID. The resulting table should look like
ID | CountryID
1 | 22
2 | 19
4 | 19
5 | 9
6 | 14
Any ideas?
I think the following query should work:
SELECT ID, MAX(CountryID)
FROM Table1
GROUP BY ID
HAVING MIN(CountryID) = MAX(CountryID)
SELECT ID, count(distinct CountryID)
FROM Table1
GROUP BY ID
HAVING count(distinct CountryID)=1

Update a column and refer back it in the same query

I have a table in SQL Server 2014 and need to recursively update a column based on its previous value. For e.g.
---------------------------------------
ID | price | diff_with_prev_price |
---------------------------------------
1 | 29 | 0 |
2 | 25 | 0 |
3 | 20 | 0 |
4 | 35 | 0 |
5 | 40 | 0 |
--------------------------------------|
I want to recursively update third column like below
---------------------------------------
ID | price | diff_with_prev_price |
---------------------------------------
1 | 29 | 0 |
2 | 25 | 25 |
3 | 20 | 5 |
4 | 35 | -30 |
5 | 40 | 10 |
--------------------------------------|
It is the summation of previous value of third column with next value of 'price'.
Can someone please give some hint to do this either using CTE or LEAD/LAG, but without using cursors. I have to update million rows.
You can try this:
SELECT 1 AS ID , 29 AS price, 0 AS diff_with_prev_prive
INTO #tmp
UNION SELECT 2 AS ID , 25 AS price, 0 AS diff_with_prev_prive
UNION SELECT 3 AS ID , 20 AS price, 0 AS diff_with_prev_prive
UNION SELECT 4 AS ID , 35 AS price, 0 AS diff_with_prev_prive
UNION SELECT 5 AS ID , 40 AS price, 0 AS diff_with_prev_prive
WITH cte AS
(
SELECT
ID
, price
, diff_with_prev_prive
, price - ISNULL(LAG(price) OVER (ORDER BY ID),0) AS new_value
FROM #tmp
)
UPDATE t
SET diff_with_prev_prive = t.new_value
FROM cte t
SELECT * FROM #tmp

select the most recent in all groups of with the same value in one column

The question isn't very clear, but I'll illustrate what I mean, suppose my table is like such:
item_name | date added | val1 | val2
------------------------------------
1 | date+1 | 10 | 20
1 | date | 12 | 21
2 | date+1 | 5 | 6
3 | date+3 | 3 | 1
3 | date+2 | 5 | 2
3 | date | 3 | 1
And I want to select row 1, 3, 4 as they are the most recent entries for each item
Try this:
select *
from tableX t1
where t1.date_added = (select max(t2.date_added)
from tableX t2
where t2.item_name = t1.item_name )