I am aware of the slow query log but isnt this just for queries that exceed the maximum execution time? My question is slightly different (i think).
I am running the below query and to me it doesnt seem like it should present a problem. But when i run it, the "Running" time is over 20 seconds and then it sits there "Fetching" for ages! To the point where i have to stop it due to DB performance issues.
Can anyone explain where i am going wrong here. Coding newbie so please be gentle.
SELECT
t1.oid,
(SELECT
DATE_FORMAT(t2.date,
'%d/%m/%y') AS 'AVF Date'
FROM
t2
WHERE
t1.oid = t2.fk_oid
AND t2.type = '-25'
AND YEAR(t2.date) BETWEEN 2005 AND 2014
ORDER BY t2.date ASC
LIMIT 1) AS 'AVF Date',
t2.site
FROM
t1
left join t2 on t1.oid=t2.fk_oid
UPDATE - Ok so what i need is as follows. We have a patient database and these patients have procedures recorded on this database. I am trying to return the patient oid (t1.oid) along with the FIRST procedure that they had (if it was between 2005 and 2014) and where they had it (t2.date and t2.site respectively), but only if the procedure is of a particular type (t2.type = '-25')
So basically. All the patients who had an AVF between 2005 and 2014 along with the "site" of the AVF (which in this case is left arm/right arm etc.)
I hope this makes it a bit clearer.
Thanks in advance.
Mikey
I think you can use only the join without the subquery in the select, have a try:
SELECT t1.oid, DATE_FORMAT(t2.date,'%d/%m/%y'),t2.site
FROM table1 AS t1
LEFT JOIN table2 AS t2
ON t1.oid = t2.fk_oid
WHERE
t2.type = '-25'
AND YEAR(t2.date) BETWEEN 2005 AND 2014
ORDER BY t2.date ASC
LIMIT 1;
Ps: I haven't tested the code.
SELECT a.fk_oid AS oid,
DATE_FORMAT(a.first_date, '%d/%m/%y') AS 'AVF Date',
GROUP_CONCAT(b.site) AS "site(s)"
FROM
( SELECT fk_oid,
MIN(date) AS first_date
FROM site
WHERE date > CURDATE() - INTERVAL 10 YEAR
AND date <= CURDATE()
AND t2.type = -25
GROUP BY fk_oid
) AS a
JOIN site AS b
WHERE b.fk_oid = a.fk_oid
AND b.date = a.first_date ;
The inner query finds the first_date for each oid.
The outer query gets the site, but further assumes there might have been multiple sites for a given oid and date.
INDEX(type, date) is needed for performance
INDEX(fk_oid, date) is needed for performance
I assumed type is a number, not a string.
I rewrote the date comparison to imply "in the last 10 years". If there are no rows for the future, get id of AND date <= CURDATE().
Since you seem to need only the oid, I got rid of t1. If you need more fields from t1, then add JOIN t1 WHERE t1.oid = a.fk_oid and reference the fields needed.
(I am deleting my other Answer.)
Related
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
I have a valid MySQL Query that selects the latest occupancy percentage of a table from each community entered in my DB, but it seems to be scanning the entire DB of entries as the lookup time takes roughly 3-4 seconds.
With the details provided in the query below, can someone provide me with a faster/better way to lookup the latest timestamp field for each community? - I need the query to select every community entered, with the latest timestamp, but the limit for each community selected should be 1 (meaning community named "Test Community" will have possibly hundreds of submissions but I need the latest entered Timestamp selected, along with the same selection for every community entered in the table)
SELECT t1.reportID, t1.communityID, t1.region, t1.percentOccupied,
t1.TIMESTAMP, Communities.fullName
FROM NightlyReports t1
INNER JOIN Communities On t1.communityID = Communities.communityID
WHERE t1.TIMESTAMP = ( SELECT MAX( TIMESTAMP ) FROM NightlyReports WHERE
t1.communityID = NightlyReports.communityID )
AND t1.region = 'GA' ORDER BY percentOccupied DESC
In my experience, correlated subqueries often have rather poor performance; try this instead:
SELECT t1.reportID, t1.communityID, t1.region, t1.percentOccupied
, t1.TIMESTAMP, Communities.fullName
FROM NightlyReports AS t1
INNER JOIN Communities ON t1.communityID = Communities.communityID
INNER JOIN (
SELECT communityID, MAX( TIMESTAMP ) AS lastTimestamp
FROM NightlyReports
WHERE region = 'GA'
GROUP BY communityID
) AS lastReports ON t1.communityID = lastReports.communityID
AND t1.TIMESTAMP = lastReports.lastTimestamp
WHERE t1.region = 'GA'
ORDER BY percentOccupied DESC
Your query is fine. For this query (which is rewritten just a bit):
SELECT nr.reportID, nr.communityID, nr.region, nr.percentOccupied,
nr.TIMESTAMP, c.fullName
FROM NightlyReports nr INNER JOIN
Communities c
ON nr.communityID = c.communityID
WHERE nr.TIMESTAMP = (SELECT MAX(nr2.TIMESTAMP)
FROM NightlyReports nr2
WHERE nr.communityID = nr2.communityID
) AND
nr.region = 'GA'
ORDER BY percentOccupied DESC;
You want indexes on:
NightlyReports(region, timestamp, communityid)
NightlyReports(communityid, timestamp)
Communities(communityID) (this may already exist)
The correlated subquery is not per se a problem.
Relevant table structure is:
id(INT), reference_id(INT), status(VARCHAR(20)), date(TIMESTAMP)
This record is updated once per day.
Problem:
I want to display each record where the status field, which is static most of the time, is different to the one on the preceeding day, thus being able to show the date a status changed and display a history.
How?
Pseudo:
Show * where status for today != status for yesterday
Early logic, but my limited knowledge of mysql is causing me to hit a wall.
SELECT * from table_name WHERE DATE(date) = CURDATE()
UNION
SELECT table_name.status AS yesterday_status WHERE DATE(date) = CURDATE() - 1
I was then going to compare them both in perl, which is not so efficient so I'm hoping somebody can share some enlightenment...
Here is my approach. It is not exactly what you were asking for, but if maybe you will like my solution.
http://sqlfiddle.com/#!9/896b4/7
SELECT
today.*,
yesterday.*
FROM t_status today
LEFT JOIN t_status yesterday
ON DATE(yesterday.date)<DATE(today.date)
AND yesterday.reference_id = today.reference_id
AND yesterday.status != today.status
WHERE DATE(today.date) = CURDATE()
ORDER BY yesterday.date DESC
LIMIT 1
;
UPDATE Or probably I misunderstood your goal, and you need only compare yesterdays status, not all past statuses. If so you can try this INNER JOIN:
http://sqlfiddle.com/#!9/dd61f1/4
SELECT
today.*,
yesterday.*
FROM t_status today
INNER JOIN t_status yesterday
ON DATE_ADD(CURDATE(),INTERVAL -1 DAY) = DATE(yesterday.date)
AND yesterday.reference_id = today.reference_id
AND yesterday.status != today.status
WHERE DATE(today.date) = CURDATE();
You are welcome if any questions.
The below query shows the records along with the previous day's record (I assumed that reference_id is also a join citeria, if not, just remove it)
select t1.*, t2.* from table t1
left join table t2 on t1.reference_id=t2.reference_id and date(t1.date)-1=date(t2.date)
You can even filter it further to display only those pairs, where the previous day's status is different (note that the join changes from left to inner, since if there is no previous day's record, that's not a change is status):
select t1.*, t2.* from table t1
inner join table t2 on t1.reference_id=t2.reference_id and date(t1.date)-1=date(t2.date) and t1.status<>t2.status
I think query should be like
select * from tablename where status <> (select * from tablename where date=Sysdate-1)
Sysdate is use for System date.
I have a table which contains all orders, i'm trying to separate orders by hour.
if there is no record for an specific hour, the query will ignore that hour, but what i'm trying to achieve is to report'0' for that hour.
I also joined the table with a temporary table containing all hours.
SELECT sum(orders.price), hour(orders.time) as hour
FROM orders
RIGHT JOIN dummy_time as dummy
ON hour(orders.time) = dummy.time
WHERE state = 1
AND (date(orders.time) = '2014-06-17' or orders.time is null)
GROUP BY hour
You can view my query in SQLFiddle
To get all rows from dummy_time, move your conditions from your WHERE to your RIGHT JOIN. Also, select the hour from dummy.time so you will get all hours.
Use COALESCE to get values of 0 where an order doesn't have records.
SELECT COALESCE(sum(orders.price),0), dummy.time as hour
FROM orders
RIGHT JOIN dummy_time as dummy
ON hour(orders.time) = dummy.time
AND orders.state = 1
AND orders.time BETWEEN '2014-06-17 00:00:00' AND '2014-06-17 23:59:59'
GROUP BY dummy.time
http://www.sqlfiddle.com/#!2/c7adb/2
The query plan for the query below looks worse than the one above but because you reported that the JOIN seems to be the main source of slowness it's worth a try. The query below reduces the set of rows before doing a JOIN.
SELECT
COALESCE(t1.orders_sum,0),
t2.time
FROM
(
SELECT
sum(orders.price) orders_sum,
hour(orders.time) orders_hour
FROM orders
WHERE orders.state = 1
AND orders.time BETWEEN '2014-06-17 00:00:00' AND '2014-06-17 23:59:59'
GROUP BY hour(orders.time)
) t1 RIGHT JOIN dummy_time t2 ON t1.orders_hour = t2.time
http://www.sqlfiddle.com/#!2/0775b/1
Also, make sure your tables are indexed
CREATE INDEX test_index1 ON orders (state,time);
CREATE INDEX test_index2 ON dummy_time (time);
Is this what you are looking for? It uses a case for when state=1 versus state=0 on whether to display sum orders or whether to display 0. If not please let me know your desired result.
I am attempting to find all entries in the 'archive' table that have the same workID as any rows where historyID is NULL, worktype = "WEST", and the completeDate at least four months old. I am using a sub query to complete this request. Is there a more efficient way? My query is below.
SELECT * from archive b WHERE b.workID IN
(SELECT a.workID FROM archive a, worktypes t
WHERE a.historyID IS NULL AND (t.worktype IN ('WEST') AND a.worktype = t.id)
AND a.completeDate >= DATE(NOW() - Interval 4 month));
Additional Info: The tables are related only by the worktype. Basically I'm using the worktypes table just so I can type 'WEST' instead of giving an id number so it's easier to understand the query at-a-glance. I added on to the query. I didn't copy and paste it all. Oops.
Thank you!
SELECT b.*
FROM archive b
INNER JOIN archive a
on b.workID = a.workID
AND a.historyID IS NULL
AND a.completeDate >= DATE(NOW() - Interval 4 month)
INNER JOIN worktypes t
ON a.worktype = t.id
AND t.worktype = 'WEST'
The problem with your query and the proposed solution is that we don't know how the tables relate to each other. What column do you have in archive that is also in worktypes?
Also you should avoid "IN" where possible, it is inefficient.
Yeah sure:
SELECT * from archive a LEFT JOIN worktypes t ON a.workID=t.workID WHERE a.historyID IS NULL AND t.worktype IN ('WEST') AND a.completeDate >= DATE(NOW() - Interval 4 month));
The LEFT JOIN will get all the entries in the first table and appends to them the equivalent columns from the matched second table.
Hopes this helps.