I have a table of hours which looks like :
I want to sum the hours_spent results for this week only and group the results by the created_by person. I have this query which returns the correct data for showing only results in this week :
SELECT staff_id, first_name, last_name, date_entered, `hours_spent` as total_hours FROM hours LEFT JOIN staff ON hours.created_by = staff.staff_id where yearweek(`date_entered`) = yearweek(curdate());
But when I add the SUM(hours_spent) as total_hours and group by staff_id like the example below I get 0 results.
SELECT staff_id, date_entered, first_name, last_name, SUM(`hours_spent`) as total_hours FROM hours LEFT JOIN staff ON hours.created_by = staff.staff_id group by staff_id having yearweek(`date_entered`) = yearweek(curdate());
I'm assuming it's not working because the Having part of my statement doesn't return individual rows of dates so it breaks.
I feel like I am doing this the hard way. Should I be trying to run a second summing query on the results of the first query rather than combine it all into one (I was hoping for cleanliness). Or should I be using a subquery to filter out the dates that aren't this week then group the totals if so how could I accomplish this?
I got what I was expecting with :
SELECT staff.first_name,staff.last_name, sum(hours_spent)
FROM hours
LEFT JOIN staff ON hours.created_by = staff.staff_id
WHERE yearweek(date_entered,1) = (yearweek(curdate(),1)-1)
GROUP BY created_by
Related
I need to compute employees' monthly salaries based on meetings attended, deductions and bonuses given;
Employees have different pay per meeting based on their job position.
The solution is:
salary = (Pay_per_minute * meetings_attended) + bonuses - deductions ;
I have four tables:
Jobs: Id, title, pay_per_meeting
Employees: Id, Name, job_id
Bonuses: Id, amount, employee_id, date
Deductions: Id, amount, employee_id, date
Meetings: Id, employee_id, date
SELECT
COUNT(meetings.employee_id) as meetings_attended,
COUNT(deductions.amount) as debt,
COUNT(bonuses.amount) bonus,
(SELECT jobs.pay_per_attendance from jobs where jobs.id = (select job_id from employees where id=meetings.employee_id)) as pay,
((meetings_attended * pay) + bonus - debt) as salary
FROM meetings
JOIN deductions ON deductions.employee_id = meetings.employee_id
JOIN bonuses ON bonuses.employee_id = meetings.employee_id
WHERE meetings.employee_id = 1
GROUP BY MONTH(meetings.date), MONTH(deductions.date), MONTH(bonuses.date)
The above query returns many incorrect values whenever i remove the salary line but gives error of unknown column pay, meetings_attended, debt and bonus, am sure something is wrong with the grouping but i can't just see it.
You can't refer to column aliases in the same select list as they're defined, you need to refer to the underlying column. And a subquery can't access an aggregate calculated in the main query. You need to repeat the aggregate expression, or move everything into a subquery and do the calculation with it in an outer query.
Also, all your COUNT() expressions are going to return the same thing, since they're just counting rows (I assume none of the values can be NULL). You probably want COUNT(DISTINCT <column>) to get different counts, and you need to use a column that's unique, so they should be the primary key column, e.g. COUNT(DISTINCT deductions.id).
Another problem is that when you try to sum and count values when you have multiple joins, you end up with a result that's too high, because rows get duplicated in the cross product of all the tables. See Join tables with SUM issue in MYSQL. The solution is to calculate the sums from each table in subqueries.
SELECT m.month, m.meetings_attended, d.debt, b.bonus,
m.meetings_attended * j.pay_per_meeting + b.amount - d.amount AS salary
FROM (
SELECT MONTH(date) AS month, COUNT(*) AS meetings_attended
FROM meetings
WHERE employee_id = 1
GROUP BY month) AS m
JOIN (
SELECT MONTH(date) AS month, COUNT(*) AS bonus, SUM(amount) AS amount
FROM bonuses
WHERE employee_id = 1
GROUP BY month) AS b ON m.month = b.month
JOIN (
SELECT MONTH(date) AS month, COUNT(*) AS debt, SUM(amount) AS amount
FROM deductions
WHERE employee_id = 1
GROUP BY month) AS d ON m.month = d.month
CROSS JOIN employees AS e
JOIN jobs AS j ON j.id = e.job_id
WHERE e.employee_id = 1
I am having a problem getting data from a large amount MySQL database.
With the below code it is ok to get the list of 10K patients and 5K appointments which is our test server.
However, on our live server, the number of patients is over 100K and the number of appointments is over 300K and when I run the code after a while it gives 500 error.
I need the list of the patients whose patient_treatment_status is 1 or 3 and has no appointment after one month from their last appointment. (The below code is working for small amount of patients and appointments)
How can I optimise the first database query so there will be no need the second database query in the foreach loop?
<?php
ini_set('memory_limit', '-1');
ini_set('max_execution_time', 0);
require_once('Db.class.php');
$patients = $db->query("
SELECT
p.id, p.first_name, p.last_name, p.phone, p.mobile,
LatestApp.lastAppDate
FROM
patients p
LEFT JOIN (SELECT patient_id, MAX(start_date) AS lastAppDate FROM appointments WHERE appointment_status = 4) LatestApp ON p.id = LatestApp.patient_id
WHERE
p.patient_treatment_status = 1 OR p.patient_treatment_status = 3
ORDER BY
p.id
");
foreach ($patients as $row) {
$one_month_after_the_last_appointment = date('Y-m-d', strtotime($row['lastAppDate'] . " +1 month"));
$appointment_check = $db->single("SELECT COUNT(id) FROM appointments WHERE patient_id = :pid AND appointment_status = :a0 AND (start_date >= :a1 AND start_date <= :a2)", array("pid"=>"{$row['id']}","a0"=>"1","a1"=>"{$row['lastAppDate']}","a2"=>"$one_month_after_the_last_appointment"));
if($appointment_check == 0){
echo $patient_id = $row['id'].' - '.$row['lastAppDate'].' - '.$one_month_after_the_last_appointment. '<br>';
}
}
?>
First off, this subquery likely does not do what you think it does.
SELECT patient_id, MAX(start_date) AS lastAppDate
FROM appointments WHERE appointment_status = 4
Without a GROUP BY clause, that subquery will simply take the maximum start_date of all appointments with appointment_status=4, and then arbitrarily pick one patient_id. To get the results you want you'll need to GROUP BY patient_id.
For your overall question, try the following query:
SELECT
p.id, p.first_name, p.last_name, p.phone, p.mobile,
LatestApp.lastAppDate
FROM
patients p
INNER JOIN (
SELECT patient_id,
MAX(start_date) AS lastAppDate
FROM appointments
WHERE appointment_status = 4
GROUP BY patient_id
) LatestApp ON p.id = LatestApp.patient_id
WHERE
(p.patient_treatment_status = 1
OR p.patient_treatment_status = 3)
AND NOT EXISTS (
SELECT 1
FROM appointments a
WHERE a.patient_id = p.patient_id
AND a.appointment_status = 1
AND a.start_date >= LatestApp.lastAppDate
AND a.start_date < DATE_ADD(LatestApp.lastAppDate,INTERVAL 1 MONTH)
)
ORDER BY
p.id
Add the following index, if it doesn't already exist:
ALTER TABLE appointments
ADD INDEX (`patient_id`, `appointment_status`, `start_date`)
Report how this performs and if the data appears correct. Provide SHOW CREATE TABLE patient and SHOW CREATE TABLE appointments for further assistance related to performance.
Also, try the query above without the AND NOT EXISTS clause, together with the second query you use. It is possible that running 2 queries may be faster than trying to run them together, in this situation.
Note that I used an INNER JOIN to find the latest appointment. This will result in all patients that have never had an appointment to not be included in the query. If you need those added, just UNION the results those found by selecting from patients that have never had an appointment.
I have a MySQL table from which I want to extract attendance information(Student Id, course/subject for attendance, date range,whether the student was present or not). I have written the following query:
SELECT
COUNT(a_id),
(
SELECT COUNT(*) FROM attendance
WHERE state = 'present'
AND `dater` BETWEEN '$a' AND '$b'
) AS Count,
stud_id
FROM attendance
WHERE
stud_id =(SELECT id FROM users WHERE NAME = '$stud')
Which is giving me the correct results, but when I change the student,its not giving me the correct count for the days recorded for present. Not mention that I have not yet added the course parameter into the query
The MySQL table is as follows:
I need help for the query to return the desired results(Count the accurate days present for each student, as well as adding the course parameter into the query so that the query will look for attendance records for a specific course, for a specific student, for a specified date range).
Looks like you want to seperate your queries:
Select (select count(*) from <database>.attendance where state = 'present' AND (dater between '$a' and '$b') AND name=(SELECT id FROM users WHERE NAME = '$stud')) as present, (select count(*) from <database>.attendance where state = 'absent' AND (dater between '$a' and '$b') AND name=(SELECT id FROM users WHERE NAME = '$stud')) as absent from <database>.attendance WHERE stud_id =(SELECT id FROM users WHERE NAME = '$stud');
try this :)
Resolved it using JOIN as follows:
SELECT u.id, a.stud_id, a.course_id, count(*) FROM attendance a
JOIN users u ON u.id=a.stud_id
JOIN courses c ON c.c_id=a.course_id
WHERE a.state='present' and dater between '2017-09-01' and '2017-09-14'
GROUP BY a.stud_id, a.course_id;
Thanks for your help.
I have one table with a list of number of sales per month against product code and another with a list of months that can extend before or after the months that had a sale in. I need to results to show 0 sales if there were no sales in the month and for the cumulative to add this up. I have tried using case and if and getting it to put 0 if sales.sales was null but this did not work and I still just had blanks.
create table summary as (SELECT
q1.productid As productid,
q1.date AS Month_View,
q1.sales AS Monthly_Units_Sold,
(#runtot_sales := #runtot_sales + q1.sales) AS Cumulative_Sales
FROM
(SELECT
sales.productid,
dates.date,
if(sales.date is null,0,sales.sales) as sales
from
dates
left join sales on dates.date = sales.date
where
sales.productid = '$input1'
group by dates.date
ORDER BY date) AS q1);
";
Try COALESCE() function to return the first non-NULL value of a list Also see demo here
CREATE TABLE summary AS
(SELECT
q1.productid AS productid,
q1.date AS Month_View,
q1.sales AS Monthly_Units_Sold,
(
#runtot_sales := #runtot_sales + q1.sales
) AS Cumulative_Sales
FROM
(SELECT
sales.productid,
dates.date,
COALESCE(sales.sales, 0) AS sales
FROM
dates
LEFT JOIN sales
ON dates.date = sales.date
WHERE sales.productid = '$input1'
GROUP BY dates.date
ORDER BY DATE) AS q1) ;
MySQL COALESCE() function
You are misusing GROUP BY and therefore getting indeterminate results. See this: http://dev.mysql.com/doc/refman/5.5/en/group-by-extensions.html
If you're aggregating your items by product and date you probably want something like this.
SELECT sales.productid,
dates.date,
SUM(sales.sales) as sales
FROM dates
LEFT JOIN sales ON dates.date = sales.date
WHERE sales.productid = '$input1'
GROUP BY sales.productid, dates.date
ORDER BY /* i'm not sure what you're trying to do with the running total */
Note that SUM(sales.sales) handles the NULL values from your LEFT JOIN correctly. If the date doesn't join a sales row then sales.sales will be NULL.
If you're trying to do a month-by-month summary you need more logic than you have. See this writeup: http://www.plumislandmedia.net/mysql/sql-reporting-time-intervals/
I have a search page for my users and one of the things I want them to be able to do is search items in our DB using a time range.
Currently, my query looks like this:
select ****
from ****
where created_on >='{$errTimeDayOne}' and created_on <= '{$errTimeDayTwo}'
So, say $errTimeDayOne = 2013-03-24 00:00:00 and $errTimeDayTwo = 2013-04-01 01:00:00 it will search between that range. I'm trying to search between 00:00:00 - 01:00:00 through dates 2013-03-24 - 2013-04-01. Any thoughts or suggestions?
Update:
My query now looks like this:
SELECT call_id, priority_name, caller_name,
cust_name, cust_rep, caller_dept,
DATE_FORMAT(created_on , '%Y-%m-%d') AS created_on,
DATE_FORMAT(updated_on, '%Y-%m-%d') AS updated_on,
source_name, created_by, cat_name, subcat_name
FROM calls INNER JOIN categories ON calls.cat_id =
categories.cat_id INNER JOIN ticket_priority ON
calls.ticket_priority = ticket_priority.priority_id
INNER JOIN ticket_source ON calls.ticket_source =
ticket_source.source_id LEFT OUTER JOIN subcategories
ON calls.subcat_id = subcategories.subcat_id WHERE
created_by = 'brett.little' AND (DATE(created_on)
between '2013-03-01' and '2013-08-01') AND
(TIME(created_on) between '1:00:00' and '16:00:00')
It is displaying results when I leave out '(TIME(created_on) between '1:00:00' and '16:00:00')'. Might this have anything to do with the date_format in the beginning of the query?
You'll have to enhance the clause to check the times and dates separately:
WHERE (DATE(created_on) BETWEEN '2013-03-24' and '2013-04-01')
AND (TIME(created_on) BETWEEN '00:00:00' AND '01:00:00')