Group by month including empty months - mysql

I want to select all my order values per month. I know this works fine with GROUP BY month but only with months with orders in it. Now I want also the months with no orders so I get all months.
This is my query:
SELECT SUM(VerkoopfactBedrag) AS bedrag, DATE_FORMAT(VerkoopfactDatum,'%M') AS date
FROM verkoopfacturen
WHERE Verkoopfact_UserId = 12
AND VerkoopfactDatum BETWEEN '2011-01-01' AND '2011-12-30'
GROUP BY MONTH(VerkoopfactDatum)
So when the result of a month is 0 I want to see the month with value 0 but now the month don't show up.
Is this possible?

One way to do this is to create and populate a table full of consecutive months.
You can then OUTER JOIN using that table.
So something like:
drop table if exists all_months;
create table all_months (a_month_id int unsigned PRIMARY KEY,a_month varchar(20) NOT NULL, UNIQUE KEY `all_months_uidx1` (a_month));
insert into all_months values (1,'January');
insert into all_months values (2,'February');
insert into all_months values (3,'March');
insert into all_months values (4,'April');
insert into all_months values (5,'May');
insert into all_months values (6,'June');
insert into all_months values (7,'July');
insert into all_months values (8,'August');
insert into all_months values (9,'September');
insert into all_months values (10,'October');
insert into all_months values (11,'November');
insert into all_months values (12,'December');
SELECT SUM(IFNULL(t1.VerkoopfactBedrag,0)) AS bedrag,
am.a_month AS date
from
(
select
ifnull(vn.VerkoopfactBedrag,0) as VerkoopfactBedrag,
cast(DATE_FORMAT(VerkoopfactDatum, '%M') as char) as mdate
FROM verkoopfacturen vn
WHERE Verkoopfact_UserId = 12
AND VerkoopfactDatum BETWEEN '2011-01-01' AND '2011-12-31'
GROUP BY DATE_FORMAT(VerkoopfactDatum, '%M')
) t1 RIGHT OUTER JOIN all_months am on t1.mdate = am.a_month
group by am.a_month
order by a_month_id asc;
PS Not sure if you have anything against Oudejaarsavond but there are 31 days in December ;-)

After searching for a simple solution i finally found this which I think is SIMPLE. This will show last year and this years sales side by side.
select
date_format(current_date - INTERVAL 1 YEAR,'%Y') as LAST_YR,
date_format(NOW(),'%Y') as THIS_YR,
monthname(date) as month,
sum(case when year(date) = date_format(current_date - INTERVAL 1 YEAR,'%Y') then amount else 0 end) as sales_ly,
sum(case when year(date) = DATE_FORMAT(NOW(),'%Y') then amount else 0 end) as sales_ty
from tablename
where date between date_format(current_date - INTERVAL 1 YEAR,'%Y-%01-%01')
and date_format(current_date, '%Y-%12-%31')
group by monthname(date)
order by max(month(date));

Related

How to return a row for every date in SQL?

I want to retrieve the sum of transactions for every date from the last 7 days from my MySQL database, but some dates don't have any transactions. How do I return a 0 for those days?
Here is the SQL query I've worked on nd tried, but this one only gives those that do have a value for those days.
SELECT COUNT(transaction_id) AS orders, SUM(amount) AS sales, CAST(time AS DATE) AS time FROM tbltransactions WHERE time BETWEEN CAST(? AS DATE) AND CAST(? AS DATE) GROUP BY CAST(time AS DATE) ORDER BY time ASC
Try to generate the dates first, then join your data table:
SELECT COUNT(transaction_id) AS orders
, SUM(amount) AS sales
, CAST(dates.time AS DATE) AS time
FROM
(
SELECT DATE_SUB(CURDATE(), INTERVAL 7 DAY) + INTERVAL num DAY AS time
FROM
(
SELECT 1 AS num UNION ALL SELECT 2 UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5 UNION ALL SELECT 6 UNION ALL SELECT 7
) n
) dates
LEFT JOIN tbltransactions
ON dates.time = tbltransactions.time
GROUP BY CAST(dates.time AS DATE)
ORDER BY dates.time ASC
here is the test data:
CREATE TABLE tbltransactions (
time DATE,
transaction_id INT,
amount DECIMAL(10,2)
);
INSERT INTO tbltransactions (time, transaction_id, amount)
VALUES
('2023-01-20', 1, 100.00),
('2023-01-21', 2, 200.00),
('2023-01-27', 3, 300.00),
('2023-01-29', 4, 400.00),
('2023-01-29', 5, 500.00);

Expand rows to contain monthly rows between 2 dates

I have a table 'positions' with the following variable:
- id
- role
- startdate
- enddate
I need to generate monthly series using data of anothr table. Is there a way to expand each of the rows of the table 'positions' so that each role is expanded into n-rows of months between start date and end date.
For example, Row 1 containing:
(0001, 'Salesperson', '2020-01', '2020-05')
I need to expand into something like:
(0001, 'Salesperson', '2020-01')
(0001, 'Salesperson', '2020-02')
(0001, 'Salesperson', '2020-03')
(0001, 'Salesperson', '2020-04')
(0001, 'Salesperson', '2020-05')
Thanks!
I tried iterating through the table but haven't been able to get the reuslt
Your question is tagged with 2 Databases. I am using SQL Server to answer. You can try something like this:
SQL Fiddle DEMO
DECLARE #StartDate DATETIME = '2018-01-01 00:00:00.000'; -- this can be any date below the minimum StartDate
WITH tt AS (
SELECT '0001' AS EmployeeID, '2020-01-01 00:00:00.000' AS StartDate, '2020-05-31 00:00:00.000' AS EndDate
),
MyTable AS
(
SELECT #StartDate AS myDate
UNION ALL
SELECT DATEADD(Month,1,myDate)
FROM MyTable
WHERE DATEADD(Month,1,myDate) <= '2020-05-31 00:00:00.000'
)
SELECT
EmpId.EmployeeID,
CONVERT(VARCHAR(7), a.myDate, 126)
FROM
MyTable a
INNER JOIN
(
SELECT EmployeeID, MIN(StartDate) MinStartDate
FROM tt
GROUP BY EmployeeID
) EmpId ON
a.MyDate >= EmpId.MinStartDate
LEFT JOIN
tt ON
EmpId.EmployeeID = tt.EmployeeID AND
a.myDate >= tt.StartDate AND
a.myDate <= ISNULL(tt.EndDate, GETDATE())
ORDER BY EmpId.EmployeeID DESC, a.MyDate
OPTION (MAXRECURSION 0)
Also I have assumed that your startDate and EndDate are in Date format inside your table.

SQL: Group by for last 3 months and last 5 months respectively

Based on a table of the sales from different time periods, I am trying to calculate aggregated values from 2 different time periods: (1) Sales where date > '2018-11-26' and (2) sales where date > '2018-09-26'
create table revenue (sales float, date_time datetime)
insert into revenue (sales,date_time) values (300, '2018-09-01')
insert into revenue (sales,date_time) values (200, '2018-10-01')
insert into revenue (sales,date_time) values (300, '2018-11-01')
insert into revenue (sales,date_time) values (400, '2019-01-01')
insert into revenue (sales,date_time) values (500, '2019-02-01')
I've seen other solutions that uses case when for time periods, however the difference in this case is that the time periods are not mutually exclusive.
The query I want should work something like this:
select sum(sales) from revenue
group by date_time where date_time > '2018-11-26',
date_time where date_time > '2018-09-26'
you can use union all
select 'p1' as prd , sum(sales) from revenue
where date_time >= '2018-11-26'
union all
select 'p2', sum(sales) from revenue
where date_time > '2018-09-26'
or you can use case when
select case when date_time >= '2018-11-26' then 'p1'
when date_time > '2018-09-26'
then 'p2' end as period, sum(sales) from revenue
group by case when date_time >= '2018-11-26' then 'p1'
when date_time > '2018-09-26'
then 'p2' end

Multiple Select Subquery Count based on Hour of Day, Would Like to Add Table/Column

Right now, I have a multiple select subquery that is grabbing data based on hour of the day that's a count. What I want to do now, is to introduce another table into that query, and count based on an id as well as the datetime in the original table.
What I have right now is:
select
(
select count(a_date)
from t1
where d_date
between '2013-01-07 00:00:00' and '2013-01-07 00:59:59'
) AS '00:00 to 00:59',
(
select count(a_date)
from t1
where d_date
between '2013-01-07 01:00:00' and '2013-01-07 01:59:59'
) AS '01:00 to 01:59'
and so on, till the end of the day.
I have another query that's giving me the count based on the id and datetime, but there's only two columns, one which is showing the c_name and the other showing the count for the hour.
Ex.
select t2.c_name, count(t1.a_date)
from t2 join t1
on t2.t1_key = t1.t2_key
where t1.d_date
between '2013-01-07 00:00:00' and '2013-01-07 00:59:59'
group by t2.c_id
Basically, I'd like to combine these two queries into one that can show the c_name and all of the hours of the day.
Any suggestions?
I would look into using the CASE statement.
Try something like this (adding your additional 23 columns):
select c_name,
SUM(case when HOUR(d_date) = 0 then 1 else 0 end) '00:00 to 00:59',
SUM(case when HOUR(d_date) = 1 then 1 else 0 end) '01:00 to 01:59'
from t2
join t1 on t2.t1_key = t1.t2_key
group by c_name
And here is the SQL Fiddle.
You just need to add your WHERE criteria for d_date -- something like:
where d_date between '2013-01-07 00:00:00' and '2013-01-07 23:59:59'
or
where Date(d_date) = '2013-01-07'
Good luck!

Combining multiple columns in 1 resultset

I have a table like
CREATE TABLE sales
(`id` int, `date` date, `amount` int(4))
;
insert into sales values (1, '2012-09-01', 200),
(2, '2012-09-01', 300),
(3, '2012-09-02', 400),
(4, '2012-09-02', 500),
(5, '2012-09-02', 600)
I wish to retrieve a row showing the sales for today, and the sales for yesterday
like
Date Today Total sales Yesterday Sales
2012-09-02 1500 500
Tried using something like
SELECT id, date, sum(amount) FROM sales
GROUP BY date;
But it returns the sales day wise. I understand that can be done programmatically, but is there a better way to directly retrieve it from the DB?
sqlfiddle
SELECT id, date(now()) as `date`,
SUM(IF(date(`date`) = date(now()), `amount`, 0)) as TodayTotalSales,
SUM(IF(date(`date`) < date(now()), `amount`, 0)) as OtherDaySales
FROM sales;
http://sqlfiddle.com/#!2/0ef6a/18
You are getting that because Id is different for each record. You have two option now:
Don't retrieve Id and write query like:
SELECT date, sum(amount) FROM sales
GROUP BY date;
Use a join with subquery
SELECT a.ID, a.date, b.amount
FROM sales a, (SELECT date, sum(amount) amount FROM sales
GROUP BY date) b
WHERE a.date = b.date;
Please Note: In option 2, second and third columns will be repeating with same value for each id within a day.
SELECT date, sum(amount), yestersales
FROM sales AS s1,
(SELECT sum(amount) as yestersales, ADDDATE(date, 1) AS yesterdate
FROM sales GROUP BY date) AS s2
WHERE s1.date = s2.yesterdate
GROUP BY date;
Will do what you want, but it's not really very efficient, I don't think. I would personally do it in code.
Selecting the ID doesn't really make much sense here since you're grouping by date.