Top sales making product for each year - mysql

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

Related

Get summary grouped by category with three tables on mysql

category
---------------------------
id_category primary key
category
id_user foreign key
counterpart
---------------------------
id_counterpart primary key
counterpart
id_category foreign key
id_user foreign key
transaction
---------------------------
transaction primary key
date
id_counterpart foreign key
amount
id_card foreign key
id_user foreign key
Hello,
I have thoses table on mysql database and i want to have summary of each category (with 0 if there is any transaction) by month and year based on id_user.
I tried this command to have grouped by counterpart and it works but cannot reach when i add category and group by id_category.
select counterpart, s2.total from counterpart as s1
left join (select coalesce(sum(amount),0) as total, id_counterpart from transaction where year(date) = 2019 and month(date) = 7 and id_user = 2 group by id_counterpart) as s2
on s1.id_counterpart = s2.id_counterpart
left join category on s1.id_category = category.id_category
group by counterpart;
Do you have any idea to do that ? Else, i will do with php.
Thank you.
Edit : Add example
INSERT INTO `category` (`id_category`, `category`, `id_user`) VALUES
(1, 'cat_a', 1),
(2, 'cat_b', 1),
(3, 'cat_c', 1);
INSERT INTO `counterpart` (`id_counterpart`, `counterpart`, `id_category`, `id_user`) VALUES
(1, 'cp_a', 1, 1),
(2, 'cp_b', 2, 1),
(3, 'cp_c', 2, 1);
INSERT INTO `transaction` (`id_transaction`, `date`, `id_counterpart`, `amount`, `id_card`, `id_user`) VALUES
(1, '2019-07-01 00:00:00', 1, 400.00, 2, 1),
(2, '2019-07-01 00:00:00', 1, -24.95, 2, 1),
(3, '2019-07-31 00:00:00', 2, -20.04, 2, 1);
(4, '2019-07-30 00:00:00', 2, -1.00, 2, 1);
(5, '2019-07-29 00:00:00', 3, -2.00, 2, 1);
(6, '2019-07-28 00:00:00', 1, -3.00, 2, 1);
(7, '2019-07-27 00:00:00', 3, 2.00, 2, 1);
(8, '2019-07-26 00:00:00', 2, 5.00, 2, 1);
On july 2019 i want to have this, for user 1 :
cat_a 372.05
cat_b 16.04
cat_c 0.00
Join the tables and then group by category:
select c.category, coalesce(sum(t.amount), 0) total
from category c
left join counterpart as cp
on c.id_category = cp.id_category and c.id_user = cp.id_user
left join transaction t
on t.id_counterpart = cp.id_counterpart and t.id_user = cp.id_user and year(t.date) = 2019 and month(t.date) = 7 and t.id_user = 1
group by c.id_category, c.category
See the demo.
Results:
| category | total |
| -------- | ------ |
| cat_a | 372.05 |
| cat_b | -16.04 |
| cat_c | 0 |

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.

Mysql get total of matched items from two tables

I have two tables as given below...
INSERT INTO `tbl_project_skills` (`id`, `projectId`, `skillId`) VALUES
(17, 2, 44),
(27, 2, 43),
(31, 2, 78),
(33, 142, 79),
(35, 123, 88);
INSERT INTO `tbl_user_skills` (`id`, `userId`, `skillId`) VALUES
(2, 1, 44),
(3, 1, 78),
(5, 23, 45),
(6, 1, 36),
(7, 23, 88);
I want to find that how many skills are matched of user with each project like below
-----------------------------------------------------
userId | projectId | number_of_matched_skills
-----------------------------------------------------
1 | 2 | 2
1 | 142 | 0
1 | 123 | 0
23 | 2 | 0
23 | 142 | 0
23 | 123 | 1
------------------------------------------------------
I've tried below solution for the same...
SELECT ps.projectId, us.userId, ps.skillId AS pskillid, us.skillId AS uskillid, #points_veri:= (CASE WHEN '44' IN(ps.skillId) THEN 1 ELSE 0 END) + (CASE WHEN '43' IN(ps.skillId) THEN 1 ELSE 0 END) + (CASE WHEN '78' IN(ps.skillId) THEN 1 ELSE 0 END) AS number_of_matched_skills FROM tbl_user_skills AS us, tbl_project_skills AS ps
But I can't get it right. Hope you guys can give a hint.
I think you may have another table for store user and project, or this query will need get userId from tbl_user_skills and projectId from tbl_project_skills first, then do cross join, then join tbl_user_skills and tbl_project_skills to do aggregation:
select
main.userId,
main.projectId,
count(ps.projectId) as number_of_matched_skills
from (
select u.userId, p.projectId
from (
select distinct projectId from tbl_project_skills
) p
cross join (
select distinct userId from tbl_user_skills
) u
) main
left join tbl_user_skills us on us.userId = main.userId
left join tbl_project_skills ps on ps.skillId = us.skillId and main.projectId = ps.projectId
group by main.userId, main.projectId
Here is a demo in rextester.com.
Another option without having another table. Make an OUTER JOIN and then a GROUP BY on skillId.
SELECT userid,
projectid,
SUM(CASE WHEN p.skillId>1 AND u.skillId>1 THEN 1 ELSE 0 END) AS skills
FROM tbl_user_skills u
FULL OUTER JOIN tbl_project_skills p
ON u.skillId = p.skillId
GROUP BY userid, projectid
ORDER BY skills DESC

How do I get the sum of each persons best ten scores of the season?

I have a database of results for a season of 25 games. However only each persons best ten scores count.
Can anybody tell me how to sum just the top ten scores of each person AND show the minimum score that was used in that sum (their tenth best).
The database has PlayerName, TournamentID, Points
eg.
- TounamentID PlayerName Points
- 1 Jo 100
- 1 Tel 50
- 1 Kevin 75
- 2 Jo 100
- 2 Tel 50
- 2 Kevin 75
- 3 Jo 100
- 3 Tel 50
- 3 Kevin 75
- 4 Jo 100
- 4 Tel 50
- 4 Kevin 75
- 5 Jo 100
- 5 Tel 50
- 5 Kevin 75 etc
Many thanks in advance
EDIT 1
At the moment I have this kind of working though it doesn't handle duplicate scores very well and can actual end up adding up the top 11 if there's a duplicate;
SELECT X.PlayerName, Sum(X.Points) AS SumOfPoints, Min(X.Points) AS Target
FROM SoP11PreBats AS X
WHERE (((10)>(SELECT count(*)
FROM SoP11PreBats
WHERE PlayerName = X.PlayerName
AND Points > X.Points )))
GROUP BY X.PlayerName
ORDER BY Sum(X.Points) DESC;
Something like this would work for one player at a time:
SELECT SUM(n), MIN(n) FROM
(SELECT points AS n
FROM table
WHERE PlayerName = ?
ORDER BY n DESC
LIMIT 10
)
I'm not sure how to expand it to produce a table for every player.
SELECT test.playername, sum(top10.score), MIN(top10.score)
FROM test
LEFT JOIN (SELECT playername, score FROM test ORDER BY score DESC LIMIT 10) top10
ON top10.playername = test.playername
GROUP BY test.playername
Edit: Turns out the above approach using a subselect and join is not going to do the trick. Because you limit the results in the subselect it is not going to lead to a set of max 10 records PER playername.
The next approach would be to do something like
SELECT pk, name, score from test where
pk IN (SELECT pk FROM test t2 WHERE t2.name = name ORDER BY score DESC LIMIT 10)
This could create the proper set of records to join with. However LIMIT is not supported inside an IN clause (yet). So this won't compile.
EIDT2: The final approach I can think of goes like this:
select distinct test.name
, (SELECT sum(t2.score) FROM (select t3.score FROM test t3 WHERE t3.name = test.name ORDER BY score desc LIMIT 10) t2)
, (SELECT min(t2.score) FROM (select t3.score FROM test t3 WHERE t3.name = test.name ORDER BY score desc LIMIT 10) t2)
FROM test
This one also won't compile because the most inner test.name in the where can't be resolved properly.
I don't think that what you want to do can currently be done in 1 single query on mysql, but I'm curious if someone can proof me wrong.
Ok I got it working but it's about the most nasty sql hack I could think of and I'm not sure you should even consider to put it in production like this. It also only runs on mysql:
select PlayerName, sum(Points), min(Points) from
(select distinct SoP11PreBats.PlayerName, (SELECT t2.Points FROM test t2 WHERE t2.PlayerName = SoP11PreBats.PlayerName ORDER BY Points desc LIMIT 1) as Points FROM test
UNION ALL
select distinct SoP11PreBats.PlayerName, (SELECT t2.Points FROM test t2 WHERE t2.PlayerName = SoP11PreBats.PlayerName ORDER BY Points desc LIMIT 1,1) as Points FROM test
UNION ALL
select distinct SoP11PreBats.PlayerName, (SELECT t2.Points FROM test t2 WHERE t2.PlayerName = SoP11PreBats.PlayerName ORDER BY Points desc LIMIT 2,1) as Points FROM test
UNION ALL
select distinct SoP11PreBats.PlayerName, (SELECT t2.Points FROM test t2 WHERE t2.PlayerName = SoP11PreBats.PlayerName ORDER BY Points desc LIMIT 3,1) as Points FROM test
UNION ALL
select distinct SoP11PreBats.PlayerName, (SELECT t2.Points FROM test t2 WHERE t2.PlayerName = SoP11PreBats.PlayerName ORDER BY Points desc LIMIT 4,1) as Points FROM test
UNION ALL
select distinct SoP11PreBats.PlayerName, (SELECT t2.Points FROM test t2 WHERE t2.PlayerName = SoP11PreBats.PlayerName ORDER BY Points desc LIMIT 5,1) as Points FROM test
UNION ALL
select distinct SoP11PreBats.PlayerName, (SELECT t2.Points FROM test t2 WHERE t2.PlayerName = SoP11PreBats.PlayerName ORDER BY Points desc LIMIT 6,1) as Points FROM test
UNION ALL
select distinct SoP11PreBats.PlayerName, (SELECT t2.Points FROM test t2 WHERE t2.PlayerName = SoP11PreBats.PlayerName ORDER BY Points desc LIMIT 7,1) as Points FROM test
UNION ALL
select distinct SoP11PreBats.PlayerName, (SELECT t2.Points FROM test t2 WHERE t2.PlayerName = SoP11PreBats.PlayerName ORDER BY Points desc LIMIT 8,1) as Points FROM test
UNION ALL
select distinct SoP11PreBats.PlayerName, (SELECT t2.Points FROM test t2 WHERE t2.PlayerName = SoP11PreBats.PlayerName ORDER BY Points desc LIMIT 9,1) as Points FROM test
) top10
group by name
I have a feeling this will not work:
SELECT *
FROM
( SELECT pd.PlayerName
, ( SELECT SUM(t10.Points)
FROM
( SELECT t10.Points
FROM SoP11PreBats AS t10
WHERE t10.PlayerName = pd.PlayerName
ORDER BY t10.Points DESC
LIMIT 10
) AS x
) AS Sum10
, ( SELECT t10.Points
FROM SoP11PreBats AS t10
WHERE t10.PlayerName = pd.PlayerName
ORDER BY t10.Points DESC
LIMIT 1 OFFSET 9
) AS TenthBest
FROM
( SELECT DISTINCT PlayerName
FROM SoP11PreBats
) AS pd
) AS y
ORDER BY Sum10 DESC
But this will:
SELECT pb.PlayerName AS PlayerName
, COALESCE(SUM(p.Points),0) + pb.TenthBest*(10-COUNT(p.Points))
AS SumOfPoints
, pb.TenthBest AS Target
FROM
( SELECT pd.PlayerName
, ( SELECT t10.Points
FROM SoP11PreBats AS t10
WHERE t10.PlayerName = pd.PlayerName
ORDER BY t10.Points DESC
LIMIT 1 OFFSET 9
) AS TenthBest
FROM
( SELECT DISTINCT PlayerName
FROM SoP11PreBats
) AS pd
) AS pb
LEFT JOIN SoP11PreBats AS p
ON p.PlayerName = pb.PlayerName
AND p.Points > pb.TenthBest
GROUP BY pb.PlayerName
ORDER BY SumOfPoints DESC
This works (see test output below):
set #count:=0, #player:='';
SELECT
PlayerName,
SUM(Points) as sum_top_10,
MIN(Points) as min_top_10
FROM (SELECT PlayerName, Points
FROM (SELECT
Points,
#count := if (#player != PlayerName, 0, #count + 1) as count,
#player := PlayerName as PlayerName
FROM (SELECT PlayerName, Points FROM SoP11PreBATS order by 1, 2 desc) x) y
where count < 10) z
group by 1;
Here's the test, using OP's data, plus extra rows for 'Jo' to make more than 10 rows:
create table SoP11PreBATS (TounamentID int, PlayerName text, Points int);
delete from SoP11PreBATS;
insert into SoP11PreBATS values
(1, 'Jo', 100), (1, 'Tel', 50), (1, 'Kevin', 75), (2, 'Jo', 100), (2, 'Tel', 50),
(2, 'Kevin', 75), (3, 'Jo', 100), (3, 'Tel', 50), (3, 'Kevin', 75), (4, 'Jo', 100),
(4, 'Tel', 50), (4, 'Kevin', 75), (5, 'Jo', 100), (5, 'Tel', 50), (5, 'Kevin', 75),
(5, 'Jo', 50), (6, 'Jo', 75), (7, 'Jo', 100), (8, 'Jo', 50), (9, 'Jo', 75),
(10, 'Jo', 50), (11, 'Jo', 75), (12, 'Jo', 100);
select * from SoP11PreBATS where playername = 'Jo' order by points desc;
+-------------+------------+--------+
| TounamentID | PlayerName | Points |
+-------------+------------+--------+
| 1 | Jo | 100 |
| 2 | Jo | 100 |
| 3 | Jo | 100 |
| 4 | Jo | 100 |
| 5 | Jo | 100 |
| 7 | Jo | 100 |
| 12 | Jo | 100 |
| 6 | Jo | 75 |
| 9 | Jo | 75 |
| 11 | Jo | 75 |
| 5 | Jo | 50 |
| 8 | Jo | 50 |
| 10 | Jo | 50 |
+-------------+------------+--------+
-- Inspection shows Jo should have 925 as sum and 75 as min
-- Ran query above and got:
+------------+------------+------------+
| PlayerName | sum_top_10 | min_top_10 |
+------------+------------+------------+
| Jo | 925 | 75 |
| Kevin | 375 | 75 |
| Tel | 250 | 50 |
+------------+------------+------------+
-- Test output correct
I think this works, and it only uses one derived table:
SELECT #row := 0, #pp := NULL, #min := 0;
SELECT Player,
SUM(Points) AS Points,
MIN(Points) AS MinPoints
FROM (
SELECT Player,
Points,
#row := IF(
IFNULL(#pp, '') <> Player AND NOT (#pp := Player),
1,
#row + 1
) AS Row
FROM SoP11PreBats
ORDER BY Player, Points DESC
) tmp
WHERE tmp.Row <= 10
GROUP BY Player;
Test data:
CREATE TABLE `SoP11PreBats` (
`TournamentID` int(11) NOT NULL,
`Player` varchar(255) NOT NULL,
`Points` int(11) NOT NULL
);
INSERT INTO SoP11PreBats (TournamentID, Player, Points) VALUES
(15, 'Jo', 10),
(14, 'Jo', 20),
(13, 'Jo', 30),
(12, 'Jo', 40),
(11, 'Jo', 50),
(10, 'Jo', 60),
( 9, 'Jo', 70),
( 8, 'Jo', 80),
( 7, 'Jo', 90),
( 6, 'Jo', 100),
( 5, 'Jo', 110),
( 4, 'Jo', 120),
( 3, 'Jo', 130),
( 2, 'Jo', 140),
( 1, 'Jo', 150),
( 1, 'Tel', 15),
( 2, 'Tel', 25),
( 3, 'Tel', 35),
( 4, 'Tel', 45),
( 5, 'Tel', 55),
( 6, 'Tel', 65),
( 7, 'Tel', 75),
( 8, 'Tel', 85),
( 9, 'Tel', 95),
(10, 'Tel', 105),
(11, 'Tel', 115),
(12, 'Tel', 125),
(13, 'Tel', 135),
(14, 'Tel', 145),
(15, 'Tel', 155),
( 1, 'Kevin', 10),
( 2, 'Kevin', 20),
( 3, 'Kevin', 30),
( 4, 'Kevin', 40),
( 5, 'Kevin', 50),
( 6, 'Kevin', 60),
( 7, 'Kevin', 70),
( 8, 'Kevin', 80),
( 9, 'Kevin', 90),
(10, 'Kevin', 100),
(11, 'Kevin', 110),
(12, 'Kevin', 120),
(13, 'Kevin', 130),
(14, 'Kevin', 140),
(15, 'Kevin', 150);
Result:
+--------+--------+-----------+
| Player | Points | MinPoints |
+--------+--------+-----------+
| Jo | 1050 | 60 |
| Kevin | 1050 | 60 |
| Tel | 1100 | 65 |
+--------+--------+-----------+