I have a table rental:
rentalId int(11)
Customer_customerId int(11)
Vehicle_registrationNumber varchar(20)
startDate datetime
endDate datetime
pickUpLocation int(11)
returnLocation int(11)
booking_time timestamp
timePickedUp timestamp
timeReturned timestamp
and table payment:
paymentId int(11)
Rental_rentalId int(11)
amountDue decimal(10,2)
amountPaid decimal(10,2)
paymentDate timestamp
I run two group by functions, first one counts the number of reservations and sums the payments by day, this function only works as expected when having pickUpLocation` is omitted, otherwise it returns incorrect values. :
SELECT COUNT(rentalId) AS number_of_rentals, MONTH(booking_time) AS month,
`YEAR(booking_time) AS year,
CONCAT(DAY(booking_time), '-', MONTH(booking_time), '-',`
YEAR(booking_time) ) AS date, SUM(amountDue) AS total_value, SUM(amountPaid) AS
total_paid, `pickUpLocation`
FROM (`rental`)
JOIN `payment` ON `payment`.`Rental_rentalId` = `rental`.`rentalId`
GROUP BY DAY(booking_time)
HAVING `month` = 2
AND `year` = 2012
AND `pickUpLocation` = 1
ORDER BY `booking_time` desc
LIMIT 31
The second function is expected to sum the reservations and payments (both due and received) for the entire month, for a specific location:
SELECT COUNT(rentalId) AS number_of_rentals, MONTH(booking_time) AS month,
YEAR(booking_time) AS year, SUM(amountDue) AS total_value,
SUM(amountPaid) AS total_paid,
`pickUpLocation`
FROM (`rental`)
JOIN `payment` ON `payment`.`Rental_rentalId` = `rental`.`rentalId`
GROUP BY MONTH(booking_time)
HAVING `month` = 2
AND `year` = 2012
AND `pickUpLocation` = 1
ORDER BY `booking_time` desc
It works for some locations and doesn't work for others (returns correct set when there are many reservations, but when there are only few, it returns empty set). I use MySQL. Any help greatly appreciated.
You're doing an inner join between rental and payment which means you will only ever get rentals that have been paid for. If you want to find rentals without payment info too in your result, you need to use a LEFT JOIN instead of just an (inner) JOIN.
Note that that may result in NULLs in your result if there are no payments to account for, so you may have to adjust the output of your query using one of the control flow functions.
Edit: You're also GROUPing before your conditions, that will GROUP all rows for a month into one single row. Since the year and the PickupLocation may vary, you will get random values (of the ones available) in those two fields. HAVING will then filter on those random fields, leaving you with a possibly empty result set. WHERE on the other hand will see every row before GROUPing and do the right thing (tm) on a row to row basis, so the conditions should be put there instead.
(The same change should probably be done to your first, working, query)
Demo here.
You may need to push some conditions from HAVING to WHERE clause:
WHERE YEAR(booking_time) = 2012
AND MONTH(booking_time) = 2
AND `pickUpLocation` = 1
GROUP BY DAY(booking_time)
LIMIT 31
For a specific month, you don't even need the GROUP BY:
WHERE YEAR(booking_time) = 2012
AND MONTH(booking_time) = 2
AND `pickUpLocation` = 1
The above condition is not very good regarding performance:
WHERE YEAR(booking_time) = 2012
AND MONTH(booking_time) = 2
You should change it into:
WHERE booking_time >= '2012-02-01'
AND booking_time < '2012-03-01'
so the query can use an index on booking_time (if you have or you add one in the future) and so it doesn't call the YEAR() and MONTH() functions for every row of the table.
Related
My work is for an appointment system:
I have two tables:
Times(hour varchar);
Reservations(time varchar, date varchar);
Times table have all the times a store is open (as strings) from 8 to 6pm (08:00,08:30,09:00,etc..)
Reservation has the times reserved.
The store has 3 employees that can do an appointment simultaneously, so 3 client can reserve at 10:00am per example.
My goal is to return the list of times that aren't reserved but on one condition: If a time has been reserved less than 3 times it can still be reserved. I tried this query
SELECT `hour` FROM `times` WHERE `hour` NOT IN (SELECT `time` FROM `reservations` WHERE `date` = '$date' HAVING COUNT(`time`)>=3);
The problem is this returns null if there are no reserved times, but i cant understand why.. If the subquery returns null, the first query not in subquery(null) must return all the times in the Times table right? Its giving me empty rows... Anyone know why?
This query:
SELECT `time`
FROM `reservations`
WHERE `date` = '$date'
GROUP BY `time`
HAVING COUNT(*) >= 3
returns the list of times that are reserved under your condition.
So use a LEFT JOIN of Times to that query and return only the unmatched rows:
SELECT t.`hour`
FROM `times` t LEFT JOIN (
SELECT `time`
FROM `reservations`
WHERE `date` = '$date'
GROUP BY `time`
HAVING COUNT(*) >= 3
) r on r.time = t.`hour`
WHERE r.time IS NULL
If subquery returns null, then IN operator will always give you null as result. If you want to get results ensure you don't have nulls in subquery or make subquery return empty set. I tried it on SQL server.
I am trying to pickup Account with End Date NULL first then latest date if there are more accounts with the same item
Table Sample
Result expected
Select distinct *
from Sample
where End Date is null
Need help to display the output.
Select *
from Sample
order by End_Date is not null, End_date desc
According to sample it seems to me you need union and not exists corelate subquery
select * from table_name t where t.enddate is null
union
select * from table_name t
where t.endate=( select max(enddate) from table_name t1 where t1.Item=t.Item and t1.Account=t.Account)
and not exists ( select 1 from table_name t2 where enddate is null and
t1 where t2.item=t.item
)
SELECT * FROM YourTable ORDER BY End_Date IS NOT NULL, End_Date DESC
In a Derived Table, you can determine the end_date_to_consider for every Item (using GROUP BY Item). IF() the MIN() date is NULL, then we consider NULL, else we consider the MAX() date.
Now, we can join this back to the main table on Item and the end_date to get the required rows.
Try:
SELECT t.*
FROM
Sample AS t
JOIN
(
SELECT
Item,
IF(MIN(end_date) IS NULL,
NULL,
MAX(end_date)) AS end_date_to_consider
FROM Sample
GROUP BY Item
) AS dt
ON dt.Item = t.Item AND
(dt.end_date_to_consider = t.end_date OR
(dt.end_date_to_consider IS NULL AND
t.end_date IS NULL)
)
First of all you should state clearly which result rows you want: You want one result row per Item and TOU. For each Item/TOU pair you want the row with highest date, with null having precedence (i.e. being considered the highest possible date).
Is this correct? Does that work with your real accounts? In your example it is always that all rows for one account have a higher date than all other account rows. If that is not the case with your real accounts, you need something more sophisticated than the following solution.
The highest date you can store in MySQL is 9999-12-31. Use this to treat the null dates as desired. Then it's just two steps:
Get the highest date per item and tou.
Get the row for these item, tou and date.
The query:
select * from
sample
where (item, tou, coalesce(enddate, date '9999-12-31') in
(
select item, tou, max(coalesce(enddate, date '9999-12-31'))
from sample
group by item, tou
)
order by item, tou;
(If it is possible for your enddate to have the value 9999-12-31 and you want null have precedence over this, then you must consider this in the query, i.e. you can no longer simply use this date in case of null, and the query will get more complicated.)
My db info is
I wont to get for each weekday unique record if employee_id has value that record, else employee_id is Null.
I try this query
SELECT
*
FROM
`calendar`
WHERE `employee_id` = 1
OR `employee_id` IS NULL
GROUP BY `weekday`
result is
But I expect
Can you help me? Thanks
Try this out:
SELECT
b.calendar_id,
a.employee_id,
a.weekday
FROM
(SELECT
`weekday`,
MAX(employee_id) AS employee_id
FROM
calendar
WHERE employee_id = 1
OR employee_id IS NULL
GROUP BY `weekday`) a
LEFT JOIN calendar b
ON a.weekday = b.weekday
AND a.employee_id = b.employee_id ;
Let me know in case of any clarifications.
The problem is, that you have columns in the list after SELECT, that don't appear in the GROUP BY expression nor have an aggregate function applied to them. MySQL, in contrast to most other DBMS, allows this in older versions (or with certain settings) but the results are likely to be garbage.
Fix this by including the columns in the GROUP BY expression or by applying an aggregate function on them. For example:
SELECT max(`employee_id`),
`weekday`
FROM `calendar`
WHERE `employee_id` = 1
OR `employee_id` IS NULL
GROUP BY `weekday`;
Note the max() aggregate function on employee_id.
I am trying to query a table. There are 3 important fields: attendant_id, client_id, and date.
Each time an attendant works with a client, they add an entry which includes their id, the client's id, and the date. Occasionally, an attendant will work with more than one client on the same day. I would like to capture when this happens. Here is what I have so far:
SELECT *
FROM timesheet_lines tsl1
WHERE EXISTS
(
SELECT *
FROM timesheet_lines tsl2
WHERE tsl1.date = tsl2.date
AND tsl1.attendant_id = tsl2.attendant_id
AND tsl1.client_id <> tsl2.client_id
AND tsl1.date between '2014-04-01' AND '2014-06-30'
LIMIT 2,5
)
I only want to display results where an attendant worked with at least 2 different clients. I don't expect it to be possible to have more than 5 on a single day. This is why I am using LIMIT 2,5.
I am also only interested in April through June of this year.
I think I may have the right syntax, but the query seems to be taking forever to run. Is there a faster query? There should be only about 42000+ entries all together for this particular date range. I am not expecting to get more than about 500-600 results that meet the criteria.
I ended up using the following:
create TEMPORARY table tempTSL1
(date1 date, start1 time, end1 time, attend1 varchar(50), client1 varchar(50), type1 tinyint);
insert into tempTSL1(date1, start1, end1, attend1, client1, type1)
select date, start_time, end_time, attendant_id, client_id, type
from timesheet_lines
WHERE
timesheet_lines.date BETWEEN '2014-04-01' AND '2014-06-30'
and timesheet_lines.type IN (1,2,5,6);
create TEMPORARY table tempTSL2
(date2 date, start2 time, end2 time, attend2 varchar(50), client2 varchar(50), type2 tinyint);
insert into tempTSL2(date2, start2, end2, attend2, client2, type2)
select date, start_time, end_time, attendant_id, client_id, type
from timesheet_lines
WHERE
timesheet_lines.date BETWEEN '2014-04-01' AND '2014-06-30'
and timesheet_lines.type IN (1,2,5,6);
SELECT *
FROM tempTSL1
WHERE (attend1,date1) IN (
SELECT attend2
,date2
FROM tempTSL2 tsl2
GROUP BY attend2
,date2
HAVING COUNT(date2) > 1
)
GROUP BY attend1
,client1
,date1
HAVING COUNT(client1) = 1
ORDER BY date1,attend1,start1
You are likely making it much more complex than it needs to be. Try something like this:
SELECT attendant_id
,client_id
,date
FROM timesheet_lines
WHERE (attendant_id,date) IN (
SELECT attendant_id
,date
FROM timesheet_lines tsl1
GROUP BY attendant_id
,date
HAVING COUNT(date) > 1
)
GROUP BY attendant_id
,client_id
,date
HAVING COUNT(client_id) = 1
The subquery returns results only of attendants performing multiple activities on the same date. The top query will pull from the same table, matching the attendant and dates of activity, and filter the result set to items where there is only 1 client in the grouping. Example:
attendant_id client_id date
1 A 2014-01-01
1 B 2014-01-01
2 C 2014-01-01
2 D 2014-01-02
Will return:
attendant_id client_id date
1 A 2014-01-01
1 B 2014-01-01
Untested, but I think it should be in line with what you are looking for, assuming the following two statements are true:
You are not trying to capture two different attendants working the same client on the same day
An attendant can only perform one activity per client per day
If the second point is not true, then you will need to incorporate additional fields into the subquery (such as an activity_id or something).
Hope this helps.
Employees
EmpID : int(10)
Firstname: varchar(100)
Lastname: varchar(100)
HireDate: timestamp
TerminationDate: timestamp
AnnualReviews
EmpID: int(10)
ReviewDate: timestamp
What is query that returns each employee and for each row/employee include the greatest number of employees that worked for the company at any time during their tenure and the first date that maximum was reached.
So far, this is my query:
select *, (select count(empid) from employees where terminationdate between t.hiredate and t.terminationdate)
from employees as t
group by empid
What you have is close.
But there's more work to do.
We'd to work out the conditions that determine how many employees were "working" at any point in time (i.e. at a given timestamp value.) The condition I'd check:
HireDate <= timestamp < TerminationDate
We'd need to extend that comparison, so that a NULL value for TerminationDate would be handled like it were a point in time after the timestamp value. That's easy enough to do.)
HireDate <= timestamp AND ( timestamp < TerminationDate OR TerminationDate IS NULL
So, something like this:
SELECT COUNT(1)
FROM Employees e
WHERE ( :timestamp >= e.HireDate )
AND ( :timestamp < e.TerminationDate OR e.TerminationDate IS NULL)
That "count" value would remain the same, and would only change for a "hire" or "terminate" event.
If we got a distinct list of all timestamps for all "hire" and "terminate" events, we could get the number of employees at that point in time.
So, this query would give us the employee count every time the employee count might change:
SELECT t.ts AS `as_of`
, COUNT(1) AS `employee_count`
FROM Employees e
JOIN ( SELECT t.TerminationDate AS ts
FROM Employees t
WHERE t.TerminationDate IS NOT NULL
GROUP BY t.TerminationDate
UNION
SELECT h.HireDate AS ts
FROM Employees h
WHERE h.HireDate IS NOT NULL
GROUP BY h.HireDate
) t
ON ( t.ts >= e.HireDate )
AND ( t.ts < e.TerminationDate OR e.TerminationDate IS NULL)
GROUP BY t.ts
We could use that result (as an inline view) and join that to particular Employee, and get just the rows that have an as_of timestamp that matches the period of employment for that employee. Then just pulling out the maximum employee_count. It wouldn't be difficult to identify the earlier of multiple as_of dates, if that maximum employee_count occurred multiple times.
(The wording of the question leaves open a question, the "earliest date" ever that the employee count met or exceeded the maximum that occurred during an employees tenure, or just the earliest date within the employees tenure that the maximum was reached. It's possible to get either result.)
That's just one way to approach the problem.