How to get records based on most occurring value? - oracle

I have following table and I want to get records where promotion is mostly occurring.
For example if I got two events
FREQ_VISITOR, value= 250
HIGH_SHOPPER, value= 320
Then Promo 1 and Promo 2 should come in result. Since these 2 promos exists mostly for every trigger and their given values.

Here's one option, based on what I understood:
SQL> with test (event_name, value, promotion) as
2 (select 'freq_visitor', 250, 'promo1' from dual union all
3 select 'high_shopper', 320, 'promo2' from dual union all
4 select 'freq_visitor', 250, 'promo3' from dual union all
5 select 'high_shopper', 320, 'promo1' from dual union all
6 select 'freq_visitor', 250, 'promo2' from dual
7 ),
8 cnt_promo as
9 (select promotion, count(*) cnt
10 from test
11 group by promotion
12 ),
13 most_promos as
14 (select max(cnt) max_cnt
15 from cnt_promo
16 )
17 select c.promotion
18 from cnt_promo c join most_promos m on c.cnt = m.max_cnt;
PROMOT
------
promo1
promo2
SQL>

This is a good candidate for analytic functions.
The below code is a bit longer than the self-join approach, and flows from the inside-out instead of a more traditional top-to-bottom direction. But this approach will likely be faster, since it only reads from the table once. And this approach is easier to debug than common table expressions, since you can highlight and run different inline views and watch the result set be built.
--Promotions with the highest counts.
select promotion
from
(
--RANK the promotion counts.
select promotion, promotion_count,
rank() over (order by promotion_count desc) promotion_rank
from
(
--Count of promos per event and value.
select promotion, count(*) promotion_count
from
(
--Test data
select 'freq_visitor' event_name, 250 value, 'promo1' promotion from dual union all
select 'high_shopper' event_name, 320 value, 'promo2' promotion from dual union all
select 'freq_visitor' event_name, 250 value, 'promo3' promotion from dual union all
select 'high_shopper' event_name, 320 value, 'promo1' promotion from dual union all
select 'freq_visitor' event_name, 250 value, 'promo2' promotion from dual
) test_data
group by promotion
) add_promo_count
) add_promo_rank
where promotion_rank = 1
order by promotion;

Related

Filtering a dataset based on condition SQL Oracle

I have the following input and expected output I am looking for. Basically I would like to filter request_id only when the type = crossborder` and then show the units. I think I would need to use some Min and Max but I am not sure how to use it.
Input
request_id type unit_count
A11 local 10
A11 crossborder 5
B11 local 15
C11 crossborder 25
Output
request_id type unit_count
C11 crossborder 25
I think what you can use here in ranking. You can rank based on anything in the rows and order by the value to leave the highest value on top, that value will get the ranking # 1, all you have to do later is to filter for all the rankings #1.
This little demo will show what I am talking about:
with data as (
select 1 as id, 'cross' as type, 27 as unit from dual union
select 1 as id, 'cross' as type, 23 as unit from dual union
select 1 as id, 'cross' as type, 2 as unit from dual union
select 3 as id, 'cross' as type, 25 as unit from dual union
select 2 as id, 'cross' as type, 23 as unit from dual union
select 5 as id, 'cross' as type, 2 as unit from dual)
select id, type,unit, rank() over ( order by unit desc) ranking from data where type='cross'
Output:
As per my understanding of the question, You don't need MAX or MIN but simply an aggregation -
SELECT request_id, type, unit_count
FROM (SELECT request_id, type, unit_count,
COUNT(*) OVER (PARTITION BY request_id) CNT
FROM YOUR_TABLE)
WHERE CNT = 1
AND type = 'crossborder';

Oracle Apex Pivot sorting

I have an interactive report, which using Pivot, but I was trying to custom sort the report by row column of pivot "Age Group", but I couldn't.
I need this report in such this sorting:
How I can do that?
Thanks
Looks like you'd want to sort by the first digit(s), and then by age_group itself (as a string). Something like this (sample data in lines #1 - 10; query you might be interested in begins at line #11):
SQL> with test (age_group) as
2 (select 'wahine 45-64' from dual union all
3 select 'wahine 25-44' from dual union all
4 select 'rangtahi 15-24' from dual union all
5 select 'pepis 0_4' from dual union all
6 select 'pakeke 45-64' from dual union all
7 select 'pakeke 25-44' from dual union all
8 select 'N/A' from dual union all
9 select 'kuia 65+' from dual
10 )
11 select age_group
12 from test
13 order by to_number(regexp_substr(age_group, '\d+')), age_group;
AGE_GROUP
--------------
pepis 0_4
rangtahi 15-24
pakeke 25-44
wahine 25-44
pakeke 45-64
wahine 45-64
kuia 65+
N/A
8 rows selected.
SQL>

Oracle 11g - How to calculate the value of a number in range minimum or max

i need a help to get solution to my problem, Please.
I have a table like this :
ID Number
|6 |20.90 |
|7 |45.00 |
|8 |52.00 |
|9 |68.00 |
|10 |120.00 |
|11 |220.00 |
|12 |250.00 |
The first range is 0 - 20.90.
When the value is in the half, the value id is for the max range.
When i got value 20.91, i want to get "ID = 6".
If the value is 31.00, i want to get "ID = 6"
If the value is
33.95, i want to get "ID = 7".
if the value is 44.99, i want to get ID = 7
How i can do it? Is there a function that will do what I need?
If you want the record with a number that is closest to your input, then you can use this:
select *
from (
select *
from mytable
order by abs(number - my_input_number), id
)
where rownum < 2
The inner query selects all records, but orders them by the distance they have from your input number. This distance can be calculated with number - my_input_number. But that could be negative, so we take the absolute value of that. This result is not output; it is just used to order by. So records with smaller distances will come first.
Now we need just the first of those records, and that is what the outer query does with the typical Oracle reserved word rownum: it represents a sequence number for every record of the final result set (1, 2, 3, ...). The where clause will effectively filter away all records we do not want to see, leaving only one (with smallest distance).
As mathguy suggested in comments, the order by now also has a second value to order by in case the input value is right at the mid point between the two closest records. In that case the record with the lowest id value will be chosen.
This is a good illustration of the power of analytic functions:
with mytable ( id, value ) as (
select 6, 20.90 from dual union all
select 7, 45.00 from dual union all
select 8, 52.00 from dual union all
select 9, 68.00 from dual union all
select 10, 120.00 from dual union all
select 11, 220.00 from dual union all
select 12, 250.00 from dual
),
inputs ( x ) as (
select 0.00 from dual union all
select 20.91 from dual union all
select 31.00 from dual union all
select 33.95 from dual union all
select 44.99 from dual union all
select 68.00 from dual union all
select 32.95 from dual union all
select 400.11 from dual
)
-- End of test data (not part of the solution). SQL query begins BELOW THIS LINE
select val as x, new_id as closest_id
from (
select id, val,
last_value(id ignore nulls) over (order by val desc) as new_id
from (
select id, (value + lead(value) over (order by value))/2 as val
from mytable
union all
select null, x
from inputs
)
)
where id is null
order by x -- if needed
;
Output:
X CLOSEST_ID
------ ----------
0 6
20.91 6
31 6
32.95 6
33.95 7
44.99 7
68 9
400.11 12

How to breakdown data by month and showing zero for months with no data?

Using information in Table A, how can I produce results in Table B below?
Table A:
CASE_ID DATE_EFF COPAY STATUS
1 11/04/2016 10 A
1 11/20/2016 5 A
1 11/23/2016 5 R
1 12/01/2016 1 A
1 12/10/2016 2 A
1 12/12/2016 10 A
1 12/31/2016 50 R
For the above CASE_ID, we have dates in Nov 2016 and Dec 2016 only, however, I want to produce a breakdown of this CASE_ID for a period of 6 months as below where for each month the copays are summed where applicable as per the DATE_EFF and for the months that are not within the above dates, a zero is entered. Also, only records with copays with a status of 'A' are summed for any month -- so those with status of 'R' are ignored in the summation. For example, based on data in Table A above, the intended results are as follow:
Table B:
CASE_ID MONTH TOTAL_COPAY
1 01/2017 0
1 12/2016 13
1 11/2016 15
1 10/2016 0
1 09/2016 0
1 08/2016 0
I have below as a possible solution[using a with clause], but can this be achieved without the use of the 'with' clause?
Possible Solution:
WITH
XRF AS
( SELECT CASE_ID, COPAY, DATE_EFF
FROM Table_A WHERE STATUS = 'A'
)
SELECT F.CASE_ID, ST, NVL(SUM(F.COPAY),0) TOTAL_COPAY FROM XRF F PARTITION BY (F.CASE_ID)
RIGHT OUTER JOIN (SELECT '12/2016' ST FROM DUAL UNION ALL
SELECT '11/2016' FROM DUAL UNION ALL
SELECT '10/2016' FROM DUAL UNION ALL
SELECT '09/2016' FROM DUAL UNION ALL
SELECT '08/2016' FROM DUAL UNION ALL
SELECT '07/2016' FROM DUAL) STS
ON (TO_CHAR(LAST_DAY((F.DATE_EFF)),'MM/YYYY') = STS.ST)
GROUP BY F.CASE_ID, ST ORDER BY F.CASE_ID, ST DESC
;
UPDATE AND SOLUTION:
Using the above query, I believe I am have answered my own question by implementing it as below -- not sure though if using this method is expensive when you have millions of records of such CASE_IDs. Any thoughts?
SELECT F.CASE_ID, ST, NVL(SUM(F.COPAY),0) TOTAL_COPAY FROM (SELECT CASE_ID, COPAY, DATE_EFF FROM TABLE_A WHERE STATUS = 'A') F PARTITION BY (F.CASE_ID)
RIGHT OUTER JOIN (SELECT '12/2016' ST FROM DUAL UNION ALL
SELECT '11/2016' FROM DUAL UNION ALL
SELECT '10/2016' FROM DUAL UNION ALL
SELECT '09/2016' FROM DUAL UNION ALL
SELECT '08/2016' FROM DUAL UNION ALL
SELECT '07/2016' FROM DUAL) STS
ON (TO_CHAR(LAST_DAY((F.DATE_EFF)),'MM/YYYY') = STS.ST)
GROUP BY F.CASE_ID, ST ORDER BY F.CASE_ID, ST DESC
;

Oracle SQL Select Query Getting Max Row As a Fraction of a Rollup Total

hoping I might be able to get some advise regarding Oracle SQL…
I have a table roughly as follows (there are more columns, but not necessary for this example)…
LOCATION USER VALUE
1 1 10
1 2 20
1 3 30
2 4 10
2 5 10
2 6 20
1 60
2 40
100
I’ve used rollup to get subtotals.
What I need to do is get the max(value) row for each location and express the max(value) as a percentage or fraction of the subtotal for each location
ie:
LOCATION USER FRAC
1 3 0.5
2 6 0.5
I could probably solve this using my limited knowledge of select queries, but am guessing there must be a fairly quick and slick method..
Thanks in advance :)
Solution using analytic functions
(Please note the WITH MY_TABLE AS serving only as dummy datasource)
WITH MY_TABLE AS
( SELECT 1 AS LOC_ID,1 AS USER_ID, 10 AS VAL FROM DUAL
UNION
SELECT 1,2,20 FROM DUAL
UNION
SELECT 1,3,30 FROM DUAL
UNION
SELECT 2,4,10 FROM DUAL
UNION
SELECT 2,5,10 FROM DUAL
UNION
SELECT 2,6,20 FROM DUAL
)
SELECT LOC_ID,
USER_ID,
RATIO_IN_LOC
FROM
(SELECT LOC_ID,
USER_ID,
RATIO_IN_LOC,
RANK() OVER (PARTITION BY LOC_ID ORDER BY RATIO_IN_LOC DESC) AS ORDER_IN_LOC
FROM
(SELECT LOC_ID,
USER_ID,
VAL,
VAL/SUM(VAL) OVER (PARTITION BY LOC_ID) AS RATIO_IN_LOC
FROM MY_TABLE
)
)
WHERE ORDER_IN_LOC = 1
ORDER BY LOC_ID,
USER_ID;
Result
LOC_ID USER_ID RATIO_IN_LOC
1 3 0,5
2 6 0,5
with inputs ( location, person, value ) as (
select 1, 1, 10 from dual union all
select 1, 2, 20 from dual union all
select 1, 3, 30 from dual union all
select 2, 4, 10 from dual union all
select 2, 5, 10 from dual union all
select 2, 6, 20 from dual
),
prep ( location, person, value, m_value, total ) as (
select location, person, value,
max(value) over (partition by location),
sum(value) over (partition by location)
from inputs
)
select location, person, round(value/total, 2) as frac
from prep
where value = m_value;
Notes: Your table exists already? Then skip everything from "inputs" to the comma; your query should begin with with prep (...) as ( ...
I changed user to person since user is a keyword in Oracle, you shouldn't use it for table or column names (actually you can't unless you use double quotes, which is a very poor practice).
The query will output two or three or more rows per location if there are ties at the top. Presumably this is what you desire.
Output:
LOCATION PERSON FRAC
---------- ---------- ----------
1 3 .5
2 6 .5

Resources