Transpose in ORACLE SQL (Convert 6 columns into 3 Columns) - sql

I've an output like the below.
I want that to be converted into the below
Where ID is the count of TOTAL, HIGH, SENT, WAITING, DATE. I have been contemplating for a while and couldn't get what I want. Can any one please help in ORACLE SQL?
Thanks in advance.

Here's one option:
SQL> with test (cdate, total, high, sent, waiting, loc) as
2 (select 1012018, 23, 4, 35, 45, 13456 from dual union all
3 select 1212018, 74, 2, 77, 82, 98765 from dual
4 ),
5 temp as
6 (select 5 rn, loc, cdate as value from test union all
7 select 1 rn, loc, total from test union all
8 select 2 rn, loc, high from test union all
9 select 3 rn, loc, sent from test union all
10 select 4 rn, loc, waiting from test
11 )
12 select rn, value, loc
13 from temp
14 order by loc, rn;
RN VALUE LOC
---------- ---------- ----------
1 23 13456
2 4 13456
3 35 13456
4 45 13456
5 1012018 13456
1 74 98765
2 2 98765
3 77 98765
4 82 98765
5 1212018 98765
10 rows selected.
SQL>

Related

Return limit rows based on value in column

I've got this piece of code:
SELECT t1.sku_id, t1.putaway_group, t1.shortage, t4.location_id, t4.qty_on_hand
FROM
(WHERE clauses)t1
LEFT JOIN
(
SELECT *
FROM (
SELECT location_id, sku_id, qty_on_hand,
DENSE_RANK() OVER ( PARTITION BY sku_id ORDER BY qty_on_hand DESC ) AS rnk
FROM inventory
WHERE substr(zone_1,1,5) IN ('TOTEB','TOTEC')
)
WHERE rnk = 1
ORDER BY 2 DESC
)t4
ON t3.sku_id = t4.sku_id
Where the output is:
What i want to achieve is to return as many rows from location_id as shortage require.
For example if shortage is -84 THEN as an output for SKU: 02295441 i want to return 6 rows because (6*16 = 96) which will cover my shortage.
Not really sure if it's possible or if yes then how to write a where/having clause to limit output rows.
Currently I'm just doing it through power query in excel, but just wondering if it's possible straight from sql.
Thanks in advance.
You can use the SUM analytic function:
SELECT t1.sku_id,
t1.putaway_group,
t1.shortage,
t4.location_id,
t4.qty_on_hand
FROM /*(WHERE clauses)*/ t1
LEFT JOIN (
SELECT location_id,
sku_id,
qty_on_hand,
SUM(qty_on_hand) OVER (
PARTITION BY sku_id
ORDER BY qty_on_hand DESC, ROWNUM
) AS total_qty
FROM inventory
WHERE zone_1 LIKE 'TOTEB%'
OR zone_1 LIKE 'TOTEC%'
) t4
ON ( t1.sku_id = t4.sku_id
AND -t1.shortage > t4.total_qty - t4.qty_on_hand )
Which, for the sample data:
CREATE TABLE t1 (sku_id, putaway_group, shortage) AS
SELECT 'SKU1', 'TEXTILES', -84 FROM DUAL UNION ALL
SELECT 'SKU2', 'PLASTICS', -13 FROM DUAL;
CREATE TABLE inventory(location_id, sku_id, qty_on_hand, zone_1) AS
SELECT LEVEL, 'SKU1', LEAST(LEVEL * 4, 16), 'TOTEB' || CHR(64 + LEVEL) FROM DUAL CONNECT BY LEVEL <= 10
UNION ALL
SELECT LEVEL, 'SKU2', LEVEL, 'TOTEC' || CHR(64 + LEVEL) FROM DUAL CONNECT BY LEVEL <= 6;
Outputs:
SKU_ID
PUTAWAY_GROUP
SHORTAGE
LOCATION_ID
QTY_ON_HAND
SKU1
TEXTILES
-84
4
16
SKU1
TEXTILES
-84
5
16
SKU1
TEXTILES
-84
6
16
SKU1
TEXTILES
-84
7
16
SKU1
TEXTILES
-84
8
16
SKU1
TEXTILES
-84
9
16
SKU2
PLASTICS
-13
6
6
SKU2
PLASTICS
-13
5
5
SKU2
PLASTICS
-13
4
4
fiddle
Due to lack of sample data, my CTE represents (simplified) result you currently have; if you apply row_number function to it and then return rows that satisfy the condition you mentioned (see line #22), you might get what you want:
SQL> with data (sku_id, shortage, location_id, qty_on_hand) as
2 (select 1, -84, 3, 16 from dual union all
3 select 1, -84, 2, 16 from dual union all
4 select 1, -84, 5, 16 from dual union all
5 select 1, -84, 5, 16 from dual union all
6 select 1, -84, 5, 16 from dual union all
7 select 1, -84, 6, 16 from dual union all
8 select 1, -84, 1, 16 from dual union all
9 select 1, -84, 2, 16 from dual union all
10 select 1, -84, 1, 16 from dual union all
11 select 1, -84, 2, 16 from dual union all
12 --
13 select 2, -20, 1, 10 from dual
14 ),
15 temp as
16 (select d.*,
17 row_number() over (partition by sku_id order by qty_on_hand) rnk
18 from data d
19 )
20 select *
21 from temp
22 where rnk <= ceil(abs(shortage) / qty_on_hand);
SKU_ID SHORTAGE LOCATION_ID QTY_ON_HAND RNK
---------- ---------- ----------- ----------- ----------
1 -84 3 16 1 --> SKU_ID = 1 begins here
1 -84 2 16 2
1 -84 5 16 3
1 -84 5 16 4
1 -84 5 16 5
1 -84 6 16 6 --> SKU_ID = 1 ends here; 6 rows
2 -20 1 10 1
7 rows selected.
SQL>

Reset Row Number in Oracle conditionally

I have this record set returned , now I want to have a row number column which gets reset after every 3rd row. Can anyone help me with this? needs to be done with Oracle SQL.
Explanation below-
data
current row number
rquired row number
Chris
1
1
Bryan
2
2
Jim
3
3
Davis
4
1
Kia
5
2
Jones
6
3
Mary
7
1
Carrie
8
2
Pearce
9
3
Cesar
10
1
Bob
11
2
You can mod the current value:
mod(current_row_num - 1, 3) + 1
So using a CTE to represent your current result set:
with your_result (data, current_row_num) as (
select 'Chris', 1 from dual union all
select 'Bryan', 2 from dual union all
select 'Jim', 3 from dual union all
select 'Davis', 4 from dual union all
select 'Kia', 5 from dual union all
select 'Jones', 6 from dual union all
select 'Mary', 7 from dual union all
select 'Carrie', 8 from dual union all
select 'Pearce', 9 from dual union all
select 'Cesar', 10 from dual union all
select 'Bob', 11 from dual
)
select data, current_row_num, mod(current_row_num - 1, 3) + 1 as required_row_num
from your_result
order by current_row_num
DATA
CURRENT_ROW_NUM
REQUIRED_ROW_NUM
Chris
1
1
Bryan
2
2
Jim
3
3
Davis
4
1
Kia
5
2
Jones
6
3
Mary
7
1
Carrie
8
2
Pearce
9
3
Cesar
10
1
Bob
11
2
db<>fiddle

Oracle - How to use avg(count(*)) on having clause or where clause?

I have data like this:
CONCERT_ID EVENT_ID ATTENDANCE AVG_ATTENDANCE_EACH_CONCERT
---------- ---------- ---------- ---------------------------
1 1 1 1,5
1 2 2 1,5
3 5 2 2
3 6 2 2
5 11 4 2
5 12 1 2
5 13 1 2
Thats from this query:
select concert_id, event_id, count(customer_id) attendance,
avg(count(*)) over (partition by concert_id) avg_attendance_each_concert
from booking
group by concert_id, event_id
order by event_id;
How to make a limitation on that query. What i want to make is
If the attendance is below average attendance show result
I already tried avg(count(*)) over (partition by concert_id) to having clause but gave me an error group function is too deep
It's easy to get the desired results by applying just one nesting :
select * from
(
select concert_id, event_id, count(customer_id) attendance,
avg(count(*)) over (partition by concert_id) avg_attendance_each_concert
from booking
group by concert_id, event_id
order by event_id
)
where attendance < avg_attendance_each_concert
D e M o
Include an "intermediate" table, a query which returned the correct result in your last question. Then select values - which satisfy a new condition - from it.
SQL> with booking (concert_id, event_id, customer_id) as
2 (select 1, 1, 10 from dual union
3 select 1, 2, 10 from dual union
4 select 1, 2, 20 from dual union
5 --
6 select 3, 5, 10 from dual union
7 select 3, 5, 20 from dual union
8 select 3, 6, 30 from dual union
9 select 3, 6, 40 from dual union
10 --
11 select 5, 11, 10 from dual union
12 select 5, 11, 20 from dual union
13 select 5, 11, 30 from dual union
14 select 5, 11, 40 from dual union
15 select 5, 12, 50 from dual union
16 select 5, 13, 60 from dual
17 ),
18 inter as
19 (select concert_id, event_id, count(customer_id) attendance,
20 avg(count(*)) over (partition by concert_id) avg_attendance_each_concert
21 from booking
22 group by concert_id, event_id
23 )
24 select concert_id, event_id, attendance, avg_attendance_each_concert
25 from inter
26 where attendance < avg_attendance_Each_concert
27 order by event_id;
CONCERT_ID EVENT_ID ATTENDANCE AVG_ATTENDANCE_EACH_CONCERT
---------- ---------- ---------- ---------------------------
1 1 1 1,5
5 12 1 2
5 13 1 2
SQL>

How to get the full row of data based on max value?

I have an Oracle table with a usage counter. I need to get the full row of data for each sensor with the max counter value?
For ALO I need data in row 2.
For AMA I need data in row 10.
For A11 I need data in row 9658.
For MSP I need data in row 9659.
Any help would be greatly appreciated!
Thanks,
Dave
You can use window function.
selcet * from (
select a.*, row_number() over (partition by facility_id_n
order by usage_counter_n desc) rn
from your_tbl a)
where rn =1;
Or use the First window-funcrion.
ROW_NUMBER won't return correct result if there are two rows with the same USAGE_COUNTER_N value per FACILITY_ID_N. For example:
SQL> WITH test
2 AS (SELECT 9640 ID_n, 'ALO' sensor_id_c, 317 usage_counter_n FROM DUAL
3 UNION
4 SELECT 9641, 'ALO', 18 FROM DUAL
5 UNION
6 SELECT 9642, 'ALO', 0 FROM DUAL
7 UNION
8 SELECT 9659, 'MSP', 25 FROM DUAL --> MAX for MSP ...
9 UNION
10 SELECT 9660, 'MSP', 10 FROM DUAL
11 UNION
12 SELECT 1000, 'MSP', 25 FROM DUAL --> ... but this is also MAX for MSP
13 )
14 SELECT *
15 FROM (SELECT a.*,
16 ROW_NUMBER ()
17 OVER (PARTITION BY sensor_id_c
18 ORDER BY usage_counter_n DESC) rn
19 FROM test a)
20 WHERE rn = 1;
ID_N SEN USAGE_COUNTER_N RN
---------- --- --------------- ----------
9640 ALO 317 1
1000 MSP 25 1
SQL>
RANK might be better:
SQL> l16
16* ROW_NUMBER ()
SQL> c/row_number/rank
16* rank ()
SQL> /
ID_N SEN USAGE_COUNTER_N RN
---------- --- --------------- ----------
9640 ALO 317 1
1000 MSP 25 1
9659 MSP 25 1
SQL>
Or, using the oldfashioned way:
14 SELECT *
15 FROM test t
16 WHERE t.usage_counter_n = (SELECT MAX (t1.usage_counter_n)
17 FROM test t1
18 WHERE t1.sensor_id_c = t.sensor_id_c)
19 ORDER BY sensor_id_c;
ID_N SEN USAGE_COUNTER_N
---------- --- ---------------
9640 ALO 317
9659 MSP 25
1000 MSP 25
SQL>
Depending on your requirements to handle duplicates, the KEEP clause might also be a useful option
SQL> with test
2 AS (SELECT 9640 ID_n, 'ALO' sensor_id_c, 317 usage_counter_n FROM DUAL
3 UNION
4 SELECT 9641, 'ALO', 18 FROM DUAL
5 UNION
6 SELECT 9642, 'ALO', 0 FROM DUAL
7 UNION
8 SELECT 9659, 'MSP', 25 FROM DUAL --> MAX for MSP ...
9 UNION
10 SELECT 9660, 'MSP', 10 FROM DUAL
11 UNION
12 SELECT 1000, 'MSP', 25 FROM DUAL --> ... but this is also MAX for MSP
13 )
14 SELECT sensor_id_c,
15 min(id_n) keep ( dense_rank first order by usage_counter_n DESC ) id_n,
16 min(usage_counter_n) keep ( dense_rank first order by usage_counter_n DESC ) usage_counter_n
17 from test
18 group by sensor_id_c;
SEN ID_N USAGE_COUNTER_N
--- ---------- ---------------
ALO 9640 317
MSP 1000 25

oracle sql - numbering group of rows

i have the following table with different prices in every week and need a numbering like in the last column. consecutive rows with same prices should have the same number like in weeks 11/12 or 18/19. but on the other side weeks 2 and 16 have the same prices but are not consecutive so they should get a different number.
w | price | r1 | need
===========================
1 167,93 1 1
2 180 1 2
3 164,72 1 3
4 147,42 1 4
5 133,46 1 5
6 145,43 1 6
7 147 1 7
8 147,57 1 8
9 150,95 1 9
10 158,14 1 10
11 170 1 11
12 170 2 11
13 166,59 1 12
14 161,06 1 13
15 162,88 1 14
16 180 2 15
17 183,15 1 16
18 195 1 17
19 195 2 17
i have already experimented with the analytics functions (row_number, rank, dens_rank), but didn't found a solution for this problem so far.
(oracle sql 10,11)
does anyone have a hint? thanks.
Simulating your table first:
SQL> create table mytable (w,price,r1)
2 as
3 select 1 , 167.93, 1 from dual union all
4 select 2 , 180 , 1 from dual union all
5 select 3 , 164.72, 1 from dual union all
6 select 4 , 147.42, 1 from dual union all
7 select 5 , 133.46, 1 from dual union all
8 select 6 , 145.43, 1 from dual union all
9 select 7 , 147 , 1 from dual union all
10 select 8 , 147.57, 1 from dual union all
11 select 9 , 150.95, 1 from dual union all
12 select 10, 158.14, 1 from dual union all
13 select 11, 170 , 1 from dual union all
14 select 12, 170 , 2 from dual union all
15 select 13, 166.59, 1 from dual union all
16 select 14, 161.06, 1 from dual union all
17 select 15, 162.88, 1 from dual union all
18 select 16, 180 , 2 from dual union all
19 select 17, 183.15, 1 from dual union all
20 select 18, 195 , 1 from dual union all
21 select 19, 195 , 2 from dual
22 /
Table created.
Your need column is calculated in two parts: first compute a delta column which denotes whether the previous price-column differs from the current rows price column. If you have that delta column, the second part is easy by computing the sum of those deltas.
SQL> with x as
2 ( select w
3 , price
4 , r1
5 , case lag(price,1,-1) over (order by w)
6 when price then 0
7 else 1
8 end delta
9 from mytable
10 )
11 select w
12 , price
13 , r1
14 , sum(delta) over (order by w) need
15 from x
16 /
W PRICE R1 NEED
---------- ---------- ---------- ----------
1 167.93 1 1
2 180 1 2
3 164.72 1 3
4 147.42 1 4
5 133.46 1 5
6 145.43 1 6
7 147 1 7
8 147.57 1 8
9 150.95 1 9
10 158.14 1 10
11 170 1 11
12 170 2 11
13 166.59 1 12
14 161.06 1 13
15 162.88 1 14
16 180 2 15
17 183.15 1 16
18 195 1 17
19 195 2 17
19 rows selected.
You can nest your analytic functions using inline views, so you first group the consecutive weeks with same prices and then dense_rank using those groups:
select w
, price
, r1
, dense_rank() over (
order by first_w_same_price
) drank
from (
select w
, price
, r1
, last_value(w_start_same_price) ignore nulls over (
order by w
rows between unbounded preceding and current row
) first_w_same_price
from (
select w
, price
, r1
, case lag(price) over (order by w)
when price then null
else w
end w_start_same_price
from your_table
)
)
order by w
The innermost inline view with LAG function lets the starting week of every consecutive group get it's own week number, but every consecutive week with same price gets null (weeks 12 and 19 in your data.)
The middle inline view with LAST_VALUE function then use the IGNORE NULLS feature to give the consecutive weeks the same value as the first week within each group. So week 11 and 12 both gets 11 in first_w_same_price and week 18 and 19 both gets 18 in first_w_same_price.
And finally the outer query use DENSE_RANK to give the desired result.
For each row you should count previous rows where (w-1) row price isn't the same as (w) price:
select T1.*,
(SELECT count(*)
FROM T T2
JOIN T T3 ON T2.w-1=T3.w
WHERE T2.Price<>T3.Price
AND T2.W<=T1.W)+1 rn
from t T1
SQLFiddle demo
Try this:
with tt as (
select t.*, decode(lag(price) over(order by w) - price, 0, 1, 0) diff
from t
)
select w
, price
, r1
, row_number() over (order by w) - sum(diff) over(order by w rows between UNBOUNDED PRECEDING and current row) need
from tt
SELECT w, price, r1,
ROW_NUMBER () OVER (PARTITION BY price ORDER BY price) row_column
FROM TABLE