Grouping by date and people - mysql

I am developing a database in php/mysql.
I have a table ‘matterjuncactions’ which contains the fields
actiondate
howlong
staffid
When a member of staff records an action it is entered into the table with the field howlong recording time as a decimal.
A member of staff could record any number of actions in a day. (There are currently 12 staff members)
What I would like to do is have a page showing a table with dates down the left hand side and staff ids across the top with each cell containing the sum of the time spent for that day. (i.e. sum of’howlong’)
So something like:
Date | Staffid 1 | Staff id2 |
6th August | 3.5 | 2.7 |
5th August | 5.7 | 4.6 |
etc
I can get the totals for a single staff member using:
SELECT DATE_FORMAT(matterjuncactions.actiondate,'%W-%D') AS fDt
, SUM(howlong) AS tottime
FROM matterjuncactions
WHERE staffid=1
GROUP
BY matterjuncactions.actiondate
ORDER
BY matterjuncactions.actiondate DESC
I can’t work out how to get this to display all of the data for all of the staffids.

Try this one
SELECT DATE_FORMAT(matterjuncactions.actiondate,'%W-%D') AS fDt,
( select SUM(howlong) from matterjuncactions AS tottime1 where staffid=1) as total1,
( select SUM(howlong) from matterjuncactions AS tottime2 where staffid=2) as total2
FROM matterjuncactions
GROUP
BY matterjuncactions.actiondate
ORDER
BY matterjuncactions.actiondate DESC
Hope it helps :)

Related

mySQL: Given 2 column data, find 3rd column and then AVG() 3rd column based on 2nd column

I've 3 columns, staffName, dateOfIncident, incidentNo. I'm looking for 'total incidents' which will be the total number of incidents from a certain staff for a particular year, which I get from dateOfIncident. Now I must find the average of the number of 'total incidents', finally providing the headings:
staffName | avgIncidents
What I have so far is:
SELECT l.staffName, l.dateOfIncident, COUNT(l.incidentNo) AS avgIncidents
FROM incidentsR l
Which displays:
staffName | dateOfIncident | avgIncidents
.... | ..... | ....
Though this obviously supplies 3 columns output, and so far I'm only able to count the total amount of incidents, which I then need to use to calculate the avg.
What I need help with is how to take information from the first 2 columns of name and date to find the 'total incidents' for that specific person for that year. And then for each staffName calculate the average of the number of 'total incidents' for the years we have data on the staff.
Your query would be:
SELECT l.staffName, year(l.dateOfIncident) as year,
COUNT(*) as incidentsPerYear
FROM incidentsR l
GROUP BY l.staffName, year(l.dateOfIncident);
To get the average per year for the staff, you could use a subquery. Or do the calculation directly:
SELECT l.staffName,,
COUNT(*) / COUNT(DISTINCT year(l.dateOfIncident))as avgIncidentsPerYear
FROM incidentsR l
GROUP BY l.staffName;

Count number of action and show last date of action

I am querying an audit database to try and find out how many actions each user has completed and when their last action was.
The query I am using is :
SELECT user_id,
count(id) as actions,
datetime
from auditing
WHERE datetime>='2014-03-01 00:00:00'
GROUP BY user_id
ORDER BY `auditing`.`datetime` DESC
This correctly shows me the total number of items but it does not show the correct last date - the date it does show me it quite random i.e. not at the top or bottom of the list but taken from somewhere in the middle. I checked this for a number of entries produced and they are all wrong and do not reflect the latest action.
How can I get it to show me the last (most recent) event in the above query?
Example:
user_id | actions | datetime
1 | 10 | 2014-07-04 16:10:14
2 | 55 | 2014-07-05 11:15:08
3 | 8 | 2014-07-04 22:19:43
Thanks
You should only SELECT columns that are part of your GROUP BY clause or are a result of an aggregate function. You can and probably should configure your database server so that it would complain about your query. It would say something like:
ERROR 1055 (42000): 'datetime' isn't in GROUP BY
The reason behind it is, that you don't tell the database server which datetime value you want (the earliest, the average, the latest?). So in order to get the last value, try this query:
SELECT user_id, count(id) as actions, max(datetime)
FROM auditing
WHERE datetime>='2014-03-01 00:00:00'
GROUP BY user_id
ORDER BY user_id
You can try with this:
SELECT user_id, COUNT(actions), MAX(datetime)
FROM auditing
WHERE datetime>='2014-03-01 00:00:00'
GROUP BY user_id

Months calculations inside stored procedure

I need to get a list of months that are not located inside the database.
Example:
Table members
ID | Member's code | Member since
1 | 555-12 | 2012-11-22
Table membership
ID | Code | Paid
1 | 555-12 | 2013-1-1
2 | 555-12 | 2013-3-12
3 | 555-12 | 2013-5-1
Let's say that today is : 2013-11-17
I need to get output like this:
Member's code | Debt ( Months )
555-12 | 11-2012
555-12 | 12-2012
555-12 | 2-2013
555-12 | 4-2013
Is this possible to do with a SQL? Do I need to have a stored procedure where I will pass Member's code?
My idea is to use a number table, that contains just numbers from 0 to 100 or more:
CREATE TABLE numbers (
n INT
);
INSERT INTO numbers (n)
VALUES
(0),(1),(2),(3),(4),(5),(6),(7),(8),(9),(10),(11),(12),(13),(14),(15)...;
Then you can use a query like this:
SELECT
m.ID,
m.Code,
DATE_FORMAT(m.Member_since + INTERVAL num.n MONTH, '%m-%Y') As Debt_Month_Year
FROM
members m INNER JOIN numbers num
ON TIMESTAMPDIFF(MONTH, m.Member_since, LAST_DAY(CURDATE()))>=num.n
LEFT JOIN membership ms
ON
m.Code = ms.Code
AND
LAST_DAY(ms.Paid)=LAST_DAY(m.Member_since + INTERVAL num.n MONTH)
WHERE
ms.id IS NULL
-- and if you wish, add the following line:
AND m.Code = '555-12'
Please see fiddle here.
select code,
left(since,7) as debt
from user where code='555-12'
union all
select code,
left(date_add(paid, interval -1 MONTH),7) as debt
from paid where code='555-12'
Fiddle
http://sqlfiddle.com/#!2/3d988/1
If you can guarantee that you will only have a consistent one month gap between the entries for each user, then I think timus's solution should work.
If not, I think you'll need to have some sort of date table to determine what values you don't have in the database.
Here's a SQL Fiddle demonstrating what I'm talking about. I intentionally removed one of the rows from your paid table to test skipping more than one month. Basically it gets the month from the member table (the first entry, I guess), and I union that to a second query getting the months that are not in the paid table.
SQL Fiddle
select
left (mbr_since,7) as MNTH
from
user
union
select
mnth
from
months
left outer join paid
on months.mnth = left(paid,7)
where
paid.code is null
The months table is just a row for each month (2013-01, 2013-02, etc). The second query joins to that, and then filters out rows where there is a match in the paid table. Hopefully my explanation is making sense...

How can I repeat this query over several months in MySQL?

I have a membership list with over 1.2M members. People commonly subscribe, unsubscribe, and re-subscribe to the list. Often, I find myself needing to know which users were subscribed at a particular moment in time. I have a table called subscription_history, with this structure:
-----------------------------------------------------------------------
| id | native key |
-----------------------------------------------------------------------
| user_id | foreign key that joins the user table |
-----------------------------------------------------------------------
| change_code | 1 or 2 for subscriptions, 4-7 for unsubscriptions |
-----------------------------------------------------------------------
| created_at | date-time stamp when the change was made |
-----------------------------------------------------------------------
Right now, if I want to know who was subscribed at a particular date in the past (March 31, 2012 in this example), I run this query:
SELECT user_id
FROM
(SELECT
user_id
, MAX(created_at) AS last_change_date
, change_code
FROM subscriptionhistory
WHERE DATE(created_at) <= '2012-03-31'
GROUP BY user_id
) AS last
WHERE change_code IN (1,2)
This finds each user's last subscription action before or on the target date, then returns the user if that action was a subscription. We then use that list of users to run various other queries, such as the average lifetime sales. This system works well, but only for one date at a time. If I wanted to know the average subscriber's lifetime sales for every month of the year, I would have to run this query 12 times, manually incrementing the date in the WHERE statement each time.
Now I want to create a version of this that can I can use for more than a single date... so that it could give me all users subscribed in January, then February, etc., and I could run average lifetime sales for subscribers in each month. I can't just do a GROUP BY for this, since someone who was a subscriber in March might have unsubscribed in April and re-subscribed in June. I suppose I could 12 UNION queries ... but was hoping for something a little more elegant!
A few limiting parameters: I only have read-only access to the database; I cannot change anything about the table structure or make temporary tables. I have to do this only in MySQL - because of the way our CRM works, I can't use Python or PHP to manipulate results. Any help would be greatly appreciated! Please let me know if I am not explaining this well. Thanks!
SELECT user_id, group_concat(date_format(created_at, '%Y-%m')) as ActiveMonth from
(SELECT user_id, created_at, change_code from Subscriptions WHERE
change_code in (1,2) order by 1,2,3) b
group by user_id
order by user_id, ActiveMonth desc
You can take the group_concat out and the group by and it should give you a row and active month for every user_id.
I created a SQLFiddle and changed the table name to subscriptions for ease of use.
http://sqlfiddle.com/#!2/6b2f2/14

MySql - Problems in a query construct

I need your help....I'm working on a little Time Management Sytem for my compagny.
I have several tables, including this two tables :
Pointage
Id_check | id_user | week | time | …….
Users
Id_user | first_name | last_name | ………
I would like find a means to construct a report which give me all people who didn't check 5 days for last weeks. For example
Id_user | week | time
So I have created a query like that :
SELECT week,id_user,SUM(time) AS totalW FROM pointage WHERE week<42
GROUP BY id_user,week HAVING totalW<5 ORDER BY id_user
My problem is that this query give me lates only if the person has checked at least one time (for a week).
For example, if the id_user '1' don't check any time for the week 40, he won't appear in my report. An important problem for a query which should give me all people in late in their checks. He will be appeared if he had checked at least one time, for example 1 day.
I have tried to modify my query, I have created a new table 'week', join it with LEFT / RIGHT JOIN but I don't find any solution to solve my wish !
So my last chance is to post this message !
Do you have an idea to obtain this report ?
Thanks very much for your help and sorry for my bad english !
Nicolas
select week.week, users_id_users,
(
select
if(sum(time) is null, 0, sum(time))
from pointage
where users.id_user=pointage.id_user and pointage.week=week.week
group by pointage.id_user
having count(*)<5
) as sum_time
from users, week
where week.week<42
assuming your week table contains record from week 1..52