How to get summary data for every months in mysql - mysql

I want to count the number of items sold(item_count) every month for every item,
--
-- Table structure for table `sales`
--
CREATE TABLE `sales` (
`id` int(11) NOT NULL,
`item_id` int(11) NOT NULL,
`date` date NOT NULL,
`item_count` int(11) NOT NULL,
`amount` float NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
--
-- Dumping data for table `sales`
--
INSERT INTO `sales` (`id`, `item_id`, `date`, `item_count`, `amount`) VALUES
(1, 1, '2018-01-15', 11, 110),
(2, 2, '2018-01-21', 5, 1000),
(3, 1, '2018-02-02', 7, 700),
(4, 2, '2018-02-11', 3, 3000);
I have tried this SQL, but it's not showing the data correctly.
SELECT `sales`.`item_id`,
(CASE WHEN MONTH(sales.date)=1 THEN sum(sales.item_count) ELSE NULL END) as JAN,
(case when MONTH(sales.date)=2 THEN sum(sales.item_count) ELSE NULL END) as FEB
FROM sales WHERE 1
GROUP BY sales.item_id
ORDER BY sales.item_id
This is my expected result,
item_id JAN FEB
1 11 7
2 5 3
I am getting this,
item_id JAN FEB
1 18 NULL
2 8 NULL

Here is an immediate fix to your query. You need to sum over a CASE expression, rather than the other way around.
SELECT
s.item_id,
SUM(CASE WHEN MONTH(s.date) = 1 THEN s.item_count END) AS JAN,
SUM(CASE WHEN MONTH(s.date) = 2 THEN s.item_count END) AS FEB
FROM sales s
GROUP BY
s.item_id
ORDER BY
s.item_id;
But the potential problem with this query is that in order to support more months, you need to add more columns. Also, if you want to cover mulitple years, then this approach also might not scale. Assuming you only have a few items, here is another way to do this:
SELECT
DATE_FORMAT(date, '%Y-%m') AS ym,
SUM(CASE WHEN item_id = 1 THEN item_count END) AS item1_total,
SUM(CASE WHEN item_id = 2 THEN item_count END) AS item2_total
FROM sales
GROUP BY
DATE_FORMAT(date, '%Y-%m');
This would generate output looking something like:
ym item1_total item2_total
2018-01 11 5
2018-02 7 3
Which version you use depends on how many months your report requires versus how many items might appear in your data.

Related

get specific data along with group by

I've a table named log.
Table: log
ID user_id time_of_action
I want to get result for each user for each date i.e. group by date,user_id.
So, here's the expected output structure:
user_id date occurred_in_afternoon occurred_at_night total_action_count
Explanation:
occurred_in_afternoon: whether any action of a user occurred in between 12:00 PM to 4:00 PM
occurred_at_night: whether any action of a user occurred between 8:00 PM to 12:00 AM (next day)
Schema and sample data:
DROP TABLE IF EXISTS `logs`;
CREATE TABLE `logs` (
`Id` int(11) NOT NULL AUTO_INCREMENT,
`user_id` int(11) DEFAULT NULL,
`time_of_action` timestamp NULL DEFAULT NULL ON UPDATE CURRENT_TIMESTAMP,
PRIMARY KEY (`Id`)
);
INSERT INTO `logs` VALUES ('1', '71', '2016-03-10 10:07:34');
INSERT INTO `logs` VALUES ('2', '66', '2016-03-10 14:07:57');
INSERT INTO `logs` VALUES ('3', '71', '2016-03-10 22:08:27');
INSERT INTO `logs` VALUES ('4', '71', '2016-03-10 15:08:40');
And here's my current query:
SELECT
user_id,
DATE(time_of_action) `date`,
CASE WHEN time_of_action BETWEEN TIMESTAMPADD(HOUR,12,DATE(time_of_action)) AND TIMESTAMPADD(HOUR,16,DATE(time_of_action)) THEN 1 ELSE 0 END occurred_in_afternoon,
CASE WHEN time_of_action BETWEEN TIMESTAMPADD(HOUR,20,DATE(time_of_action)) AND TIMESTAMPADD(HOUR,24,DATE(time_of_action)) THEN 1 ELSE 0 END occurred_at_night,
COUNT(*) total_action_count
FROM `logs`
GROUP BY `date`,user_id
my current output:
user_id date occurred_in_afternoon occurred_at_night total_action_count
66 2016-03-10 1 0 1
71 2016-03-10 0 0 3
Expected output:
user_id date occurred_in_afternoon occurred_at_night total_action_count
66 2016-03-10 1 0 1
71 2016-03-10 1 1 3
The problem is that I am not getting the expected result. I guess occurred in afternoon value is reset by another time_of_action which doesn't lie in that afternoon region.
And is it possible to implement it in a single query?
You missed to use an aggregate function. You can use MAX() or BIT_OR() for your purpose:
SELECT
user_id,
DATE(time_of_action) `date`,
MAX(CASE WHEN time_of_action BETWEEN TIMESTAMPADD(HOUR,12,DATE(time_of_action)) AND TIMESTAMPADD(HOUR,16,DATE(time_of_action)) THEN 1 ELSE 0 END) occurred_in_afternoon,
MAX(CASE WHEN time_of_action BETWEEN TIMESTAMPADD(HOUR,20,DATE(time_of_action)) AND TIMESTAMPADD(HOUR,24,DATE(time_of_action)) THEN 1 ELSE 0 END) occurred_at_night,
COUNT(*) total_action_count
FROM `logs`
GROUP BY `date`,user_id
Update: I would also prefer a more readable version like
SELECT
user_id,
DATE(time_of_action) `date`,
BIT_OR(TIME(time_of_action) BETWEEN '12:00:00' AND '16:00:00') occurred_in_afternoon,
BIT_OR(TIME(time_of_action) BETWEEN '20:00:00' AND '23:59:59') occurred_at_night,
COUNT(*) total_action_count
FROM `logs`
GROUP BY `date`,user_id
I was thinking to have an alias of the result table that I've got through SUM in order to get Binary value for those two fields.
SELECT
t.user_id,
t.date,
CASE WHEN t.occurred_in_afternoon > 0 THEN 1 ELSE 0 END AS occurred_in_afternoon,
CASE WHEN t.occurred_at_night > 0 THEN 1 ELSE 0 END AS occurred_at_night,
t.total_action_count
FROM
(SELECT
user_id,
DATE(time_of_action) `date`,
SUM(CASE WHEN time_of_action BETWEEN TIMESTAMPADD(HOUR,12,DATE(time_of_action)) AND TIMESTAMPADD(HOUR,16,DATE(time_of_action)) THEN 1 ELSE 0 END) occurred_in_afternoon,
SUM(CASE WHEN time_of_action BETWEEN TIMESTAMPADD(HOUR,20,DATE(time_of_action)) AND TIMESTAMPADD(HOUR,24,DATE(time_of_action)) THEN 1 ELSE 0 END) occurred_at_night,
COUNT(*) total_action_count
FROM `logs`
GROUP BY `date`,user_id) t

mySQL - return 0 as an aggregate result if a field not found

If for example I have:
CREATE TABLE application (
`id` INT(11) UNSIGNED AUTO_INCREMENT PRIMARY KEY,
`month` VARCHAR(255) NOT NULL,
`amount` DECIMAL(9,2) NOT NULL)
;
INSERT INTO application
(`id`, `month`, `amount`)
VALUES
(1, 'january', 2000.00),
(2, 'february', 1000.00),
(3, 'january', 3000.00),
(4, 'january', 5000.00)
;
And then I run the query:
SELECT SUM(`amount`) as sum FROM application WHERE month IN ('january', 'february', 'march') GROUP BY `month`;
I get the result:
month sum
___________________
january | 10000.00
february | 1000.00
which is what the query was supposed to do however I'm looking for this result:
month sum
___________________
january | 10000.00
february | 1000.00
march | 0.00
how can I achieve this?
if anyone needs clarity don't vote down just ask and I will be more precise if i can.
cheers
SELECT m.mname, SUM(ISNULL(a.`amount`,0)) as sum
FROM
(
select 'january' as mname union all
select 'february' as mname union all
select 'march' as mname
) m LEFT JOIN application a on a.`month` = m.mname
GROUP BY a.`month`

How to Collect Month wise school fee where months are selected from checkboxes?

I am new in this field, I am working on a school fee management system, fee collected from students on month basis, yearly basis etc
My MySQL database schema is as follow
academic_classes table
class_id class_name
1 1st
2 2nd
.....and so on
Fee_types Table
fee_type_id fee_name
1 Admission Fee
2 Tuition Fee
3 Sports Fee
class_wise_fee_plan table
plan_id class_id fee_id amount
1 1 1 5000
2 1 2 1150
3 1 3 350
fee amount is according to classes
according to your suggestion I have add a new table
for fee frequency yearly, monthly etc
fee_writeoff table
fee_writeoff_id fee_id months
1 1 apr
2 2 jan
3 2 feb
and so on ...
I have 12 checkboxes for months in front end, How to calculate or show together fee values and fee name based on check boxes.
I want this type of Results
FeeName Apr May Jun ..... Total
Admission fee 5000 0 0 5000
Tution Fee 1100 1100 1100 3300
Total 6100 1100 1100 8300
how to create mysql stored procedure if months name selected from checkboxes from front end because months name are comma saparated how to loop through and create cases
Try below query using CASE, it is not a complete solution as you have asked for but this will solve some of your issues.
SELECT ft.fee_name, (CASE WHEN apr=1 THEN fee_amount ELSE 0 END) AS apr,
(CASE WHEN may=1 THEN fee_amount ELSE 0 END) AS apr,
(CASE WHEN jun=1 THEN fee_amount ELSE 0 END) AS apr,
(CASE WHEN jul=1 THEN fee_amount ELSE 0 END) AS apr,
(CASE WHEN aug=1 THEN fee_amount ELSE 0 END) AS apr,
(CASE WHEN apr=1 THEN fee_amount ELSE 0 END) AS apr,
FROM fee_type ft INNER JOIN fee_plan fp
USING (fee_id)
OUTPUT
fee_name APR MAY JUNE JULY AUG
Admission Fee 5000 0 0 0 0
Tuition Fee 1150 1150 1150 1150 1150
First I've to note that to design a schema you should understand the basics of relational model. When you put your spreadsheet layout to a relational table you won't get it right.
So I redesigned you schema in a relational matter. It's not the only possible schema, though it depends on rest of your application.
Schema
CREATE TABLE `fee` (
`fee_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`name` VARCHAR(32) NOT NULL,
PRIMARY KEY (`fee_id`)
) ENGINE = InnoDB;
CREATE TABLE `fee_writeoff` (
`fee_writeoff_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`fee_id` INT UNSIGNED NULL,
`date` DATE NOT NULL,
PRIMARY KEY (`fee_writeoff_id`),
INDEX `fee_id`(`fee_id`),
CONSTRAINT `fee_writeoff_has_fee`
FOREIGN KEY (`fee_id`)
REFERENCES `fee` (`fee_id`)
ON DELETE RESTRICT
ON UPDATE CASCADE
) ENGINE = InnoDB;
CREATE TABLE `fee_plan` (
`fee_plan_id` INT UNSIGNED NOT NULL AUTO_INCREMENT,
`fee_id` INT UNSIGNED NOT NULL,
`amount` DECIMAL(10,0) NOT NULL,
PRIMARY KEY (`fee_plan_id`),
INDEX `fee_id`(`fee_id`),
CONSTRAINT `fee_plan_has_fee`
FOREIGN KEY (`fee_id`)
REFERENCES `fee` (`fee_id`)
ON DELETE RESTRICT
ON UPDATE CASCADE
) ENGINE = InnoDB;
Data
INSERT INTO `fee`(`fee_id`, `name`) VALUES
(1, 'Admission Fee'),
(2, 'Tuition Fee');
INSERT INTO `fee_writeoff`(`fee_id`, `date`) VALUES
(1, '2000-04-01'),
(2, '2000-01-01'),
(2, '2000-02-01'),
(2, '2000-03-01'),
(2, '2000-04-01'),
(2, '2000-05-01'),
(2, '2000-06-01'),
(2, '2000-07-01'),
(2, '2000-08-01'),
(2, '2000-09-01'),
(2, '2000-10-01'),
(2, '2000-11-01'),
(2, '2000-12-01');
INSERT INTO `fee_plan`(`fee_id`, `amount`) VALUES
(1, 5000),
(2, 1150);
Query
SELECT
name,
SUM(CASE MONTH(`date`) WHEN 4 THEN amount ELSE 0 END) AS `April`,
SUM(CASE MONTH(`date`) WHEN 5 THEN amount ELSE 0 END) AS `May`,
SUM(CASE MONTH(`date`) WHEN 6 THEN amount ELSE 0 END) AS `June`,
SUM(CASE MONTH(`date`) WHEN 7 THEN amount ELSE 0 END) AS `July`,
SUM(CASE MONTH(`date`) WHEN 8 THEN amount ELSE 0 END) AS `August`,
SUM(CASE MONTH(`date`) WHEN 9 THEN amount ELSE 0 END) AS `September`,
SUM(CASE MONTH(`date`) WHEN 10 THEN amount ELSE 0 END) AS `October`,
SUM(CASE MONTH(`date`) WHEN 11 THEN amount ELSE 0 END) AS `November`,
SUM(CASE MONTH(`date`) WHEN 12 THEN amount ELSE 0 END) AS `December`,
SUM(CASE MONTH(`date`) WHEN 1 THEN amount ELSE 0 END) AS `January`,
SUM(CASE MONTH(`date`) WHEN 2 THEN amount ELSE 0 END) AS `February`,
SUM(CASE MONTH(`date`) WHEN 3 THEN amount ELSE 0 END) AS `March`,
SUM(amount) AS `Total`
FROM fee
JOIN fee_writeoff USING(fee_id)
JOIN fee_plan USING(fee_id)
GROUP BY name WITH ROLLUP
Here is the SQLFiddle snippet.

Mysql order by with grouping

Hello everyone today i got in to a problem..
first thing i have a two table each table i have "product_seq_id" column and i joined table using the same "product_seq_id"
in the second table there are multiple rows for "product_seq_id" i want only one with below condition
table2.date_start not be null
table2.date_start is equal to '0000-00-00' or table2.date_start <= CURDATE()
table2.date_end is equal to '0000-00-00' or table2.date_start >= CURDATE()
get highest table2.priority if 2 or more rows match on the same day
I have already did some work.. but the problem is in that it's not taking highest priority number while ordering the column with grouped
//My Query
SELECT
psp . *, pcp . *
FROM
sk_product_category_path pcp
left join
sk_product_special_price psp ON (psp.product_seq_id = pcp.product_seq_id)
where
pcp.category_seq_id = 146
AND psp.product_seq_id IS NOT NULL
AND CASE
WHEN
psp.date_start IS NOT NULL
THEN
(psp.date_start = '0000-00-00'
OR psp.date_start <= CURDATE())
AND (psp.date_end = '0000-00-00'
OR psp.date_end >= CURDATE())
ELSE 1 = 1
END
group by psp.product_seq_id
order by psp.priority desc
Result Came for above code:
# product_special_price_seq_id, product_special_price, date_start, date_end, priority, product_seq_id, product_category_path_seq_id, product_seq_id, category_seq_id
2309 123123 0000-00-00 0000-00-00 0 3196 1 3196 146
2307 12313 0000-00-00 0000-00-00 0 3197 3 3197 146
Result I wanted:
# product_special_price_seq_id, product_special_price, date_start, date_end, priority, product_seq_id, product_category_path_seq_id, product_seq_id, category_seq_id
2309 12200 0000-00-00 0000-00-00 1 3196 2 3196 146
2307 12313 0000-00-00 0000-00-00 0 3197 3 3197 146
// Table Data
CREATE TABLE IF NOT EXISTS `sk_product_category_path` (
`product_category_path_seq_id` int(11) NOT NULL AUTO_INCREMENT,
`product_seq_id` int(11) NOT NULL,
`category_seq_id` int(11) NOT NULL,
PRIMARY KEY (`product_category_path_seq_id`),
UNIQUE KEY `product_seq_id` (`product_seq_id`,`category_seq_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
INSERT INTO `sk_product_category_path` (`product_category_path_seq_id`, `product_seq_id`, `category_seq_id`) VALUES
(1, 3196, 146),
(2, 3197, 146),
(3, 3198, 146);
CREATE TABLE IF NOT EXISTS `sk_product_special_price` (
`product_special_price_seq_id` int(11) NOT NULL AUTO_INCREMENT,
`product_special_price` bigint(20) DEFAULT NULL,
`date_start` date DEFAULT NULL,
`date_end` date DEFAULT NULL,
`priority` int(11) DEFAULT NULL,
`product_seq_id` int(11) NOT NULL,
PRIMARY KEY (`product_special_price_seq_id`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 AUTO_INCREMENT=4 ;
INSERT INTO `sk_product_special_price` (`product_special_price_seq_id`, `product_special_price`, `date_start`, `date_end`, `priority`, `product_seq_id`) VALUES
(1, 12313, '0000-00-00', '0000-00-00', 0, 3197),
(2, 12200, '2014-02-11', '2014-02-11', 1, 3197),
(3, 123123, '0000-00-00', '0000-00-00', 0, 3196);
During GROUP BY in MySQL, it picks first matching row for each group unless you are using an aggregate function. The first matching need not be always row with min(id) .
The possible query should be something like :
SELECT t.*
from table_name t
inner join (
select min(id) as id
from table_name t
group by col) as s
on s.id = t.id
Please find the below query.. let me know is this is your requirement?
SELECT *
FROM sk_product_special_price pspo
WHERE pspo.priority IN(SELECT MAX(psp.priority)
FROM sk_product_special_price psp
JOIN sk_product_category_path pcp
ON(pcp.product_seq_id=psp.product_seq_id)
WHERE psp.date_start IS NOT NULL
AND psp.date_start BETWEEN '0000-00-00' AND CURDATE()
AND (psp.date_end>=CURDATE() OR psp.date_end='0000-00-00')
AND pcp.product_seq_id=pspo.product_seq_id);
I have updated the end date "'2014-02-11" to "2014-02-12" for my code to fetch end date >=today's date.
this query will return the table2 details i.e table sk_product_special_price for each all the product based on the priyority values.
the output will be
product_special_price_seq_id, product_special_price, date_start, date_end, priority, product_seq_id
2, 12200, '2014-02-11', '2014-02-12', 1, 3197
3, 123123, '0000-00-00', '0000-00-00', 0, 3196

MYSQL query - getting totals by month

http://sqlfiddle.com/#!2/6a6b1
The scheme is given above.. all I want to do is get the results as the total of sales/month... the user will enter a start date and end date and I can generate (in PHP) all the month and years for those dates. For example, if I want to know the total number of "sales" for 12 months, I know I can run 12 individual queries with start and end dates, but I want to run only one query where the result will look like:
Month numofsale
January - 2
Feb-1
March - 23
Apr - 10
and so on...
or just a list of sales without the months, I can then pair it to the array of months generated in the PHP ...any ideas...
Edit/schema and data pasted from sqlfiddle.com:
CREATE TABLE IF NOT EXISTS `lead_activity2` (
`lead_activity_id` int(11) NOT NULL AUTO_INCREMENT,
`sp_id` int(11) NOT NULL,
`act_date` datetime NOT NULL,
`act_name` varchar(255) NOT NULL,
PRIMARY KEY (`lead_activity_id`),
KEY `act_date` (`act_date`),
KEY `act_name` (`act_name`)
) ENGINE=InnoDB DEFAULT CHARSET=latin1 ;
INSERT INTO `lead_activity2` (`lead_activity_id`, `sp_id`, `act_date`, `act_name`) VALUES
(1, 5, '2012-10-16 16:05:29', 'sale'),
(2, 5, '2012-10-16 16:05:29', 'search'),
(3, 5, '2012-10-16 16:05:29', 'sale'),
(4, 5, '2012-10-17 16:05:29', 'DNC'),
(5, 5, '2012-10-17 16:05:29', 'sale'),
(6, 5, '2012-09-16 16:05:30', 'SCB'),
(7, 5, '2012-09-16 16:05:30', 'sale'),
(8, 5, '2012-08-16 16:05:30', 'sale'),
(9, 5,'2012-08-16 16:05:30', 'sale'),
(10, 5, '2012-07-16 16:05:30', 'sale');
SELECT DATE_FORMAT(date, "%m-%Y") AS Month, SUM(numofsale)
FROM <table_name>
WHERE <where-cond>
GROUP BY DATE_FORMAT(date, "%m-%Y")
Check following in your fiddle demo it works for me (remove where clause for testing)
SELECT DATE_FORMAT(act_date, "%m-%Y") AS Month, COUNT(*)
FROM lead_activity2
WHERE <where-cond-here> AND act_name='sale'
GROUP BY DATE_FORMAT(act_date, "%m-%Y")
It returns following result
MONTH COUNT(*)
07-2012 1
08-2012 2
09-2012 1
10-2012 3
You can try query as given below
select SUM(`SP_ID`) AS `Total` , DATE_FORMAT(act_date, "%M") AS Month, Month(`ACT_DATE`) AS `Month_number` from `lead_activity2` WHERE `ACT_DATE` BETWEEN '2012-05-01' AND '2012-12-17' group by Month(`ACT_DATE`)
Here 2012-05-01 and 2012-12-17 are date input from form. and It will be return you the sum of sales for particular month if exist in database.
thanks
Try this query -
SELECT
MONTH(act_date) month, COUNT(*)
FROM
lead_activity2
WHERE
YEAR(act_date) = 2012 AND act_name = 'sale'
GROUP BY
month
Check WHERE condition if it is OK for you - act_name = 'sale'.
If you want to output month names, then use MONTHNAME() function instead of MONTH().
SELECT YEAR(act_date), MONTH(act_date), COUNT(*)
FROM lead_activity2
GROUP BY YEAR(act_date), MONTH(act_date)
For getting data by month or any other data based on column you have to add GROUP BY.
You can add many columns or calculated values to GROUP BY.
I assume that "num of sales" means count of rows.
Sometimes you might want the month names as Jan, Feb, Mar .... Dec possibly for a Chart likeFusionChart
SELECT DATE_FORMAT(date, "%M") AS Month, SUM(numofsale)
FROM <Table_name>
GROUP BY DATE_FORMAT(date, "%M")
Results would look like this on table
MONTH COUNT(*)
Jul 1
Aug 2
SEP 1
OCT 3