find the last record in SQL query - mysql

This is actually part of a Massive SQL query, but I had this code working, and now I cant seem to figure out why its not finding the last appointment record.
What I am trying to do is join all the other appointments onto themselves to find out which one is the last one. And all my results appear to be null. But this is not correct because my data definitely have appointments. So I think there is something wrong with my select query. Any help would be great.
SELECT `animal_id`,
FROM_UNIXTIME(`lastappointment`.`appointmentdata_starttime`,'%D %M %Y') AS 'Last Appointment'
FROM `animal`
LEFT JOIN (
SELECT `lAppointment`.*
FROM `appointment` AS `lAppointment`
LEFT JOIN `appointment` AS `nlAppointment`
ON `nlAppointment`.`appointmentdata_starttime` <= NOW()
AND `nlAppointment`.`appointmentdata_starttime` > `lAppointment`.`appointmentdata_starttime`
AND `nlAppointment`.`appointmentdata_animal` = `lAppointment`.`appointmentdata_animal`
WHERE `lAppointment`.`appointmentdata_starttime` <= NOW()
AND `nlAppointment`.`appointment_id` IS NULL
) AS `lastappointment`
ON `animal_id` = `lastappointment`.`appointmentdata_animal`
WHERE `animaldata_active` IS NOT NULL
GROUP BY animal_id;

Using LEFT JOINs to get the MAX() is the slowest method you can think of. I recommend you to change the inner query like this (using GROUP BY):
SELECT `animal_id`,
FROM_UNIXTIME(`lastappointment`.`last_appointment`, '%D %M %Y') AS 'Last Appointment'
FROM `animal`
LEFT JOIN (
SELECT MAX(appointmentdata_starttime) AS last_appointment,
appointmentdata_animal
FROM appointment
WHERE appointmentdata_starttime <= NOW()
GROUP BY appointmentdata_animal
) AS `lastappointment`
ON `animal_id` = `lastappointment`.`appointmentdata_animal`
WHERE `animaldata_active` IS NOT NULL
-- GROUP BY animal_id; --BTW, this group_by is not needed

Related

Is it possible to LEFT JOIN a #variable table in SQL

Summary
I am attempting to LEFT JOIN on a filtered TABLE in SQL.
To do so I am first declaring a temporary table in an # variable, then attempting to join on it later.
Unfortunately I am not having much luck doing this, it massively speeds up my query when limiting to such a resultset.
Other Routes Tried
I initially was trying to conduct this on the WHERE, however this was preventing rows from occurring where there were no matching events (I am counting the events by intervals_days so I need to return regardless of whether there's matching in the other).
After I realised my mistake, I moved to the ON. I have not seen examples of this done, and I have a feeling doing a FIND_IN_SET here on a SELECT would not be performant?
QUERIES
The Initial Filter
DECLARE #filtered_events TABLE (id INT, eventable_id INT, eventable_type VARCHAR(255), occurred_at TIMESTAMP, finished_at TIMESTAMP)
INSERT INTO #filtered_events
SELECT
e.id, e.eventable_id, e.eventable_type, e.occurred_at, e.finished_at
FROM
units_events as ue
LEFT JOIN
events as e
ON
e.id = ue.event_id
WHERE
ue.unit_id
IN
(1,2,3);
Ignore the (1,2,3) here, these values are added to the query dynamically.
My Attempted Use Of It
SELECT
i.starts_at as starts_at,
i.ends_at as ends_at,
count(fe.id) as count
FROM
intervals_days as i
LEFT JOIN
#filtered_events as fe
ON
( fe.occurred_at >= starts_at AND fe.occurred_at < ends_at )
WHERE
( starts_at >= '15-01-2019' AND ends_at < NOW() )
GROUP BY
starts_at
ORDER BY
starts_at
DESC;
These queries are within one SQL document, one above the other with the terminating ; semicolon on each.
I expect this to output what my lower query outputs (a grouped resultset by the intervals_days rows)- however with the benefit of my LEFT JOIN query being conducted on a much smaller sample.
Sorry, no such syntax.
MySQL has no concept of arrays, either.
Nor can you use DECLARE outside of a Stored Routine.
As Shadow said in the comments, with MySQL I would use a subquery like this:
SELECT
i.starts_at as starts_at,
i.ends_at as ends_at,
count(fe.id) as count
FROM
intervals_days as i
LEFT JOIN
(
SELECT
e.id,
e.eventable_id,
e.eventable_type,
e.occurred_at,
e.finished_at
FROM units_events as ue
LEFT JOIN events as e
ON e.id = ue.event_id
WHERE ue.unit_id IN (1,2,3)
) fe
ON ( fe.occurred_at >= starts_at AND fe.occurred_at < ends_at )
WHERE ( starts_at >= '15-01-2019' AND ends_at < NOW() )
GROUP BY starts_at
ORDER BY starts_at
DESC;

MySQL grouping when using a sub query

I am trying to create a summary output to show a totals based on values in sub queries and then group the output by a label
Query looks like:
select c.name,
(select sum(duration) from dates d
inner join time t1 on d.time_id=t1.id
where d.employee=t.employee
and d.date >= now() - INTERVAL 12 MONTH) as ad,
(select sum(cost) from dates d
inner join time t1 on d.time_id=t1.id
where d.employee=t.employee
and d.date >= now() - INTERVAL 12 MONTH) as ac
FROM time t
inner join employees ee on t.employee=ee.employee
inner join centres c on ee.centre=c.id
where
ee.centre in (4792,4804,4834) group by c.centre
I want this to show me the ad and ac for each centre but instead it only shows values for ac for the last centre in the list and the rest show as zero
If I remove the group by then I get a list of all the entries but then it is not summarised in any way and I need that rollup view
A SQL statement that is returning a result that is unexpected is not a whole lot to go on.
Without a specification, helpfully illustrated with sample data and expected output, we're just guessing at the result that the query is supposed to achieve.
I think the crux of the problem is the value of t.employee returned for the GROUP BY, multiple detail rows with a variety of values for t.employee are getting collapsed into a single row for each value of c.centre, and the value of t.employee is from "some row" in the set. (A MySQL-specific non-standard extension allows the query to run without throwing an error, where other RDBMS would throw an error. We can get MySQL behavior more inline with the standard by including ONLY_FULL_GROUP_BY in `sql_mode. But that would just cause the SQL in the question to throw an error.)
Suggested fix (just a guess) is to derived ac and ad for each employee, before doing the GROUP BY, and then aggregating.
(I'm still suspicious of the joins to centre, not being included in the subqueries. Is employee the primary key or a unique key in employees? Is centre functionally dependent on employee? So many questions, too many assumptions.
My guess is that we are after the result returned by a query something like this:
SELECT c.name
, SUM(v.ad) AS `ad`
, SUM(v.ac) AS `ac`
FROM ( -- derive `ad` and `ac` in an inline view before we collapse rows
SELECT ee.employee
, ee.centre
, ( -- derived for each employee
SELECT SUM(d1.duration)
FROM time t1
JOIN dates d1
ON d1.time_id = t1.id
AND d1.date >= NOW() + INTERVAL -12 MONTH
WHERE d1.employee = t.employee
) AS `ad`
, ( -- derived for each employee
SELECT SUM(d2.cost)
FROM time t2
JOIN dates d2
ON d2.time_id = t2.id
AND d2.date >= NOW() + INTERVAL -12 MONTH
WHERE d2.employee = t.employee
) AS `ac`
FROM time t
JOIN employees ee
ON ee.employee = t.employee
WHERE ee.centre in (4792,4804,4834)
GROUP
BY ee.employee
, ee.centre
) v
LEFT
JOIN centres c
ON c.id = v.centre
GROUP
BY v.centre
, c.name

MYSQL select max date from joined tables

I have 2 tables which I want to join and retrieve some specific data. These are my tables.
tbl_user (reg_id, l_name, f_name, status)
tbl_payments (pay_id, reg_id, mem_plan, from_date, to_date, bill_no, payed_date)
What I need to do is select and view the users who have due payments. To do that I want to get the user details where "status=0" from tbl_user and join the 2 tables together and the conditions are to_date< current date, difference between [current date and the to_date] < 31 and filter by the Max value of to_date.
What I did so far gives me a result according to above mentioned conditions except it dosen't filter by the MAX(to_date). This is my query.
SELECT
A.reg_id,
A.f_name,
A.l_name,
B.mem_plan,
B.from_date,
Max(B.to_date) AS to_date,
B.bill_no,
B.payed_date
FROM
tbl_user A,
tbl_payments B
WHERE
A.status = 0
AND A.reg_id = B.reg_id
AND Date(Now()) >= Date(B.to_date)
AND Datediff(Date(Now()), Date(b.to_date)) < 31
GROUP BY
a.reg_id, b.mem_plan, b.from_date, b.bill_no, b.payed_date;
I'm not very familiar with MYSQL, So please someone tell me what I did wrong or if this query is not up to the standard.
Here are some sample data to work on.
tbl_user ( [M1111,Jon, Doe,0], [M1112,Jane,Doe,1],[M1113,Jony,Doe,0] )
tbl_payment ( [1,M1111,Monthly,2018-05-14,2018-06-14,b123,2018-05-14],[2,M1112,3Months,2018-02-03,2018-05-03,b112,2018-02-03],[3,M1113,Monthly,2018-06-14,2018-07-14,b158,2018-06-14],[4,M1111,Monthly,2018-06-15,2018-07-15,b345,2018-06-15],[5,M1113,Monthly,2018-06-06,2018-07-06,b158,2018-06-06],[6,M1111,Monthly,2018-07-05,2018-08-05,b345,2018-07-05] )
Assuming current date is 2018-07-17, The expecting result should be this
[M1111,Jon,Doe,Monthly,2018-06-15,2018-07-15,b345,2018-06-15],[M1113,Jony,Doe,Monthly,2018-06-14,2018-07-14,b158,2018-06-14]
Instead of that, my query gives me this.
[M1111,Jon,Doe,Monthly,2018-06-15,2018-07-15,b345,2018-06-15],[M1113,Jony,Doe,Monthly,2018-06-06,2018-07-06,b158,2018-06-06],
[M1113,Jony,Doe,Monthly,2018-06-14,2018-07-14,b158,2018-06-14]
I wrote another query which gives me the result set exactly as i want. But I'm not sure whether it's up to the standards. If someone can simplify this or make it better, appreciate very much.
SELECT A.reg_id,A.f_name,A.l_name,D.mem_plan,D.from_date,D.to_date,D.bill_no,D.payed_date
FROM tbl_user A
JOIN (SELECT B.reg_id,B.mem_plan,B.from_date,B.to_date,B.bill_no,B.payed_date
FROM tbl_payments B
JOIN (
SELECT reg_id, MAX(to_date) as to_date
FROM tbl_payments
WHERE DATE(NOW()) >= DATE(to_date) AND DATEDIFF(DATE(NOW()), DATE(to_date))<31
GROUP BY reg_id) C
ON B.reg_id = C.reg_id AND B.to_date= C.to_date) D
ON A.reg_id = D.reg_id
WHERE A.status=0;
I believe having won't work here and that your second query is about as good as it gets. I've condensed it a little here:
SELECT A.reg_id,f_name,l_name,mem_plan,from_date,to_date,bill_no,payed_date
FROM #tbl_user A
JOIN #tbl_payments B ON A.reg_id = b.reg_id
JOIN (
SELECT reg_id, MAX(to_date) as max_to_date
FROM #tbl_payments
WHERE DATE(NOW()) >= DATE(to_date) AND DATEDIFF(DATE(NOW()), DATE(to_date))<31
GROUP BY reg_id
) C ON B.reg_id = C.reg_id AND B.to_date= C.max_to_date
WHERE A.status=0;

MySQL Syntax Issue combining to working queries

I'm just starting to learn SQL, and managed to cobble together a couple of working queries, but then when I combine them I am getting a syntax error. The query throwing the error:
SELECT sca_ticket_status.name As Status, AVG(QueueTime)
FROM (SELECT DateDiff (created, now()) as 'QueueTime'
FROM sca_ticket as SubQuery
LEFT JOIN sca_ticket_status
ON sca_ticket.status_id = sca_ticket_status.id
GROUP BY name
ORDER BY sort
For reference, the two working queries that I am attempting to leverage are as follows:
SELECT sca_ticket_status.name As Status, COUNT(sca_ticket.ticket_id) AS Count
FROM sca_ticket
LEFT JOIN sca_ticket_status
ON sca_ticket.status_id = sca_ticket_status.id
WHERE sca_ticket.created between date_sub(now(),INTERVAL 1 WEEK) and now()
GROUP BY name
ORDER BY sort
SELECT AVG(QueueTime)
FROM (SELECT DateDiff (created, now()) as 'QueueTime'
FROM `sca_ticket`
WHERE `status_id` = 1) as SubQuery
Try closing your second select statement
SELECT sca_ticket_status.name As Status, AVG(QueueTime)
FROM (SELECT status_id, DateDiff (created, now()) as 'QueueTime'
FROM sca_ticket) q1
LEFT JOIN sca_ticket_status
ON q1.status_id = sca_ticket_status.id
GROUP BY name
ORDER BY sort
You will also need to expose the status_id column in your inner select list if you want to join on it later.
You do not need a subquery at all. This just slows down the processing in MySQL (the optimizer is not very smart; it materializes subqueries losing index information).
SELECT ts.name As Status, AVG(DateDiff(t.created, now()))
FROM sca_ticket t LEFT JOIN
sca_ticket_status ts
ON t.status_id = ts.id
GROUP BY ts.name
ORDER BY sort

SQL Query to Select Everything Except the Max Value

I have this rather complex query that grabs data from three tables, and now I want it to be even more complicated (Oh dear)!
I'd like the last posted feature to be displayed in it's own section of the page, and that's pretty easy by selecting the last entry in the table. However, for the complex query (the main page of the site), I'd like to be able to NOT have this feature displayed.
I'd like to union the following query to my previous query, but it isn't returning the correct results:
SELECT
features.featureTitle AS title,
features.featureSummary AS body,
features.postedOn AS dummy,
DATE_FORMAT( features.postedOn, '%M %d, %Y' ) AS posted,
NULL,
NULL,
staff.staffName,
features.featureID
FROM
features
LEFT JOIN staff ON
features.staffID = staff.staffID
WHERE features.postedOn != MAX(features.postedOn)
ORDER BY dummy DESC LIMIT 0,15
This query returns the following error:
MySQL error: #1111 - Invalid use of group function
Is there any way around this?
The max query needs to be in its own subquery, so your final SQL should be::
SELECT features.featureTitle AS title,
features.featureSummary AS body,
features.postedOn AS dummy,
DATE_FORMAT( features.postedOn, '%M %d, %Y' ) AS posted,
NULL,
NULL,
staff.staffName,
features.featureID
FROM
features
LEFT JOIN staff ON
features.staffID = staff.staffID
WHERE
features.postedOn != (select max(features.postedOn) from features)
the problem you have is that is that you need to find the max (latest) feature from the table, while going over each row, but MAX() is a group function - you have to group all rows to use it.
you can use a sub-select to get the id of the last feature:
WHERE featureId <> (SELECT featureId From features ORDER BY postedOn DESC LIMIT1)
there is a problem with this approach - the subselect is run for every row, but it is not that expensive.
You could also order by the PostedOn field in descending order and add OFFSET 1, which will display your results starting from the second row.
SELECT features.featureTitle AS title,
features.featureSummary AS body,
features.postedOn AS dummy,
DATE_FORMAT(features.postedOn, '%M %d, %Y') AS posted,
NULL,
NULL,
staff.staffName,
features.featureID
FROM
features
LEFT JOIN staff ON
features.staffID = staff.staffID
ORDER BY features.postedOn DESC
OFFSET 1;