MYSQL Cumulative Totals with SUM and Subquery - mysql

I have the below table:
CREATE TABLE `_loans` (
`loan_id` int(11) NOT NULL,
`price` float(7,2) NOT NULL,
`term` int(11) NOT NULL,
`app_date` date NOT NULL
) ENGINE=InnoDB DEFAULT CHARSET=latin1;
INSERT INTO `_loans` (`loan_id`, `price`, `term`, `app_date`) VALUES
(1, 299.00, 24, '2019-01-23'),
(2, 774.00, 24, '2019-01-24'),
(3, 817.80, 24, '2019-01-24'),
(4, 279.99, 24, '2019-01-28'),
(5, 463.99, 24, '2019-01-28'),
(6, 0.00, 24, '2019-01-28'),
(7, 357.00, 24, '2019-02-02'),
(8, 386.98, 24, '2019-02-04'),
(9, 846.00, 24, '2019-02-04'),
(10, 713.99, 24, '2019-02-06'),
(11, 0.00, 24, '2019-02-07'),
(12, 579.00, 24, '2019-02-11'),
(13, 179.00, 24, '2019-02-13'),
(14, 0.00, 24, '2019-02-19'),
(15, 259.00, 24, '2019-02-21'),
(16, 249.99, 24, '2019-02-26'),
(17, 319.00, 24, '2019-03-02'),
(18, 1108.99, 24, '2019-03-05'),
(19, 319.00, 24, '2019-03-05'),
(20, 199.97, 24, '2019-03-06');
ALTER TABLE `_loans`
ADD PRIMARY KEY (`loan_id`),
ADD KEY `app_date` (`app_date`);
And the below query, which gives a monthly summary of the table data:
SELECT
MONTHNAME(w.app_date) month,
YEAR(w.app_date) year,
COUNT(*) contracts,
SUM(w.total_price) totals,
#running_total := #running_total + SUM(w.total_price) running_totals
FROM (
SELECT
app_date,
SUM(price * term) total_price
FROM _loans l
GROUP BY l.loan_id
) w
JOIN (SELECT #running_total := 0) r
GROUP BY YEAR(w.app_date), MONTH(w.app_date)
ORDER BY YEAR(w.app_date), MONTH(w.app_date) ASC
I need a running total of the values from the 'totals' column. Everything works well except the cumulative total, which is not accumulating.
month | year | contracts | totals | running_totals
------------------------------------------------------------------
January | 2019 | 6 | 63234.72 | 63234.71923828125
February | 2019 | 10 | 85703.04 | 85703.04016113281
March | 2019 | 4 | 46727.04 | 46727.039794921875

This isn't working because of the GROUPing. You need to nest the GROUPing as a subquery and compute the running total from that:
SELECT monthname as month, year, contracts, totals,
#running_total := #running_total + totals AS running_totals
FROM (SELECT
MONTHNAME(w.app_date) monthname,
MONTH(w.app_date) monthnum,
YEAR(w.app_date) year,
COUNT(*) contracts,
SUM(w.total_price) totals
FROM (SELECT
app_date,
SUM(price * term) total_price
FROM _loans l
GROUP BY l.loan_id
) w
GROUP BY year, monthnum, monthname
) t
JOIN (SELECT #running_total := 0) r
ORDER BY year, monthnum ASC
Output:
month year contracts totals running_totals
January 2019 6 63234.72 63234.71923828125
February 2019 10 85703.04 148937.75939941406
March 2019 4 46727.04 195664.79919433594
Demo on dbfiddle

Your query is overly complicated. The subquery is not necessary for the aggregation. However, it is necessary for the use of variables:
SELECT month, year, total_price,
(#running_total := #running_total + total_price) as running_totals
FROM (SELECT MONTHNAME(l.app_date) as month,
YEAR(l.app_date) as year,
MONTH(l.app_date) as mon,
COUNT(DISTINCT l.loan_id) as contracts,
SUM(l.price * l.term) total_price
FROM _loans l
GROUP BY l.loan_id, YEAR(w.app_date), MONTH(w.app_date)
ORDER BY l.loan_id, YEAR(w.app_date), MONTH(w.app_date)
) l CROSS JOIN
(SELECT #running_total := 0) params
ORDER BY year, mon ASC

Related

How do you make a nested select with a restriction (WHERE)

I have a database for a small calendar app in which people are stored in the clients table, dates are stored in the table calendarDate and since the relations are many to many there is a connecting table called client_date which holds both of their ids.
I want to make a nested select to get all the dates for a particular person lets say with id = 2.
I came up with this, but it prints all of the dates and asigns them to the person with that id, instead of just printing the only ones he is asigned to:
SELECT c.username
, c.country
, d.day
, d.month
, d.year
, d.dayOfWeek
, d.weekOfYear
, d.emotionId
, d.id
from clients as c
join calendarDate as d
on d.id in (SELECT dateId
from client_date
WHERE clientId in (SELECT id
from clients )
)
where c.id = 2;
Is there something I am doing wrong or is there another way to make a nested select statement ?
My database and data:
DROP DATABASE IF EXISTS calendar;
CREATE DATABASE calendar;
USE calendar;
CREATE TABLE clients(
id INT AUTO_INCREMENT PRIMARY KEY,
username VARCHAR(100) NOT NULL UNIQUE,
joinedOnDate DATE NOT NULL,
country VARCHAR(100) NOT NULL
);
CREATE TABLE emotions(
id INT NOT NULL PRIMARY KEY,
name VARCHAR(100) NOT NULL UNIQUE,
value DOUBLE
);
CREATE TABLE calendarDate(
id INT AUTO_INCREMENT PRIMARY KEY,
day INT NOT NULL,
month INT NOT NULL,
year INT NOT NULL,
dayOfWeek VARCHAR(20) NOT NULL,
weekOfYear int NOT NULL,
emotionId INT NOT NULL,
CONSTRAINT FOREIGN KEY (emotionId)
REFERENCES emotions( id )
);
CREATE TABLE client_date(
dateId INT NOT NULL,
clientId INT NOT NULL,
CONSTRAINT FOREIGN KEY ( dateId )
REFERENCES calendarDate( id ) ,
CONSTRAINT FOREIGN KEY ( clientId )
REFERENCES clients( id ) ,
UNIQUE KEY( dateId, clientId )
);
USE calendar;
INSERT INTO emotions (id, name, value) VALUES
(0, 'None', 1),
(1, 'Excited', 2.0),
(2, 'Happy', 2.0),
(3, 'Positive', 1.5),
(4, 'Average', 1.0),
(5, 'Mixed', 1),
(6, 'Negative', 0.5),
(7, 'Sad', 0);
INSERT INTO clients (username, joinedOnDate, country) VALUES
('Malazzar', DATE(NOW()), 'Bulgaria'),
('Preslava981', DATE(NOW()), 'Bulgaria'),
('Thusnake', DATE(NOW()), 'United Kingdom');
INSERT INTO calendarDate (day, month, year, dayOfWeek, weekOfYear, emotionId) VALUES
(1, 1, 2019, 'Tuesday', 1, 0),
(2, 1, 2019, 'Wednesday', 1, 0),
(3, 1, 2019, 'Thursday', 1, 0),
(4, 1, 2019, 'Friday', 1, 0),
(5, 1, 2019, 'Saturday', 1, 0),
(6, 1, 2019, 'Sunday', 1, 0),
(7, 1, 2019, 'Monday', 2, 0),
(8, 1, 2019, 'Tuesday', 2, 0),
(9, 1, 2019, 'Wednesday', 2, 0),
(10, 1, 2019, 'Thursday', 2, 0),
(11, 1, 2019, 'Friday', 2, 0),
(12, 1, 2019, 'Saturday', 2, 0),
(13, 1, 2019, 'Sunday', 2, 0),
(14, 1, 2019, 'Monday', 3, 0);
INSERT INTO client_date (clientId, dateId) VALUES
(1, 1),
(1, 2),
(1, 3),
(1, 4),
(1, 5),
(1, 6),
(1, 7),
(2, 8),
(2, 9),
(2, 10),
(2, 11),
(2, 12),
(2, 13),
(2, 14);
The output I get for the particular client:
Preslava981 Bulgaria 1 1 2019 Tuesday 1 0 1
Preslava981 Bulgaria 2 1 2019 Wednesday 1 0 2
Preslava981 Bulgaria 3 1 2019 Thursday 1 0 3
Preslava981 Bulgaria 4 1 2019 Friday 1 0 4
Preslava981 Bulgaria 5 1 2019 Saturday 1 0 5
Preslava981 Bulgaria 6 1 2019 Sunday 1 0 6
Preslava981 Bulgaria 7 1 2019 Monday 2 0 7
Preslava981 Bulgaria 8 1 2019 Tuesday 2 0 8
Preslava981 Bulgaria 9 1 2019 Wednesday 2 0 9
Preslava981 Bulgaria 10 1 2019 Thursday 2 0 10
Preslava981 Bulgaria 11 1 2019 Friday 2 0 11
Preslava981 Bulgaria 12 1 2019 Saturday 2 0 12
Preslava981 Bulgaria 13 1 2019 Sunday 2 0 13
Preslava981 Bulgaria 14 1 2019 Monday 3 0 14
Your mistake is here:
WHERE clientId in (SELECT id from clients)
Every client ID exists in the clients table. You want this instead:
WHERE clientId = c.id
You could also use a non-corelated subquery instead, which is easier to read:
on (c.id, d.id) in (select clientid, dateid from client_date)
But as has been mentioned by others, a mere join to client_date would do the same job.
Why not just use joins?
select c.username, c.country, d.*
from clients c join
client_date cd
on cd.clientId = c.id join
calendarDate as d
on d.id cd.dateId
where c.id = 2;
I want to make a nested select to get all the dates for a particular
person lets say with id = 2
This can be done with:
select *
from calendarDate
where day in (
select dateId
from client_date
where clientId = 2
)
An equivalent JOIN query would be:
select d.*
from calendarDate d
join client_date cd
on cd.dateId = d.day
where cd.clientId = 2
View on DB Fiddle
Note: The two queries are only equivalent, if there are no duplicates in the client_date table.
If you want to select data from more than one table, then you actualy need to use a JOIN. In that case asking for how to do it without joins, wouldn't make sense.

Top sales making product for each year

I have two tables with following schema.
CREATE TABLE Product (
id INT AUTO_INCREMENT,
name VARCHAR(64) UNIQUE,
unit_price VARCHAR(6),
PRIMARY KEY (id)
);
CREATE TABLE Sale (
id INT AUTO_INCREMENT,
product_id INT, created_at DATETIME,
units INT, PRIMARY KEY (id),
FOREIGN KEY (product_id) REFERENCES Product(id)
);
INSERT INTO Product(name, unit_price)
VALUES
('Methadone', 30),
('Codeine', 20),
('Morphine', 40),
('Fentanyl', 10);
INSERT INTO Sale(product_id, created_at, units)
VALUES
(1, '2004-01-06 08:27:25', 8),
(4, '2004-03-05 05:21:15', 24),
(2, '2005-01-06 08:26:55', 3),
(4, '2005-03-31 01:55:25', 6),
(1, '2005-11-09 11:33:20', 2),
(4, '2006-02-15 10:46:30', 4),
(3, '2006-04-14 07:40:50', 1),
(4, '2008-01-06 08:27:25', 5),
(4, '2008-01-10 21:31:45', 3),
(1, '2008-01-28 16:07:30', 9),
(3, '2008-03-05 05:21:00', 4),
(2, '2008-03-08 18:25:50', 5),
(4, '2008-05-02 02:15:20', 1),
(4, '2008-05-05 15:19:40', 3),
(4, '2008-06-28 23:08:55', 2),
(4, '2008-07-02 12:14:00', 2),
(3, '2008-08-29 09:07:50', 1),
(2, '2008-10-22 16:56:20', 5),
(3, '2008-10-26 06:01:25', 2),
(4, '2008-12-19 13:49:55', 4),
(2, '2008-12-23 02:55:30', 4),
(1, '2009-02-15 10:43:45', 4),
(2, '2009-04-14 07:37:50', 6),
(3, '2009-06-11 04:31:25', 3),
(4, '2009-08-08 01:25:30', 12),
(1, '2010-11-09 11:33:20', 8),
(1, '2011-01-14 10:22:35', 3),
(3, '2011-03-13 07:16:10', 3),
(4, '2011-05-10 04:10:15', 4),
(4, '2011-07-07 01:04:35', 4),
(4, '2011-09-02 21:58:10', 3),
(2, '2011-10-30 18:51:45', 3),
(1, '2011-12-27 15:45:20', 1),
(4, '2012-02-23 12:43:25', 2),
(4, '2012-04-21 09:37:30', 3),
(4, '2012-06-18 06:31:20', 1),
(2, '2012-08-15 03:25:40', 4),
(2, '2013-01-14 10:23:20', 4),
(4, '2013-02-01 05:01:35', 1),
(4, '2013-03-13 07:16:55', 1),
(4, '2013-03-31 01:55:10', 2),
(4, '2013-05-10 04:11:15', 2),
(3, '2013-05-27 22:48:45', 3),
(1, '2013-07-07 01:05:20', 3),
(2, '2013-07-24 19:42:20', 4),
(4, '2013-09-02 21:59:40', 4),
(3, '2013-09-20 16:36:10', 1),
(4, '2013-10-30 18:54:00', 2),
(4, '2013-11-17 13:29:45', 1),
(4, '2013-12-27 15:48:20', 3);
I want to find top sales making product for each year sorted by year in descending order along-with the total sale made for the top selling product.
I have written the following query.
SELECT EXTRACT(YEAR FROM S.created_at) as year,
GROUP_CONCAT(DISTINCT P1.name
ORDER BY P1.name
SEPARATOR ',')as top,
MAX(S.units*P1.unit_price) as sale
FROM Sale S
INNER JOIN Product P1 ON S.product_id=P1.id
GROUP BY year
ORDER BY year desc
Expected Output:
2013 Codeine,Fentanyl,Morphine 160
2012 Codeine 80
2011 Methadone,Morphine 120
2010 Methadone 240
2009 Codeine,Fentanyl,Methadone,Morphine 120
2008 Codeine,Morphine 280
2006 Fentanyl,Morphine 40
2005 Codeine,Fentanyl,Methadone 60
2004 Fentanyl,Methadone 240
Original Output:
2013 Codeine,Fentanyl,Methadone,Morphine 120
2012 Codeine,Fentanyl 80
2011 Codeine,Fentanyl,Methadone,Morphine 120
2010 Methadone 240
2009 Codeine,Fentanyl,Methadone,Morphine 120
2008 Codeine,Fentanyl,Methadone,Morphine 270
2006 Fentanyl,Morphine 40
2005 Codeine,Fentanyl,Methadone 60
2004 Fentanyl,Methadone 240
The query was supposed to return only the top selling products, bu it's returning all the products. Could anyone provide any help?
Here is a solution for MySQL < 8.0. First, aggregate sales by year and product and filter on the top selling products with an aggregate, correlated subquery; then, aggregate by year:
select
year_at,
group_concat(name order by name) products,
total_sales
from (
select
year(s.created_at) year_at,
p.name,
sum(s.units * p.unit_price) total_sales
from Product p
inner join Sale s on s.product_id = p.id
group by year_at, p.id, p.name
having total_sales = (
select sum(s1.units * p1.unit_price) total_sales1
from Product p1
inner join Sale s1 on s1.product_id = p1.id
where year(s1.created_at) = year_at
group by s1.product_id
order by total_sales1 desc
limit 1
)
) t
group by year_at, total_sales
order by year_at desc
Demo on DB Fiddlde:
year_at | products | total_sales
------: | :---------------------------------- | ----------:
2013 | Codeine,Fentanyl,Morphine | 160
2012 | Codeine | 80
2011 | Methadone,Morphine | 120
2010 | Methadone | 240
2009 | Codeine,Fentanyl,Methadone,Morphine | 120
2008 | Codeine,Morphine | 280
2006 | Fentanyl,Morphine | 40
2005 | Codeine,Fentanyl,Methadone | 60
2004 | Fentanyl,Methadone | 240
You seem to be after this...
SELECT GROUP_CONCAT(a.name) names,a.total, a.year
FROM
( SELECT p.name
, SUM(p.unit_price*s.units) total
, YEAR(created_at) year
FROM product p
JOIN sale s
ON s.product_id = p.id
GROUP
BY year
, name
) a
JOIN
( SELECT MAX(total) total
, year
FROM
( SELECT p.name
, SUM(p.unit_price*s.units) total
, YEAR(created_at) year
FROM product p
JOIN sale s
ON s.product_id = p.id
GROUP
BY year
, name
) n
GROUP
BY year) b
ON b.year = a.year AND b.total = a.total
GROUP BY a.total, a.year
ORDER BY year DESC;
+-------------------------------------+-------+------+
| names | total | year |
+-------------------------------------+-------+------+
| Fentanyl,Codeine,Morphine | 160 | 2013 |
| Codeine | 80 | 2012 |
| Morphine,Methadone | 120 | 2011 |
| Methadone | 240 | 2010 |
| Morphine,Methadone,Fentanyl,Codeine | 120 | 2009 |
| Morphine,Codeine | 280 | 2008 |
| Morphine,Fentanyl | 40 | 2006 |
| Codeine,Methadone,Fentanyl | 60 | 2005 |
| Methadone,Fentanyl | 240 | 2004 |
+-------------------------------------+-------+------+
Here's a variant of my Scalar-Aggregate Comparison query technique that will achieve what you want in a vastly better-performing query than self-joining the aggregate results. As you can see below, this only ever performs a single join, then 3 cascaded layers of aggregation to achieve the final output.
SELECT
yr,
SUBSTRING(MAX(CONCAT(LPAD(total_sales, 16, '0'), products)), 17) AS best_seller,
MAX(total_sales) AS sales_amount
FROM (
SELECT
yr,
total_sales,
GROUP_CONCAT(name order by name) AS products
FROM (
SELECT
YEAR(s.created_at) AS yr,
p.id,
p.name,
SUM(s.units * p.unit_price) AS total_sales
FROM Product AS p
INNER JOIN Sale AS s on s.product_id = p.id
GROUP BY YEAR(s.created_at), p.id
) AS pys
GROUP BY yr, total_sales
) AS py
GROUP BY yr
ORDER BY yr DESC;
Here's a DB Fiddle demo: https://dbfiddle.uk/?rdbms=mysql_5.6&fiddle=e14ed7f396738ce064f1998bdb4310ac
try this:
Select name, year(created_at), sum(unit_price*units )
from Product p inner join sale s on p.id = s.product_id
group by name, year(created_at)
order by 2, 3 desc
If you want the top selling product for each year, use rank() and an aggregation query:
select ps.*
from (select p.name, year(s.created_at) as year, sum(p.unit_price*s.units) as sales
rank() over (partition by year(s.created_at) order by sum(p.unit_price*s.units) desc as seqnum
from Product p inner join
sale s
on p.id = s.product_id
group by name, year
) ps
where seqnum <= 1; -- You can change "1" for more rows

Cumulative sum with max date group by month year and id

Now i need to make similar query but need to several criteria
Here is my table
`transaksi` (`transid`, `idpinj`, `tanggal`,`sisapokok`, `sisajasa`
(1, 1, '2018-01-01', 1000, 100, 1),
(2, 1, '2018-01-05', 1000, 100, 3),
(3, 2, '2018-02-04', 1000, 100, 4),
(4, 2, '2018-02-08', 1000, 100, 5),
(5, 1, '2018-02-19', 1000, 100, 3),
(6, 3, '2018-02-22', 1000, 100, 2),
(7, 2, '2018-03-09', 1000, 100, 3),
(8, 3, '2018-03-10', 1000, 100, 3)
(9, 3, '2018-03-12', 1000, 100, 4)
(10, 1, '2018-03-17', 1000, 100, 4)
(11, 4, '2018-03-19', 1000, 100, 3)
(12, 2, '2018-03-20', 1000, 100, 4)
DB Fiddle table
From the table above i need to get output as follow
Month sisapokok sisajasa
Jan-2018 1000 100 ->row2
Feb-2018 4000 400 ->+ row3+5
Mar-2018 12000 1200 ->+ row9+10+11+12
First I need to get sum(sisapokok) and sum(sisajasa) for each idpinj where date is max(tanggal), status between 3 and 4. This value then sum as total per month
Make cumulative sum each month for the last 12 month
I try this query but it get the max(date) from all records not max(date) by month and each idpinj.
SELECT a.idpinj,a.sisapokok
FROM transaksi a
INNER JOIN
(
SELECT idpinj, MAX(tanggal) tgl
FROM transaksi
GROUP BY idpinj
) b ON a.idpinj = b.idpinj
AND a.tanggal = b.tgl
ORDER BY `a`.`idpinj` ASC
Not sure exactly what you are asking for but see if this helps:
select monthyear, sum(sisapokok)sisapokok, sum(sisajasa)sisajasa from (
select cast(month(tanggal) as varchar)+'-'+cast(year(tanggal) as varchar) monthyear, sum(sisapokok)sisapokok, sum(sisajasa)sisajasa
from #transaksi
group by cast(month(idpinj) as varchar)+'-'+cast(year(tanggal) as varchar) , tanggal) a
group by monthyear
Based on the fiddle data
select yyyy,mm,
#s:=#s+sisapokok sisapokok,
#t:=#t+sisajasa sisajasa
from
(
select yyyy,mm,sum(sisapokok) sisapokok,sum(sisajasa) sisajasa
from
(
select year(tanggal) yyyy,month(tanggal) mm, sisapokok,sisajasa
from transaksi t
join
(
select year(tanggal) yyyy,month(tanggal) mm,idpinj,max(transid) maxid
from `transaksi`
where status in(3,4)
group by year(tanggal),month(tanggal),idpinj
) s on s.maxid = transid
) t
group by yyyy,mm
) u
,(select #s:=0,#t:=0) r
order by yyyy,mm
+------+------+-----------+----------+
| yyyy | mm | sisapokok | sisajasa |
+------+------+-----------+----------+
| 2018 | 1 | 2000 | 2003 |
| 2018 | 2 | 5000 | 2303 |
| 2018 | 3 | 13000 | 3103 |
+------+------+-----------+----------+
3 rows in set (0.00 sec)
Note the inner query finds the last relevant id and the code progresses outward to use variables to calculate running totals.

Group and rank top N rows by category

I have a table with the columns category and votes. I've tried multiple solutions before with very little success; usually what would happen is that instead of returning the top 3 items in each category, it returns all of the items available.
SELECT `id`, `user_id`, `full_name`, `category`, `year`, `month`, `thumbnail_photo`, `photo_title`, `votes`
FROM
(
SELECT `id`, `user_id`, `full_name`, `category`, `year`, `month`, `thumbnail_photo`, `photo_title`, `votes`,
#category_rank := IF(#current_category = category, #category_rank + 1, 1) AS category_rank,
#current_category := category
FROM `photo_contest`
ORDER BY
`category`,
`votes` DESC
) ranked
WHERE
category_rank <= 3
AND `year` = '2017'
AND `month` = 'April'
AND `votes` > 0
This particular solution was adapted from SQLines. What I ultimately want to do is to turn a table like this:
Name | Category | Votes
--------- | -------- | -----
Name Foo | CatFoo | 0
Name Bar | CatFoo | 1
Name Baz | CatFoo | 10
Name Quux | CatFoo | 200
Name ooF | CatBar | 50
Name raB | CatBar | 300
Name zaB | CatBar | 10
Name xuuQ | CatBar | 200
...to:
Name | Category | Votes
--------- | -------- | -----
Name Quux | CatFoo | 200
Name Baz | CatFoo | 10
Name Bar | CatFoo | 1
Name raB | CatBar | 300
Name xuuQ | CatBar | 200
Name ooF | CatBar | 50
...with the other WHERE statements included. Year, month, and minimum votes.
Your subquery tries to calculate ranking over the entire table. If you only want to rank for the selected year-month with votes > 0, you should copy those conditions into the subquery as its own WHERE conditions.
UPDATE:
Looks like it's the missing ORDER BY in the outer-query that causes the said problem. I've created the following DDL/SQL at sqlfiddle.
CREATE TABLE IF NOT EXISTS `votes` (
`id` INT NOT NULL,
`category` VARCHAR(10) NULL,
`year` VARCHAR(4) NULL,
`month` VARCHAR(2) NULL,
`votes` INT
)
ENGINE = InnoDB;
INSERT INTO `votes` VALUES
(10, 'cat1', '2016', '05', 300),
(10, 'cat1', '2016', '06', 200),
(10, 'cat2', '2016', '05', 500),
(11, 'cat1', '2016', '05', 200),
(11, 'cat2', '2016', '05', 0),
(11, 'cat2', '2016', '06', 100),
(12, 'cat1', '2016', '05', 400),
(12, 'cat2', '2016', '05', 150),
(13, 'cat1', '2016', '05', 350),
(13, 'cat2', '2016', '05', 100),
(13, 'cat2', '2016', '06', 150),
(14, 'cat1', '2016', '05', 0),
(14, 'cat2', '2016', '05', 450);
SELECT `id`, `category`, `year`, `month`, `votes`
FROM (
SELECT `id`, `category`, `year`, `month`, `votes`,
#category_rank := IF(#current_category = category, #category_rank + 1, 1) AS category_rank,
#current_category := category
FROM `votes`
WHERE
`year` = '2016'
AND `month` = '05'
AND `votes` > 0
ORDER BY
`category`,
`votes` DESC
) ranked
WHERE
category_rank <= 3
ORDER BY
`category`,
`votes` DESC;
I'm not an expert with MySQL, so I propose you a different (standard SQL) approach: you can join the table with itself on Category and on Votes being less or equal to the votes of the current row.
select t1.Name, t1.Category, t1.Votes, count(distinct t2.Name) as rank
from photo_contest t1
join photo_contest t2
on t1.Category = t2.Category and
t1.Votes <= t2.Votes
/*where whatever you want*/
group by t1.Name, t1.Category, t1.Votes
having count(distinct t2.Name) <= 3
order by t1.Category, rank
I tested it here and it seems to do what you asked for
It sounds like your PHPMyAdmin needs an upgrade or a replacement. Meanwhile you might want to try #Stefano Zanini's non-MySQL specific SQL:
SELECT
t1.`id`, t1.`category`, t1.`year`, t1.`month`,
t1.`votes`, count(distinct t2.`id`) as rank
FROM photo_contest t1
INNER JOIN photo_contest t2 ON
t1.`category` = t2.`category` AND
t1.`votes` <= t2.`votes`
WHERE t1.`votes` > 0
GROUP BY t1.`id`, t1.`category`, t1.`votes`
HAVING count(distinct t2.`id`) <= 3
ORDER BY t1.`category`, rank;
It's available on sqlfiddle. If you think this solution suits you better please credit #Stefano Zanini's answer instead of this one.

Select and show business open hours from MySQL

I dont need to check if business is open or close, but I need to show open hours by days.
There are some options:
1 - Business open once in day (sample - from 10:00 to 18:30) - one
rows in table
2 - Business open TWICE in day (samlpe - from 10:00 to
14:00 and from 15:00 to 18:30) - two rows in table
3 - Business may
be closed (no row inserted)
Here my MySql table of hours storing. In this sample business (affiliate_id) are open twice in days from 0 to 4, once in day 5 and closed in day 6 (no records for this day)
http://postimage.org/image/yplj4rumj/
What I need to show in website its like (according to this database example:
0,1,2,3,4 - open 10:00-14:00 and 15:00-18:30
5 - open 10:00-12:00
6 - closed
How I get results like:
http://postimage.org/image/toe53en63/
?
I tried to make queries with GROUPֹ_CONCAT and LEFT JOIN the same table ON a.day=b.day but with no luck :(
There sample of my query (that is wrong)
SELECT GROUP_CONCAT( DISTINCT CAST( a.day AS CHAR )
ORDER BY a.day ) AS days, DATE_FORMAT( a.time_from, '%H:%i' ) AS f_time_from, DATE_FORMAT( a.time_to, '%H:%i' ) AS f_time_to, DATE_FORMAT( b.time_from, '%H:%i' ) AS f_time_from_s, DATE_FORMAT( b.time_to, '%H:%i' ) AS f_time_to_s
FROM business_affiliate_hours AS a LEFT
JOIN business_affiliate_hours AS b ON a.day = b.day
WHERE a.affiliate_id =57
GROUP BY a.time_from, a.time_to, b.time_from, b.time_to
ORDER BY a.id ASC
This my table:
CREATE TABLE IF NOT EXISTS `business_affiliate_hours` (
`id` int(10) unsigned NOT NULL AUTO_INCREMENT,
`affiliate_id` int(10) unsigned NOT NULL DEFAULT '0',
`time_from` time NOT NULL,
`time_to` time NOT NULL,
`day` tinyint(1) unsigned NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
) ENGINE=MyISAM;
INSERT INTO `business_affiliate_hours` (`id`, `affiliate_id`, `time_from`, `time_to`, `day`) VALUES
(53, 57, '10:00:00', '12:00:00', 5),
(52, 57, '15:00:00', '18:30:00', 4),
(51, 57, '10:00:00', '14:00:00', 4),
(50, 57, '15:00:00', '18:30:00', 3),
(49, 57, '10:00:00', '14:00:00', 3),
(48, 57, '15:00:00', '18:30:00', 2),
(47, 57, '10:00:00', '14:00:00', 2),
(46, 57, '15:00:00', '18:30:00', 1),
(45, 57, '10:00:00', '14:00:00', 1),
(44, 57, '15:00:00', '18:30:00', 0),
(43, 57, '10:00:00', '14:00:00', 0);
Open hours may be different for every day, so I want to GROUP by the same open hours, and get list of days for all unique order of open hours.
Need your help!
Sorry for links to images, I cant upload images yes to here.
First build a materialised table of each day's combined times, then group on that:
SELECT GROUP_CONCAT(day ORDER BY day) AS days,
DATE_FORMAT(f1, '%H:%i') AS f_time_from,
DATE_FORMAT(t1, '%H:%i') AS f_time_to,
DATE_FORMAT(f2, '%H:%i') AS f_time_from_s,
DATE_FORMAT(t2, '%H:%i') AS f_time_to_s
FROM (
SELECT day,
MIN(time_from) AS f1,
MIN(time_to ) AS t1,
IF(COUNT(*) > 1, MAX(time_from), NULL) AS f2,
IF(COUNT(*) > 1, MAX(time_to ), NULL) AS t2
FROM business_affiliate_hours
WHERE affiliate_id = 57
GROUP BY day
) t
GROUP BY f1, t1, f2, t2
ORDER BY days
See it on sqlfiddle.