SELECT SUM mysql - mysql

I need to select a sum of data for every day where employees belong to a department, I want to be able to select a date range and have a summary of every day is this possible, at the moment i am just getting a summary of the whole date range in one field.
This is my query at the moment
select
date,
SUM(no_of_live_products)
from
fulfilment
INNER JOIN
employee
ON employee.employee_id = fulfilment.employee_id
where
employee.department = 'Telecoms Services'
and date >= '2013-09-16'
and date <= '2013-09-29'
Is it possible to get a summary of data for every day within that date range or not?

You can
GROUP BY `date`
to see the sum per day

If you want to get the number of days and you have several time ranges,
First, get the number of dates for a single time range, then sum these up (needs a subselect).
Take this as an example, it's not a complete query for your situation:
SELECT SUM(x.days)
FROM (
SELECT TIMESTAMPDIFF(DAY, startDate, endDate) AS days
FROM fulfilment
) x
GROUP BY employee.employee_id

why not try
SELECT
date,
SUM(no_of_live_products)
FROM
fulfilment
INNER JOIN
employee ON employee.employee_id = fulfilment.employee_id
WHERE
employee.department = 'Telecoms Services'
AND date >= '2013-09-16'
AND date <= '2013-09-29'
GROUP BY date(date)

Related

How to Prefilter dates before grouping

I've grouped some MySQL data to perform some concatenation of fields. However I have dates which I need to find the earliest date before grouping, then display the earliest startdate and latest finishdate in the result.
SELECT *,
GROUP_CONCAT(RTRIM(inventoryID),' x ',qty SEPARATOR' + ') AS jobs
FROM Jobs WHERE ordernbr = '150002' GROUP BY grouping
My Table data is in the format of:
OrderNbr, Qty, InventoryID, StartDate, FinishDate, RequestedDate, OrderDate
After grouping, I need the result to show:
InventoryID, Qty, EarliestStartDate, LatestFinishDate, OrderDate, RequestedDate, jobs
But I'm not sure how to find and display dates before and after grouping.
I'm thinking something like this
SELECT MIN(startdate) AS EarlistStartDate,
GROUP_CONCAT(RTRIM(inventoryID),' x ',qty SEPARATOR' + ') AS jobs
FROM Jobs WHERE ordernbr = '150002' GROUP BY grouping
If you want to find the earliest start date and latest finish date inside the grouping, then this should suffice:
SELECT MIN(startdate) AS EarliestStartDate, MAX(finishDate) AS LatestFinishDate
GROUP_CONCAT(RTRIM(inventoryID),' x ',qty SEPARATOR' + ') AS jobs
FROM Jobs WHERE ordernbr = '150002' GROUP BY grouping
However, if you need to find the latest finish date before the grouping and the earliest start date after the grouping, then you would need to do something like this:
SELECT COALESCE(latestbefore.finishDate, jbgroup.EarliestStartDate),
COALESCE(earliestafter.startDate, jbgroup.LatestFinishDate),
jbgroup.jobs
FROM
(
SELECT MIN(startdate) AS EarliestStartDate,
GROUP_CONCAT(RTRIM(inventoryID),' x ',qty SEPARATOR' + ') AS jobs
FROM Jobs WHERE ordernbr = '150002' GROUP BY grouping
) jbgroup
LEFT JOIN jobs latestbefore
ON latestbefore.finishDate <= jbgroup.EarliestStartDate
LEFT JOIN jobs afterlatestbefore
ON afterlatestbefore.finishDate > latestbefore.finishDate AND
afterlatestbefore.finishdate <= jbgroup.EarliestStartDate
LEFT JOIN jobs earliestafter
ON earliestafter.startDate >= jbgroup.LatestFinishDate
LEFT JOIN jobs beforeearliestafter
ON beforeearliestafter.startDate < earliestafter.startDate AND
beforeearliestafter.startDate >= jbgroup.LatestFinishDate
WHERE afterlatestbefore.finishDate IS NULL AND
beforeearliestafter.finishDate IS NULL;
Explanation:
we use our initial query as a subselect, getting the groups
latestbefore is the latest date before the group
we guarantee that latestbefore is the latest date before the group via ensuring that no such afterlatestbefore exists that is before the group but after latestbefore
earliestafter is the earliest date after the group
we guarantee that earliestafter is the earliest date after the group via ensuring that no such beforeearliestafter exists that is after the group but before earliestafter

Select all available items in a specific period

So I have 2 tables caring and client, like this
client {
id,
name
}
caring {
id,
startDate,
endDate,
clientId
}
I need to get all clients that have at least one day available between two provided dates, you can see my screenshot as reference.
In screenshot I have two clients, and I need to return both of them. As you can see, the first client have three free days (21.5.-23.5.) between provided period (16.5.-29.5.) and the second client have not any caring periods.
So far i have tried something like this
SELECT * FROM client cl
WHERE cl.id NOT IN (SELECT clientId FROM caring
WHERE endDate >= CURDATE() AND endDate <= DATE_ADD(CURDATE(), INTERVAL 14 DAY))
This one return only clients that don't have carings at all. That is partially what I need because this query don't cover first client from my screenshot. Then I tried query bellow.
SELECT ca.startDate, ca.endDate, cl.firstName, cl.lastName
FROM caring ca
LEFT JOIN client cl on cl.id = ca.clientId
WHERE ca.startDate NOT IN (
SELECT endDate
FROM caring
) AND ca.startDate <= '2017-05-29' AND ca.endDate >= '2017-05-16'
But im not getting desired results.
Any idea how I can achieve this, thx in advance!
Select carings in period of interest and limit start/end dates to this period, respectively. This limitation will allow for easier counting of "booked" i.e. not-free days later on.
SELECT ca.id,
-- Limit start/end dates to period of interest, respectively
GREATEST (ca.startDate, '2017-05-16') AS `effectiveStartDate`,
LEAST (ca.endDate, '2017-05-29') AS `effectiveEndDate`,
ca.clientId
FROM carings ca
WHERE ca.startDate <= '2017-05-29' AND ca.endDate >= '2017-05-16';
Next, count booked days:
DATEDIFF (DATE_ADD (LEAST (ca.endDate, '2017-05-29'), INTERVAL 1 DAY),
GREATEST (ca.startDate, '2017-05-16'))
AS `effectiveDays`
Finally, filter out clients that are booked over the whole period. This is done by comparing
the sum of booked days per client (GROUP BY) to
the number of days of the whole period (HAVING sumDays < DATEDIFF(...)).
As you want also clients that are not booked at all over the whole period, I would suggest to start from the clients table and "just" LEFT JOIN the (effective) carings:
SELECT cl.id, cl.name, IFNULL (SUM (eca.effectiveDays), 0) AS `sumDays`
FROM clients cl
LEFT JOIN
(SELECT ca.id,
-- Limit start/end dates to period of interest, respectively
GREATEST (ca.startDate, '2017-05-16') AS `effectiveStartDate`,
LEAST (ca.endDate, '2017-05-29') AS `effectiveEndDate`,
DATEDIFF (
DATE_ADD (LEAST (ca.endDate, '2017-05-29'), INTERVAL 1 DAY),
GREATEST (ca.startDate, '2017-05-16'))
AS `effectiveDays`,
ca.clientId
FROM carings ca
WHERE ca.startDate <= '2017-05-29' AND ca.endDate >= '2017-05-16')
eca -- effectiveCarings
ON eca.clientId = cl.id
GROUP BY cl.id, cl.name
HAVING sumDays <
DATEDIFF (DATE_ADD ('2017-05-29', INTERVAL 1 DAY), '2017-05-16')
ORDER BY cl.id;
See also http://sqlfiddle.com/#!9/1038b9/19
Select clients whose endDate happens before the last day of your provided period and there's a gap between endDate and startDate during the specified period.
SELECT * FROM client FULL OUTER JOIN caring ON client.id = caring.clientId WHERE endDate <= '2017-05-28' AND DATEDIFF(day, startDate, endDate) > DATEDIFF(day, '2017-05-16' , endDate);

Name of customer with highest sale monthwise

I have a sales table from which I select the total sales per month , highest sale , number of sale for all the months in the current year, using
select monthname(date),sum(amt_c),MAX(amt_c)
from sales where year(date)= year(now())
group by monthname(date) ;
I want to also select the customer who has done the highest purchase , i.e the customer correponding to the MAX(amt_c).
amt_c is the purchase done by the customer,
One way is a filtering join:
select filter.mn
, filter.sum_sales
, filter.max_sales
, sales.cust
from (
select monthname(date) as mn
, sum(amt_c) as sum_sales
, max(amt_c) as max_sales
from sales
where year(date) = year(now())
group by
mn
) filter
join sales
on monthname(sales.date) = filter.mn
and sales.amt_c = filter.max_sales
For more approaches, browse the greatest-n-per-group tag.
select v.monthname,
v.sum_amt_c,
v.max_amt_c,
count(s.amt_c) as num_of_amounts,
group_concat(s.cust) as customers
from (select monthname(date) as monthname,
sum(amt_c) as sum_amt_c,
max(amt_c) as max_amt_c
from sales
where date between concat(year(now()), '-01-01') and concat(year(now()), '-12-31')
group by monthname(date)) v
join sales s
on v.max_amt_c = s.amt_c
and v.monthname = monthname(s.date)
and s.date between concat(year(now()), '-01-01') and concat(year(now()), '-12-31')
group by v.monthname, v.sum_amt_c, v.max_amt_c
order by month(s.date)
This is similar to Andomar's answer however it provides the following benefits:
If your DATE field is indexed (it should be) the above query will use that index. You should not have criteria on a date field with a function applied to it. MySQL does not support function based indexes, so it is a given that year(date) is not indexed. date may be indexed, however.
This sorts the results by month # (1-12) but shows the month name.
In the event that the same 2+ customers are tied, this will list all of them, and show only one row for that month. You would otherwise potentially have 2, 3, 4+ rows for a single month in the event of a tie. This is done via MySQL's GROUP_CONCAT function.

Return active users in the last 30 days for each day

I have a table, activity that looks like the following:
date | user_id |
Thousands of users and multiple dates and activity for all of them. I want to pull a query that will, for every day in the result, give me the total active users in the last 30 days. The query I have now looks like the following:
select date, count(distinct user_id) from activity where date > date_sub(date, interval 30 day) group by date
This gives me total unique users on only that day; I can't get it to give me the last 30 for each date. Help is appreciated.
To do this you need a list of the dates and join that against the activities.
As such this should do it. A sub query to get the list of dates and then a count of user_id (or you could use COUNT(*) as I presume user_id cannot be null):-
SELECT date, COUNT(user_id)
FROM
(
SELECT DISTINCT date, DATE_ADD(b.date, INTERVAL -30 DAY) AS date_minus_30
FROM activity
) date_ranges
INNER JOIN activity
ON activity.date BETWEEN date_ranges.date_minus_30 AND date_ranges.date
GROUP BY date
However if there can be multiple records for a user_id on any particular date but you only want the count of unique user_ids on a date you need to count DISTINCT user_id (although note that if a user id occurs on 2 different dates within the 30 day date range they will only be counted once):-
SELECT activity.date, COUNT(DISTINCT user_id)
FROM
(
SELECT DISTINCT date, DATE_ADD(b.date, INTERVAL -30 DAY) AS date_minus_30
FROM activity
) date_ranges
INNER JOIN activity
ON activity.date BETWEEN date_ranges.date_minus_30 AND date_ranges.date
GROUP BY date
A bit cruder would be to just join the activity table against itself based on the date range and use COUNT(DISTINCT ...) to just eliminate the duplicates:-
SELECT a.date, COUNT(DISTINCT a.user_id)
FROM activity a
INNER JOIN activity b
ON a.date BETWEEN DATE_ADD(b.date, INTERVAL -30 DAY) AND b.date
GROUP by a.date

One query to get total # records for each day in a month

Scenario:
I have a table of registrations and there is a datetime column.
I want to display a bar graph on a webpage showing number of registrations per day for the current month.
How can I do that with one query? i.e. get back a resultset with a field for each day (of current month) with the total number of registrations that day
select day(Date), count(*)
from table
where month(Date) = month(NOW()) and year(Date) = year(NOW())
group by day(Date)
"Where" clause selects the current month. You can use count(whatever) or count(distinct whatever) instead of count(*)...
Something like this pseudo-code:
SELECT Day, COUNT(*)
FROM XXX_Users
GROUP BY "date extracted from datetime" as Day
To extract the date from datetime you could use the DATE function:
SELECT Day, COUNT(*)
FROM XXX_Users
GROUP BY DATE(created) as Day
Or you can create a Stored Procedure to enclose all the logic inside