Building Monthly aggregate from Daily table - sql

I have written the code below to check for unique customers for the last 30 days. How would I repurpose this code to check for unique customers on a month start date. I am trying to build a monthly aggregate using table_billing that has a daily grain. Can you please guide me.
select 'context.processingDate' as rptg_dt, COALESCE(item_type,'ALL_ITEMS') as item_type, unique_customers
from (select
(case when item_type_code in ('A') then 'Books'
when item_type_code in ('B','C') then 'Toys'
else 'Fruits' end
) as item_type,
count(distinct person_id) as unique_customers
from table_billing
where rptg_dt between cast('context.processingDate' as date format 'YYYY-MM-DD')-30
AND cast('context.processingDate' as date format 'YYYY-MM-DD')
and item_type_code in ('A','B','C','D','E')
group by CUBE(1)
) a;
Desired Output:
Monthly Start Date | Item Type | Unique Customers
5/1/14 | Books | 100
5/1/14 | Toys | 80
5/1/14 | Fruits | 25
5/1/14 | ALL_ITEMS | 175
6/1/14 | Books | 80
6/1/14 | Toys | 60
6/1/14 | Fruits | 40
6/1/14 | ALL_ITEMS | 95
I am looking to re-write this query as follows:
select 'context.processingDate' as month_start_dt,
COALESCE(item_type,'ALL_ITEMS') as item_type, unique_customers
from (select
(case when item_type_code in ('A') then 'Books'
when item_type_code in ('B','C') then 'Toys'
else 'Fruits' end
) as item_type,
count(distinct person_id) as unique_customers
from table_billing
where month_start_dt = cast('context.processingDate' as date format'YYYY-MM-DD') and item_type_code in ('A','B','C','D','E') group by CUBE(1)) a;
How would I tweak the query to make this possible? Thank you!

Related

case when statement in oracle across tables

Hi apologies for formatting but im stumped and frustrated and i just need some help.
I've got two tables. I have made a good faith attempt to follow community standards but just in case it doesnt work, Table A has 3 columns 'ID', to identify a sales rep, 'Start' to indicate what company term they started, and 'Sales' to indicate their sales in that first term. Table B is just an expansion of Table A where it lists all terms (i marked it as quarters) a sales person was there and their sales.
Table A
+----+---------+-------+
| ID | Quarter | Sales |
+----+---------+-------+
| 1 | 141 | 30 |
| 2 | 151 | 50 |
| 3 | 151 | 80 |
+----+---------+-------+
Table B
+----+---------+-------+
| ID | Quarter | Sales |
+----+---------+-------+
| 1 | 141 | 30 |
| 1 | 142 | 25 |
| 1 | 143 | 45 |
| 2 | 151 | 50 |
| 2 | 152 | 60 |
| 2 | 153 | 75 |
| 3 | 151 | 80 |
| 3 | 152 | 50 |
| 3 | 153 | 70 |
+----+---------+-------+
My desired output is a table with ID, start term, sales from that term, second term, sales from that term, etc. for the first 6 terms an employee is there
my code is this
select a.id, start, a.sales,
case when a.start+1 = b.quarter then sales end as secondquartersales,
case when a.start+2 = b.quarter then sales end as thridquartersales,.....
from tablea a
left join tableb b
on a.id = b.id;
it gives nulls for all case when statements. please help
maybe try GROUP BY
create table a ( id number, strt number, sales number);
create table b (id number, quarter number , sales number);
insert into a values (1,141,30);
insert into a values (2,151,50);
insert into a values (3,151,80);
insert into b values ( 1,141,30);
insert into b values ( 1,142,25);
insert into b values ( 1,143,45);
insert into b values ( 2,151,50);
insert into b values ( 2,152,60);
insert into b values ( 2,153,75);
insert into b values ( 3,151,80);
insert into b values ( 3,152,50);
insert into b values ( 3,153,70);
select a.id, a.strt, a.sales,
max(case when a.strt+1 = b.quarter then b.sales end ) as secondquartersales,
max(case when a.strt+2 = b.quarter then b.sales end ) as thridquartersales
from a, b
where a.id = b.id
group by a.id, a.strt, a.sales;
OR PIVOT
select * from (
select a.id,
case when a.strt+1 = b.quarter then 'Q2'
when a.strt+2 = b.quarter then 'Q3'
when a.strt+3 = b.quarter then 'Q4'
when a.strt = b.quarter then 'Q1'end q,
b.sales sales
from a, b
where a.id = b.id)
pivot ( max(nvl(sales,0)) for Q in ('Q1', 'Q2', 'Q3', 'Q4'));
This is valid ANSI 92 SQL, as it is an inner join. The whole ANSI style version is just syntax candy.

How to count occurrence in SQL

I have a list of items (e.g. shirts, tops, pants, adidas, nike, puma etc.) and years in a format like this 2017-01-01. I want to find out how many times each item was purchased per year and have it arranged by year.
How can I do this?
I have the following table called Items:
Year | Purchases |
------------------
2017-01-01 | makeup
2018-01-01 | clothing
2019-01-01 | makeup
2017-01-01 | shoes
2017-01-01 | clothing
2016-01-01 | shoes
2018-01-01 | clothing
2017-01-01 | clothing
2019-01-01 | makeup
The desired output is something like this:
Year | Purchases| Count
-----------------------
2016 | Shoes | 1
2017 | Makeup | 1
2017 | Clothing | 2
2017 | Shoes | 1
2018 | Clothing | 2
2019 | Makeup | 2
My code so far is this:
SELECT YEAR(d.date_format) AS Year
, Purchase = (CASE WHEN it.type IN ('shirts', 'tops', 'pants) THEN 'clothing'
WHEN it.type('nike','adidas', 'puma') THEN 'shoes'
WHEN it.type('facewash', 'lipstick') THEN 'makeup' END), COUNT(*)
FROM ....
INNER JOIN...
WHERE...
GROUP BY Year, Purchase
ORDER BY Year
select year, purchases, count(*)
from table
group by year, purchase
order by 1, 2
you can use group by with order by
select year, purchases, count(*)
from myTable
group by year, purchases
order by year
Is this what you want?
SELECT YEAR(d.date_format) AS Year,
(CASE WHEN it.type IN ('shirts', 'tops', 'pants') THEN 'clothing'
WHEN it.type IN ('nike','adidas', 'puma') THEN 'shoes'
WHEN it.type IN ('facewash', 'lipstick') THEN 'makeup'
END), COUNT(*)
FROM .... INNER JOIN...
WHERE...
GROUP BY YEAR(d.date_format),
(CASE WHEN it.type IN ('shirts', 'tops', 'pants') THEN 'clothing'
WHEN it.type IN ('nike','adidas', 'puma') THEN 'shoes'
WHEN it.type IN ('facewash', 'lipstick') THEN 'makeup'
END)
ORDER BY Year;
The use of = in the SELECT suggests that you are using SQL Server. SQL Server does not support the use of aliases in the GROUP BY, so you need to repeat the expression.

I'm getting unexpected results when I use SUM(CASE WHEN .... THEN "column_name") in sqlite3

I am trying to find the number of orders I got in the month of April. I have 3 orders but my query gets the result 0. What could be the problem?
Here's the table:
id | first | middle | last | product_name | numberOut | Date
1 | Muhammad | Sameer | Khan | Macbook | 1 | 2020-04-01
2 | Chand | Shah | Khurram | Dell Optiplex | 1 | 2020-04-02
3 | Sultan | | Chohan | HP EliteBook | 1 | 2020-03-31
4 | Express | Eva | Plant | Dell Optiplex | 1 | 2020-03-11
5 | Rana | Faryad | Ali | HP EliteBook | 1 | 2020-04-02
And here's the query:
SELECT SUM(CASE WHEN strftime('%m', oDate) = '04' THEN 'id' END) FROM orders;
If you want all Aprils, then you can just look at the month. I would recommend:
select count(*)
from orders o
where o.date >= '2020-04-01' and o.date < '2020-05-01';
Note that this does direct comparisons of date to a valid dates in the where clause.
The problem with your code is this:
THEN 'id'
You are using the aggregate function SUM() and you sum over a string literal like 'id' which is implicitly converted to 0 (because it can't be converted to a number) so the result is 0.
Even if you remove the single quotes you will not get the result that you want because you will get the sum of the ids.
But if you used:
THEN 1 ELSE 0
then you would get the correct result.
But with SQLite you can write it simpler:
SELECT SUM(strftime('%m', oDate) = '04') FROM orders;
without the CASE expression.
Or since you just want to count the orders then COUNT() will do it:
SELECT COUNT(*) FROM orders WHERE strftime('%m', oDate) = '04';
Edit.
If you want to count the orders for all the months then group by month:
SELECT strftime('%Y-%m', oDate) AS month,
COUNT(*) AS number_of_orders
FROM orders
GROUP BY month;
SELECT SUM(CASE WHEN strftime('%m', oDate) = '04' THEN 1 ELSE 0 END) FROM orders;
if you need to use SUM
There is a problem with your query. You do not need to do that aggregation operation.
SELECT COUNT(*) from table_name WHERE strftime('%m', Date) = '04';
I would use explicit date comparisons rather than date functions - this makes the query SARGeable, ie it may benefit an existing index.
The most efficient approach, with a filter in the where clause:
select count(*) cnt
from orders
where oDate >= '2020-04-01' and oDate < '2020-05-01'
Alternatively, if you want a result of 0 even when there are no orders in April you can do conditional aggregation, as you originally intended:
select sum(case when oDate >= '2020-04-01' and oDate < '2020-05-01' then 1 else 0 end) cnt
from orders

How to get different data from the same table with months as parameters?

I have a table in Oracle where a price for an article and a date are included but I need the price of the article from the last month and the current month to see the difference.
What I need is to pass the last month as paremeter and get data
Example:
+---------+-------+------------+
| article | price | date |
+---------+-------+------------+
| cup | 3.5 | 02/06/2018 |
+---------+-------+------------+
| cup | 3.7 | 04/07/2018 |
+---------+-------+------------+
| cup | 3.8 | 04/08/2018 |
+---------+-------+------------+
Expected:
Selected month: 07
+---------+--------------+-------------+----------+----------+
| article | current_price|current_month|last_price|last_mont |
+---------+--------------+-------------+----------+----------+
| cup | 3.7 | 07/2018 | 3.5 | 06/2018 |
+---------+--------------+-------------+----------+----------+
You can use the following SQL statement :
select article,
max(decode(to_char("date",'mm/yyyy'),to_char(sysdate,'mm/yyyy'),nvl(price,0)))
as current_price,
to_char(sysdate,'mm/yyyy') as current_month,
max(decode(to_char("date",'mm/yyyy'),to_char(add_months(sysdate,-1),'mm/yyyy'),
nvl(price,0)))
as last_price,
to_char(add_months(sysdate,-1),'mm/yyyy') as last_month
from articles
where article = 'cup'
group by article;
ARTICLE CURRENT_PRICE CURRENT_MONTH LAST_PRICE LAST_MONTH
cup 3,70 07/2018 3,50 06/2018
assuming sysdate(current date) between 01/07/2018 - 31/07/2018 (format dd/mm/yyyy)
Rextester Demo
Assuming the user is specifying a date, then the following gets the last two dates and prices through that date:
select article_id,
max(case when seqnum = 1 then price end) as current_price,
max(case when seqnum = 1 then date end) as current_date,
max(case when seqnum = 2 then price end) as last_price,
max(case when seqnum = 2 then price end) as last_date
from (select e.*,
row_number() over (partition by article_id order by date desc) as seqnum
from examples e
where e.date <= :date
) e
group by article_id;

Select only 1 payment from a table with customers with multiple payments

I have a table called "payments" where I store all the payments of my costumers and I need to do a select to calculate the non-payment rate in a given month.
The costumers can have multiples payments in that month, but I should count him only once: 1 if any of the payments is done and 0 if any of the payment was made.
Example:
+----+------------+--------+
| ID | DATEDUE | AMOUNT |
+----+------------+--------+
| 1 | 2016-11-01 | 0 |
| 1 | 2016-11-15 | 20.00 |
| 2 | 2016-11-10 | 0 |
+----+------------+--------+
The result I expect is from the rate of november:
+----+------------+--------+
| ID | DATEDUE | AMOUNT |
+----+------------+--------+
| 1 | 2016-11-15 | 20.00 |
| 2 | 2016-11-10 | 0 |
+----+------------+--------+
So the rate will be 50%.
But if the select is:
SELECT * FROM payment WHERE DATEDUE BETWEEN '2016-11-01' AND '2016-11-30'
It will return me 3 rows and the rate will be 66%, witch is wrong. Ideas?
PS: This is a simpler example of the real table. The real query have a lot of columns, subselects, etc.
It sounds like you need to partition your results per customer.
SELECT TOP 1 WITH TIES
ID,
DATEDUE,
AMOUNT
ORDER BY ROW_NUMBER() OVER (PARTITION BY ID ORDER BY AMOUNT DESC)
WHERE DATEDUE BETWEEN '2016-11-01' AND '2016-11-30'
PS: The BETWEEN operator is frowned upon by some people. For clarity it might be better to avoid it:
What do BETWEEN and the devil have in common?
Try this
SELECT
id
, SUM(AMOUNT) AS AMOUNT
FROM
Payment
GROUP BY
id;
This might help if you want other columns.
WITH cte (
SELECT
id
, ROW_NUMBER() OVER (PARTITION BY ID ORDER BY AMOUNT DESC ) AS RowNum
-- other row
)
SELECT *
FROM
cte
WHERE
RowNum = 1;
To calculate the rate, you can use explicit division:
select 1 - count(distinct case when amount > 0 then id end) / count(*)
from payment
where . . .;
Or, in a way that is perhaps easier to follow:
select avg(flag * 1.0)
from (select id, (case when max(amount) > 0 then 0 else 1 end) as flag
from payment
where . . .
group by id
) i