One column calculate multiple output - oracle

I have show the total product sale on the basis YTD (Year to Date), QTD(Quarter to Date) and MTD (Month to Date). The thing is I have to show only one from those. Only one output can be seen on the basis of selection i.e. like we have radio buttons to select one from many. Here also a input is given to select and on the basis of that input the output is generated. The input can be any YTD,QTD or MTD. The output is generated on the basis of input. I don't how to calculate a column output where the input can be vary.
I have a Product Table-
Product_ID Product_name Price
1 Mobile 200
2 T.V. 400
3 Mixer 300
I have a Sales table like this-
Product_ID Sales_Date Quantity
1 01-01-2015 30
2 03-01-2015 40
3 06-02-2015 10
1 22-03-2015 30
2 09-04-2015 10
3 21-05-2015 40
1 04-06-2015 40
2 29-07-2015 30
1 31-08-2015 30
3 14-09-2015 30
And my ouput column contains 3 columns that are-
Product_id, Product_Name and Total_Amount.
The column Total_Amount(quantity*price) have to calculate sale on the basis of input given by user i.e.,
IF it is YTD then it should calculate the total sale from Starting Date of Year ( 01-01-2015) to the current_date(sysdate),
IF it is QTD then in which quarter the current date is falling i.e if current month is september then from 1 July to current_date(sysdate),
IF it is MTD then in which month the current date is falling to the current_date(sysdate).
Can anyone help. Thanks!!!

-- step 1
create or replace view my_admin
as
select 'YTD' element, product_id, sum(quantity) sum_quantity
from sales
where Sales_date between trunc(sysdate,'Y') and sysdate
group by product_id
union
select 'QTD', product_id, sum(quantity) sum_quantity
from sales
where Sales_date between trunc(sysdate,'Q') and sysdate
group by product_id
union
select 'MTD', product_id, sum(quantity) sum_quantity
from sales
where Sales_date between trunc(sysdate,'MM') and sysdate
group by product_id
-- step 2
select element, p.product_name, (sum_quantity * p.PRICE) agregate
from my_admin a
inner join products p on a.product_id = p.product_id
where element = (:input)

My presumption is that you have 3 radio buttons(variables :YTD,:QTD,:MTD in my example) where just one value at a time can be picked by the user the rest will be null.
You can use a something like this to get what you want:
select SUM(a.QTY*B.PRICE) from PRODUCTS a
inner join SALES B on a.PRODUCT_ID=B.PRODUCT_ID
where
(:YTD is null or B.SALES_DATE between '01-JAN-15' and sysdate)
and
(:QTD is null or TO_CHAR(B.SALES_DATE, 'YYYY-Q')=TO_CHAR(sysdate, 'YYYY-Q'))
and
(:MTD is null or TO_CHAR(B.SALES_DATE, 'MM')=TO_CHAR(sysdate, 'MM'));
You can test it here sqlfiddle

Related

Efficiently get array of all previous dates per id per date limited to past 6 months in BigQuery

I have a very big table 'DATES_EVENTS' (20 T) that looks like this:
ID DATE
1 '2022-04-01'
1 '2022-03-02'
1 '2022-03-01'
2 '2022-05-01'
3 '2021-12-01'
3 '2021-11-11'
3 '2020-11-11'
3 '2020-10-01'
I want per each row to get all past dates (per user) limited to up to 6 months.
My desired table:
ID DATE DATE_list
1 '2022-04-01' ['2022-04-01','2022-03-02','2022-03-01']
1 '2022-03-02' ['2022-03-02','2022-03-01']
1 '2022-03-01' ['2022-03-01']
2 '2022-05-01' ['2022-05-01']
3 '2021-12-01' ['2021-12-01','2021-11-11']
3 '2021-11-11' ['2021-11-11']
3 '2020-11-11' ['2020-11-11','2020-10-01']
3 '2020-10-01' ['2020-10-01']
I have a solution for all dates not limited:
SELECT
ID, DATE, ARRAY_AGG(DATE) OVER (PARTITION BY ID ORDER BY DATE) as DATE_list
FROM
DATES_EVENTS
But for a limited up to 6 months I don't have an efficient solution:
SELECT
distinct A.ID, A.DATE, ARRAY_AGG(B.DATE) OVER (PARTITION BY B.ID ORDER BY B.DATE) as DATE_list
FROM
DATES_EVENTS A
INNER JOIN
DATES_EVENTS B
ON
A.ID=B.ID
AND B.DATE BETWEEN DATE_SUB(A.DATE, INTERVAL 180 DAY) AND A.DATE
** ruffly a solution
Anyone know of a good and efficient way to do what I need?
Consider below approach
select id, date, array(
select day
from t.date_list day
where day <= date
order by day desc
) as date_list
from (
select *, array_agg(date) over win as date_list
from dates_events
window win as (
partition by id
order by extract(year from date) * 12 + extract(month from date)
range between 5 preceding and current row
)
) t
if applied to sample data in your question - output is
In case if (as I noticed in your question) 180 days is appropriate substitution for 6 months for you - you can use below simpler version
select *, array_agg(date) over win as date_list
from dates_events
window win as (
partition by id
order by unix_date(date)
range between current row and 179 following
)

Max number of counts in a tparticular hour

I have a table called Orders, i want to get maximum number of orders for each day with respect to hours with following query
SELECT
trunc(created,'HH') as dated,
count(*) as Counts
FROM
orders
WHERE
created > trunc(SYSDATE -2)
group by trunc(created,'HH') ORDER BY counts DESC
this gets the result of all hours, I want only max hour of a day e.g.
Image
This result looks good but now i want only rows with max number of count for a day
e.g.
for 12/23/2019 max number of counts is 90 for "12/23/2019 4:00:00 PM",
for 12/22/2019 max number of counts is 25 for "12/22/2019 3:00:00 PM"
required dataset
1 12/23/2019 4:00:00 PM 90
2 12/24/2019 12:00:00 PM 76
3 12/22/2019 1:00:00 PM 25
This could be the solution and in my opinion is the most trivial.
Use the WITH clause to make a sub query then search for the greatest value in the data set on a specific date.
WITH ORD AS (
SELECT
trunc(created,'HH') as dated,
count(*) as Counts
FROM
orders
WHERE
created > trunc(SYSDATE-2)
group by trunc(created,'HH')
)
SELECT *
FROM ORD ord
WHERE NOT EXISTS (
SELECT 'X'
FROM ORD ord1
WHERE trunc(ord1.dated) = trunc(ord.dated) AND ord1.Counts > ord.Counts
)
Use ROW_NUMBER analytic function over your original query and filter the rows with number 1.
You need to partition on the day, i.e. TRUNC(dated) to get the correct result
with ord1 as (
SELECT
trunc(created,'HH') as dated,
count(*) as Counts
FROM
orders
WHERE
created > trunc(SYSDATE -2)
group by trunc(created,'HH')
),
ord2 as (
select dated, Counts,
row_number() over (partition by trunc(dated) order by Counts desc) as rn
from ord1)
select dated, Counts
from ord2
where rn = 1
The advantage of using the ROW_NUMBER is that it correct handels ties, i.e. cases where there are more hour in a day with the same maximal count. The query shows only one record and you can controll with the order by e.g. to show the first / last hour.
You can use the analytical function ROW_NUMBER as following to get the desired result:
SELECT DATED, COUNTS
FROM (
SELECT
TRUNC(CREATED, 'HH') AS DATED,
COUNT(*) AS COUNTS,
ROW_NUMBER() OVER(
PARTITION BY TRUNC(CREATED)
ORDER BY COUNT(*) DESC NULLS LAST
) AS RN
FROM ORDERS
WHERE CREATED > TRUNC(SYSDATE - 2)
GROUP BY TRUNC(CREATED, 'HH'), TRUNC(CREATED)
)
WHERE RN = 1
Cheers!!

query on sales for each day and previous sale for each product also

I want to show a sales of a product and precious sale of that product also if that product is not sale till date than previous sale column should be null. My source table has id, name, sales_date,quantity and unit_price and resultant table should contain id,name,sales_Date,current_sale (which would contain sale on that day) and previous_sale.The previous sale is for individual product not same for all product.
Let's say you have these data in your table:
ID NAME SALES_DATE QUANTITY UNIT_PRICE
---- ----- ----------- -------- --------------
1 p1 2015-07-24 10 5,00
2 p2 2015-07-24 14 10,00
3 p1 2015-07-28 15 4,00
4 p2 2015-07-29 7 11,00
5 p3 2015-07-29 3 2,00
This query, using function lag(), generates output you described (plus column previous_sales_date):
select id, name, sales_date, unit_price*quantity current_sale,
lag(sales_date) over (partition by name order by sales_date) prev_date,
lag(unit_price*quantity) over (partition by name order by sales_date) prev_sale
from sales order by name, sales_date
SQLFiddle demo
I used partition by name, but if in input table "id" means "product id" it's better to use partition by id.
Output:
ID NAME SALES_DATE CURRENT_SALE PREV_DATE PREV_SALE
---- ----- ----------- ------------ ----------- ----------
1 p1 2015-07-24 50
3 p1 2015-07-28 60 2015-07-24 50
2 p2 2015-07-24 140
4 p2 2015-07-29 77 2015-07-24 140
5 p3 2015-07-29 6
Edit: If there are many entries for product per day you need some form of aggregation, most obvious is sum,
like in example below. You can also use min, max, avg.
select name, sales_date, sale current_sale, cnt,
lag(sales_date) over (partition by name order by sales_date) prev_date,
lag(sale) over (partition by name order by sales_date) prev_sale,
lag(cnt) over (partition by name order by sales_date) prev_cnt
from (
select name, trunc(sales_date) sales_date, sum(unit_price*quantity) sale, count(1) cnt
from sales group by name, trunc(sales_date)
)
order by name, sales_date
SQLFiddle demo
I also added columns cnt and prev_cnt - showing number of rows for that product in current and previous days.

SELECT only rows that aren't repeated

So I have a table like this. This is a standard Order header - Order Detail table:
order id order_line
----------- -----------
100 1
100 2
100 3
101 1
102 1
103 1
103 2
104 1
105 1
Now, how can I make a SELECT that will only pick the orders that only have one line?
In this case I don't want orders 100 and 103.
Thanks!
Tiago
Counting lines using "group by order_id" is a good solution, however counting is not needed, simpler Max function works fine:
select order_id from orders
group by order_id
having max(order_line)=1;
In case order_line has consecutive values further "optimization" is possible:
select order_id from orders
where order_line <= 2
group by order_id
having max(order_line)=1;
Group by the order_id and take only those having 1 record per group
select order_id
from orders
group by order_id
having count(*) = 1
If you need the complete record then do
select t1.*
from orders t1
join
(
select order_id
from orders
group by order_id
having count(*) = 1
) t2 on t1.order_id = t2.order_id
You can try following query too :
select order_id , order_line
from Order_Detail
group by order_id ,order_line
having count(order_id)<2;

Hive Grouping and calculating average by calculating distinct

Folks we have one wired requirement in HIVE and we are not able to write query for the same
Basically we have following data.
CUSTOMER_NAME PRODUCT_NAME PRICE OCCURANCE ID
customer1, product1, 20, 1
customer1, product2, 30, 2
customer1, product1, 25, 3
customer1, product1, 20, 1
customer1, product2, 20, 2
Basically what we have to do is list the average price for (customer_name,product_name) for single occurance.
e.g. for combination (customer1,product1) price for product1 is
25+20/2(no of distinct occurences for customer(1 and 3)) = 22.5. But as we want to group by PRODUCT_NAME also we donot know how to calculate the distinct occurance. I have marked the query with [] bracket where we feel we need to do some change.
Other aspect is the inner query here we want to select customers where their average price will fall in to top 5 for distinct occurrencs. ( This works properly as group by clause is having only one attribute CUSTOMER_NAME)
select customer_name,product_name,[sum(price)/count(distinct(occurance_id))]
from customer_prd cprd
Join (select customer_name,sum(price)/count(distinct(occurance id))
order by sum group
by customer_name limit 5)
cprdd
where cprd.customer_name = cprdd.customer_name group by cprd.customer_name,cprd.product_name
output expected.
customer1,product1, 20 (avg for occurance ID 1) + 25(average for occurance ID 2)/2 = 22.5
customer1,product2, 30 + 20/2 = 25
If I understand correctly, it seems like the only trouble here is that you have duplicates. If you remove duplicate occurrences, then it's a simple group by and average:
select customer_name, product_name, avg(price)
from (
select distinct customer_name, product_name, price, occurance_id from cprd
) t
group by customer_name, product_name

Resources