Group all rows after nth row together - mysql

I have the current table:
+----------+-------+
| salesman | sales |
+----------+-------+
| 1 | 142 |
| 2 | 120 |
| 3 | 176 |
| 4 | 140 |
| 5 | 113 |
| 6 | 137 |
| 7 | 152 |
+----------+-------+
I would like to make a query to retrieve the 3 top salesman, and an "Other" column, that would be the sum of everyone else. The expected output would be:
+----------+-------+
| salesman | sales |
+----------+-------+
| 3 | 176 |
| 7 | 152 |
| 1 | 142 |
| Others | 510 |
+----------+-------+
I am using MySQL, and I am experienced about it, but i can't imagine a way of doing this kind of GROUP BY.
A tried UNION with 2 SELECT, one for the top 3 salesman and another select for the "Others", but I couldn't figure a way of excluding the top 3 from the 2nd SELECT

You can do this by LEFT JOINing your table to a list of the top 3 salesmen, and then grouping on the COALESCEd salesman number from the top 3 table (which will be NULL if the salesman is not in the top 3).
SELECT COALESCE(top.sman, 'Others') AS saleman,
SUM(sales) AS sales
FROM test
LEFT JOIN (SELECT salesman AS sman
FROM test
ORDER BY sales DESC
LIMIT 3) top ON top.sman = test.salesman
GROUP BY saleman
ORDER BY saleman = 'Others', sales DESC
Output:
saleman sales
3 176
7 152
1 142
Others 510
Demo on dbfiddle

Using UNION, ORDER BY, LIMIT, OFFSET AND GROUP BY statements you should do the trick:
SELECT salesman, sales
FROM t
ORDER BY sales DESC LIMIT 3
UNION
SELECT 'Others', SUM(sales)
FROM (SELECT salesman, sales
FROM t
ORDER BY sales DESC LIMIT 3, 18446744073709551615) AS tt;
The big number at the end is the way to apply limit until the end of the table, as suggested here

This is a pain in MySQL:
(select salesman, count(*) as cnt
from t
group by salesman
order by count(*), salesman
limit 3
) union all
(select 'Others', count(*)
from t left join
(select salesman, count(*) as cnt
from t
group by salesman
order by count(*)
limit 3
) t3
on t3.salesman = t.salesman
where t3.salesman is null
);

This should be the fastest one if appropriate indexes are present:
(
SELECT salesman, sales
FROM t
ORDER BY sales DESC
LIMIT 3
)
UNION ALL
(
SELECT 'Other', SUM(sales) - (
SELECT SUM(sales)
FROM (
SELECT sales
FROM t
ORDER BY sales DESC
LIMIT 3
) AS top3
)
FROM t
)
ORDER BY CASE WHEN salesman = 'Other' THEN NULL ELSE sales END DESC

this will work:
select salesman,sales from tablename a where a.salesman in (3,7,1)
union all
select 'others' as others,sum(a.sales) as sum_of_others from tablename a where
a.salesman not in (3,7,1) group by others;
check https://www.db-fiddle.com/f/73GjFXL3KsZsYnN26g3rS2/0

Related

Show/Select and Order By SUM MYSQL

I have this data
ID | Name | views
1 | one | 10
2 | one | 10
3 | three| 30
4 | four | 5
5 | four | 5
How i can show orderby sum of views ?
output like this
three | 30
one | 20
four | 10
SELECT DISTINCT NAME, SUM(VIEWS) FROM **--TABLE NAME--** ORDER BY SUM(VIEWS) DESC
OR
SELECT NAME, SUM(VIEWS) FROM **--TABLE NAME--**
GROUP BY NAME
ORDER BY SUM(VIEWS) DESC
You can use next simple query:
SELECT
`Name`, SUM(Views) `TotalViews`
FROM `Data`
GROUP BY `Name`
ORDER BY `TotalViews` DESC;
Here the example SQLize online

The division of each row by the sum of the rows that has the same value of two columns

I want to calculate the division of each row per the sum of all rows that have the same Dateadded and fundid, but it seems my query is wrong due the results is not what I was expecting.
My table schema looks like this, I avoided mine because it has many more columns:
+----+--------+------------+--------+
| id | fundid | Dateadded | amount |
+====+========+============+========+
| 1 | 45 | 21-02-2018 | 5412 |
| 2 | 45 | 21-02-2018 | 5414 |
| 3 | 45 | 21-02-2018 | 1412 |
| 4 | 45 | 22-02-2018 | 5756 |
| 5 | 45 | 22-02-2018 | 4412 |
| 6 | 45 | 25-02-2018 | 2532 |
| 7 | 45 | 26-02-2018 | 7892 |
| 8 | 45 | 26-02-2018 | 8143 |
+----+-------+-------------+--------+
Rows with id's: 1,2,3 should be calculated together because they have
the same fundid and date.
Rows with id's: 4,5 same thing.
Rows with id's: 6 it is just one.
Rows with id's: 7,8 same thing.
My SQL query:
SELECT fundid
, Dateadded
, ( amount / SUM(amount) ) AS AvgRow
FROM stock2
GROUP
BY fundid
, Dateadded
ORDER
BY DateAdded ASC
Is this what you want?
select t.*, t.amount / tt.total_amount
from stock2 t join
(select fundid, dateadded, sum(amount) as total_amount
from stock2 t
group by fundid, dateadded
) tt
using (fundid, dateadded);
Or is this?
select fundid, dateadded, sum(t.amount) / tt.total_amount
from stock2 t cross join
(select sum(amount) as total_amount
from stock2 t
) tt
group by fundid, dateadded, tt.total_amount;
Check out a very well explained response to a similar issue related to usage of Group by here).
Similarly to the situation described there, for your query is ambiguous re: what "amount" should be used for each row. I.e. if you try:
SELECT fundid, Dateadded, ( AVG(amount) / SUM(amount) ) AS AvgRow FROM stock2 GROUP BY fundid, Dateadded ORDER BY DateAdded ASC
it will work because AVG(amount) is non-ambiguous for each (fundid, Dateadded) pair that should be calculated together.
It seems you are looking for something like:
SELECT st.fundid, st.Dateadded, ( amount / st2.total) ) AS AvgRow
FROM stock2 st
inner join
(select fundid, Dateadded, sum(amount) as total
from stock2
GROUP BY fundid, Dateadded) st2
on st.fundid = st2.fundid and st.Dateadded = st2.Dateadded
order by st.Dateadded

MySQL SUM over a virtual subquery field (with another SUM) with GROUP BY

I have two tables: invoices and items.
invoices
id | timest
items
id | invoice_id | price | qty
It is apparent an invoice may have several items - items.invoice_id = invoices.id.
I have the following query that selects all invoices with the total sum of theirs items:
SELECT id, DATE_FORMAT(FROM_UNIXTIME(inv.time), "%Y-%m" ) AS _period,
(SELECT SUM(it.price*it.quantity) FROM items AS it WHERE it.invoice_id=inv.id) as total
FROM `invoices` `inv`
This generates something like:
id| _period | total
-------------------
1 | 2014-06 | 100
4 | 2014-06 | 200
5 | 2014-07 | 660
6 | 2014-07 | 300
7 | 2014-07 | 30
9 | 2015-02 | 225
Now I want to group it by the period to have output as:
_period | qty | total_price
---------------------------
2014-06 | 2 | 300
2014-07 | 3 | 990
2015-02 | 1 | 224
I can easily do it for the quantity field as
SELECT DATE_FORMAT(FROM_UNIXTIME(inv.time), "%Y-%m" ) AS _period,
COUNT(inv.id) as qty
FROM `invoices` `inv`
GROUP BY _period
But I can't figure out how the similar thing could be done for the total_price field, which results from a subquery virtual field? Does anyone have any idea?
Thank you!
You should do this using a LEFT JOIN and GROUP BY:
SELECT DATE_FORMAT(FROM_UNIXTIME(i.time, '%Y-%m') AS _period,
COUNT(DISTINCT i.id) as num_invoices
SUM(i.price * it.quantity) as total
FROM invoices i LEFT JOIN
items it
ON it.invoice_id = i.id
GROUP BY _period
ORDER BY _period;
try this
SELECT InnerTable._period, Count(InnerTable.id) as id, Sum(InnerTable.total) as total FROM
(SELECT id, DATE_FORMAT(FROM_UNIXTIME(inv.time), "%Y-%m" ) AS _period,
(SELECT SUM(it.price*it.quantity) FROM items AS it WHERE it.invoice_id=inv.id) as total
FROM `invoices` `inv`) as InnerTable FROM GROUP BY InnerTable._period.
Making sub table from the query and then put group by on it.

Combine two SQL Queries efficiently

I have two Queries:
1)
SELECT COUNT(*) AS total, COUNT(supporter) AS done FROM Supports;
2)
SELECT supporter, COUNT(supporter) AS amount FROM Supports
GROUP BY supporter ORDER BY amount DESC LIMIT 1;
How can I efficiently combine them?
This is how the Table looks like:
+-----------------------------+
| id | name | supporter |
+-----------------------------+
| 1 | user1 | sup1 |
| 2 | user1 | sup2 |
| 3 | user1 | NULL |
| 4 | user2 | sup1 |
| 5 | user2 | sup3 |
+-----------------------------+
Since you want the total, you'll have to use a subquery to combine into one query. Include it in the FROM clause.
SELECT supporter, COUNT(supporter) AS amount, total, done
FROM Supports,
(SELECT COUNT(*) AS total, COUNT(supporter) AS done FROM Supports) totals
GROUP BY supporter
ORDER BY amount DESC
LIMIT 1;
I believe this is what you are looking for:
SELECT
(SELECT COUNT(*) FROM Supports) as total,
(SELECT COUNT(supporter) FROM Supports) as done,
supporter,
COUNT(*) AS amount
FROM Supports
GROUP BY supporter
ORDER BY amount;
Results looks like this: http://sqlfiddle.com/#!9/9e4ee/9
total done supporter amount
5 4 sup3 1
5 4 sup2 1
5 4 NULL 1
5 4 sup1 2
SELECT a.total, a.done, b.supporter, b.amount
( SELECT COUNT(*) AS total,
COUNT(supporter) AS done
FROM Supports
) AS a
JOIN
( SELECT supporter,
COUNT(supporter) AS amount
FROM Supports
GROUP BY supporter
ORDER BY amount DESC
LIMIT 1
) AS b;

SUM a pair of COUNTs from two tables based on a time variable

Been searching for an answer to this for the better part of an hour without much luck. I have two regional tables laid out with the same column names and I can put out a result list for either table based on the following query (swap Table2 for Table1):
SELECT Table1.YEAR, FORMAT(COUNT(Table1.id),0) AS Total
FROM Table1
WHERE Table1.variable='Y'
GROUP BY Table1.YEAR
Ideally I'd like to get a result that gives me a total sum of the counts by year, so instead of:
| REGION 1 | | REGION 2 |
| YEAR | Total | | YEAR | Total |
| 2010 | 5 | | 2010 | 1 |
| 2009 | 2 | | 2009 | 3 |
| | | | 2008 | 4 |
I'd have:
| MERGED |
| YEAR | Total |
| 2010 | 6 |
| 2009 | 5 |
| 2008 | 4 |
I've tried a variety of JOINs and other ideas but I think I'm caught up on the SUM and COUNT issue. Any help would be appreciated, thanks!
SELECT `YEAR`, FORMAT(SUM(`count`), 0) AS `Total`
FROM (
SELECT `Table1`.`YEAR`, COUNT(*) AS `count`
WHERE `Table1`.`variable` = 'Y'
GROUP BY `Table1`.`YEAR`
UNION ALL
SELECT `Table2`.`YEAR`, COUNT(*) AS `count`
WHERE `Table2`.`variable` = 'Y'
GROUP BY `Table2`.`YEAR`
) AS `union`
GROUP BY `YEAR`
You should use an UNION:
SELECT
t.YEAR,
COUNT(*) as TOTAL
FROM (
SELECT *
FROM Table1
UNION ALL
SELECT *
FROM Table2
) t
WHERE t.variable='Y'
GROUP BY t.YEAR;
Select year, sum(counts) from (
SELECT Table1.YEAR, FORMAT(COUNT(Table1.id),0) AS Total
FROM Table1
WHERE Table1.variable='Y'
GROUP BY Table1.YEAR
UNION ALL
SELECT Table2.YEAR, FORMAT(COUNT(Table2.id),0) AS Total
FROM Table2
WHERE Table2.variable='Y'
GROUP BY Table2.YEAR ) GROUP BY year
To improve upon Shehzad's answer:
SELECT YEAR, FORMAT(SUM(counts),0) AS total FROM (
SELECT Table1.YEAR, COUNT(Table1.id) AS counts
FROM Table1
WHERE Table1.variable='Y'
GROUP BY Table1.YEAR
UNION ALL
SELECT Table2.YEAR, COUNT(Table2.id) AS counts
FROM Table2
WHERE Table2.variable='Y'
GROUP BY Table2.YEAR ) AS newTable GROUP BY YEAR