MySQL count all outside limit - mysql

I have a MySQL query which counts hours we have worked from the top five of our clients. This works fine, and I am plotting the data on a graph. What I am trying to achieve is a calculation within the query that returns a sixth result set which is the sum of all the hours which are not in the top five. i.e. An 'Other' result, so that the sum of all 6 results would equal the total of sum of projects.hours.
My query:
SELECT
SUM(projects.hours) AS total_hours
FROM projects
GROUP BY projects.companyID
ORDER BY total_hours DESC
LIMIT 5

SELECT
SUM(projects.hours) AS total_hours,
(SELECT SUM(projects.hours) FROM projects) AS all_hours
FROM
projects
GROUP BY
projects.companyID
ORDER BY total_hours DESC
LIMIT 5
Subtract total_hours from all_hours in your application.

SELECT
1 no,
projects.company,
SUM(projects.hours) AS total_hours
FROM
projects
GROUP BY
projects.companyID
ORDER BY
total_hours DESC
LIMIT 5
UNION ALL
SELECT
2 no,
'Others',
SUM(projects.hours) AS total_hours
FROM
projects
WHERE projects.companyID NOT IN
( SELECT companyID
FROM
( SELECT
projects.companyID
FROM
projects
GROUP BY
projects.companyID
ORDER BY
SUM(total_hours) DESC
LIMIT 5
) AS tmp
)

How about a combination of both approaches, with the addition of a rollup we can remove the need for the IN
(I'm not sure if you can rollup with a limit though, I don't have MYSQL here so can't test, sorry)
NOTE: I would expect this to return a company id of NULL that contains the total number of other rows,
SELECT companyID,
CASE WHEN companyID IS NULL THEN
sum(Total_hours)
ELSE
sum(Total_hours) * -1
END
FROM
(
SELECT companyID,SUM(projects.hours) AS total_hours,
FROM projects
GROUP BY projects.companyID
ORDER BY total_hours DESC WITH ROLLUP LIMIT 5
UNION ALL
SELECT null,SUM(projects.hours)*-1 total_hours FROM projects
)
GROUP BY companyID

Related

MySQL: Multiple SELECT Statements in one Query

Im still learning MySQL however I would like to know how to query multiple SELECT statements in one query.
Currently I have two queries, one that displays Order count for 12 months and another for one month. I would like to query both at the same time however receive two different results.
I have tried using UNION with my query however it only outputs into one table and its quite hard to differentiate the result with what query.
SQL:
SELECT OrderDate, OrderItems, COUNT(*) AS Total FROM tb_orders WHERE OrderDate > DATE_SUB(now(), INTERVAL 12 MONTH) GROUP BY OrderItems ORDER BY Total DESC LIMIT 10
SELECT OrderDate, OrderItems, COUNT(*) AS Total FROM tb_orders WHERE OrderDate > DATE_SUB(now(), INTERVAL 1 MONTH) GROUP BY OrderItems ORDER BY Total DESC LIMIT 10;
TIA
You can merge the 2 queries by creating an identifier on the fly whether the record is from the monthly or yearly query. Column type is a column for this purpose in below query,
SELECT z.*
FROM
(
SELECT OrderDate, OrderItems, COUNT(*) AS Total, 'YEAR' as type
FROM tb_orders
WHERE OrderDate > DATE_SUB(now(), INTERVAL 12 MONTH)
GROUP BY OrderItems
LIMIT 10
UNION
SELECT OrderDate, OrderItems, COUNT(*) AS Total, 'MONTH' as type
FROM tb_orders
WHERE OrderDate > DATE_SUB(now(), INTERVAL 1 MONTH)
GROUP BY OrderItems
LIMIT 10
) AS z
ORDER BY z.Total DESC;
Working Fiddle
With union, you can always add hard-coded values to help differentiate rows:
select 'Invertebrate' as AnimalPhylum, species as AnimalSpecies from invertebrates_table
union
select 'Vertebrate', species from vertebrates_table;

Mysql complex query, group by with limit

I've the following table
store_visits: (store_id, city_id, date, visits, ...)
I want to select the maximum 5 stores ordered by visits.
SELECT X.*
FROM (
SELECT
store_id, SUM(visits) as sum_visits FROM store_visits
WHERE
(date <= '2014-06-28' AND date >= '2014-06-27')
AND
store_visits.city_id = 2
GROUP BY
store_id
ORDER BY
sum_visits desc
) X
LIMIT 5
I was wondering if there's a way to enhance the query to eleminate the temporary table and filesort.
Try this:
SELECT store_id, SUM(visits) AS sum_visits
FROM store_visits sv
WHERE sv.date <= '2014-06-28' AND sv.date >= '2014-06-27' AND sv.city_id = 2
GROUP BY store_id
ORDER BY sum_visits DESC LIMIT 5

specific status on consecutive days

I have a MySQL table ATT which has EMP_ID,ATT_DATE,ATT_STATUS with ATT_STATUS with different values 1-Present,2-Absent,3-Weekly-off. I want to find out those EMP_ID's which have status 2 consecutively for 10 days in a given date range.
Please help
Please have a try with this:
SELECT EMP_ID FROM (
SELECT
IF((#prevDate!=(q.ATT_DATE - INTERVAL 1 DAY)) OR (#prevEmp!=q.EMP_ID) OR (q.ATT_STATUS != 2), #rownum:=#rownum+1, #rownum:=#rownum) AS rownumber, #prevDate:=q.ATT_DATE, #prevEmp:=q.EMP_ID, q.*
FROM (
SELECT
EMP_ID
, ATT_DATE
, ATT_STATUS
FROM
org_tb_dailyattendance, (SELECT #rownum:=0, #prevDate:='', #prevEmp:=0) vars
WHERE ATT_DATE BETWEEN '2013-01-01' AND '2013-02-15'
ORDER BY EMP_ID, ATT_DATE, ATT_STATUS
) q
) sq
GROUP BY EMP_ID, rownumber
HAVING COUNT(*) >= 10
The logic is, to first sort the table by employee id and the dates. Then introduce a rownumber which increases only if
the days are not consecutive or
the employee id is not the previous one or
the status is not 2
Then I just grouped by this rownumber and counted if there are 10 rows in each group. That should be the ones who were absent for 10 days or more.
Have you tried something like this
SELECT EMP_ID count(*) as consecutive_count min(ATT_DATE)
FROM (SELECT * FROM ATT ORDER BY EMP_ID)
GROUP BY EMP_ID, ATT_DATE
WHERE ATT_STATUS = 2
HAVING consecutive_count > 10

Most common hour query?

I have this table:
ID(INT) DATE(DATETIME)
Under the DATE column there are a lot of different dates, and I want to figure out the most common hour between all the rows of the table, regardless of the day.
How can I do that with a MySQL query?
SELECT HOUR(date) AS hr, COUNT(*) AS cnt
FROM yourtable
GROUP BY hr
ORDER BY cnt DESC
LIMIT 1
relevant docs: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_hour
Try this -
SELECT HOUR(`DATE`) AS `hour`, COUNT(*)
FROM `table`
GROUP BY `hour`
You could do a query like:
SELECT COUNT(daterow) AS occurrences FROM table GROUP BY daterow ORDER BY occurrences DESC LIMIT 1;
SELECT COUNT( id ) , HOUR( date )
FROM test
GROUP BY HOUR( date )
ORDER BY COUNT( id ) DESC
LIMIT 1

Sum results of a few queries and then find top 5 in SQL

I have 3 queries:
table: pageview
SELECT event_id, count(*) AS pageviews
FROM pageview
GROUP BY event_id
ORDER BY pageviews DESC, rand()
LIMIT 1000
table: upvote
SELECT event_id, count(*) AS upvotes
FROM upvote
GROUP BY event_id
ORDER BY upvotes DESC, rand()
LIMIT 1000
table: attending
SELECT event_id, count(*) AS attendants
FROM attending
GROUP BY event_id
ORDER BY attendants DESC, rand()
LIMIT 1000
I'd like to combine the event_ids of all 3 queries ordered by amount and then choose the top 5. How do I do that?
EDIT: HERE IS WHAT I DID TO MAKE IT HAPPEN:
SELECT event_id, sum(amount) AS total
FROM (
(SELECT event_id, count(*) AS amount
FROM pageview
GROUP BY event_id
ORDER BY amount DESC, rand()
LIMIT 1000)
UNION ALL
(SELECT event_id, count(*) as amount
FROM upvote
GROUP BY event_id
ORDER BY amount DESC, rand()
LIMIT 1000)
UNION ALL
(SELECT event_id, count(*) as amount
FROM attending
GROUP BY event_id
ORDER BY amount DESC, rand()
LIMIT 1000)
) x
GROUP BY 1
ORDER BY sum(amount) DESC
LIMIT 5;
To UNION the resulting rows of all three queries and then pick the 5 rows with the highest amount:
(SELECT event_id, count(*) AS amount
FROM pageview
GROUP BY event_id
ORDER BY pageviews DESC, rand()
LIMIT 1000)
UNION ALL
(SELECT event_id, count(*)
FROM upvote
GROUP BY event_id
ORDER BY upvotes DESC, rand()
LIMIT 1000)
UNION ALL
(SELECT event_id, count(*)
FROM attending
GROUP BY event_id
ORDER BY attendants DESC, rand()
LIMIT 1000)
ORDER BY 2 DESC
LIMIT 5;
The manual:
To apply ORDER BY or LIMIT to an individual SELECT, place the
clause inside the parentheses that enclose the SELECT.
UNION ALL to keep duplicates.
To add the counts for every event_id:
SELECT event_id, sum(amount) AS total
FROM (
(SELECT event_id, count(*) AS amount
FROM pageview
GROUP BY event_id
ORDER BY pageviews DESC, rand()
LIMIT 1000)
UNION ALL
(SELECT event_id, count(*)
FROM upvote
GROUP BY event_id
ORDER BY upvotes DESC, rand()
LIMIT 1000)
UNION ALL
(SELECT event_id, count(*)
FROM attending
GROUP BY event_id
ORDER BY attendants DESC, rand()
LIMIT 1000)
) x
GROUP BY 1
ORDER BY sum(amount) DESC
LIMIT 5;
The tricky part here is that not every event_id will be present in all three base queries. So take care that a JOIN does not lose rows completely and additions don't turn out NULL.
Use UNION ALL, not UNION. You don't want to remove identical rows, you want to add them up.
x is a table alias and shorthand for AS x. It is required for for a subquery to have a name. Can be any other name here.
The SOL feature FULL OUTER JOIN is not implemented in MySQL (last time I checked), so you have to make do with UNION. FULL OUTER JOIN would join all three base queries without losing rows.
Answer to follow-up question
SELECT event_id, sum(amount) AS total
FROM (
(SELECT event_id, count(*) / 100 AS amount
FROM pageview ... )
UNION ALL
(SELECT event_id, count(*) * 5
FROM upvote ... )
UNION ALL
(SELECT event_id, count(*) * 10
FROM attending ... )
) x
GROUP BY 1
ORDER BY sum(amount) DESC
LIMIT 5;
Or, to use the base counts in multiple ways:
SELECT event_id
,sum(CASE source
WHEN 'p' THEN amount / 100
WHEN 'u' THEN amount * 5
WHEN 'a' THEN amount * 10
ELSE 0
END) AS total
FROM (
(SELECT event_id, 'p'::text AS source, count(*) AS amount
FROM pageview ... )
UNION ALL
(SELECT event_id, 'u'::text, count(*)
FROM upvote ... )
UNION ALL
(SELECT event_id, 'a'::text, count(*)
FROM attending ... )
) x
GROUP BY 1
ORDER BY 2 DESC
LIMIT 5;