Can this mysql subquery be shortened with JOINs? - mysql

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.

Related

Combining 2 MySQL Queries from Different Databases

I've been trying my hand at this and I just keep getting an error or a query that hangs. Basically I have two database queries (one from each database) and I need to combine the results of the first into the second but also use the ID of the second query in the first...confusing!
The first is a simple query. Getting the number of topics approved and set it as "commentnumber". As you can see in the WHERE clause: It needs to use a.ID which would be from the second query.
Database 1
(SELECT (t.topic_posts_approved - 1)
FROM forum.bb_topics t, forum.bb_xpost xp
WHERE xp.wp_id = a.ID
AND t.topic_id = xp.topic_id) as 'commentnumber'
This is a query I've created to get 3 wordpress posts and sort them by a "weight". If I remove "commentnumber" (from the first query) it'll obviously work.
Database 2
SELECT a.post_author, a.id, b.pageviews, a.post_title, a.guid, c.meta_value, (b.pageviews * (c.meta_value + (commentnumber * 1.25))) AS 'weight'
FROM wordpress.wp_posts a, wordpress.wp_poppodyd b, wordpress.wp_postmeta c
WHERE a.ID = b.postid and (a.ID = c.post_id)
AND c.meta_key = 'thumbs_up'
AND (b.day >= NOW() - INTERVAL 2 DAY)
GROUP BY a.post_author
ORDER BY weight DESC
LIMIT 3
I've tried inner joining them but I either don't know what I'm doing or the query is just too much because a few variations I've tried just hangs until killed.
Any help would be massively appreciated!
I figured it out after sitting down for a couple of more hours with it.
As people have said, using the database.table.column name is the key.
Here is my end result in one query:
SELECT a.post_author, a.id, b.pageviews, a.post_title, a.guid, c.meta_value, t.topic_posts_approved, (b.pageviews * (c.meta_value + (t.topic_posts_approved * 1.25))) AS 'weight'
FROM wordpressdb.wp_posts a, wordpressdb.wp_poppodyd b, wordpressdb.wp_postmeta c, forumdb.bb_topics t, forumdb.bb_xpost xp
WHERE a.ID = b.postid and (a.ID = c.post_id) and (a.ID = xp.wp_id) and (t.topic_id = xp.topic_id)
AND c.meta_key = 'thumbs_up'
AND (b.day >= NOW() - INTERVAL 2 DAY)
GROUP BY a.post_author
ORDER BY weight DESC
LIMIT 3
I think you can create a new table and use both the database, select the columns that what you need and insert it to the new table then you can easily able to read that table.

Mysql - comparing two strings in the same table on different dates

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.

Query takes too long to fetch data

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.)

How do I change my mysql query to include rows for which the inner join cannot find a match?

This is the query I'm currently using:
SELECT
q1.item AS q1item,q1.store AS q1store,q1.marion AS q1marion,
q1.carbondale AS q1carbondale,q2.marion AS q2marion,q2.carbondale AS q2carbondale
FROM jdgower_items q1
INNER JOIN jdgower_items q2 ON q1.item=q2.item
AND q1.store_id=q2.store_id AND DATE(q1.datetime) = DATE(NOW())
AND DATE(q2.datetime) = DATE(NOW() - INTERVAL 1 DAY)
WHERE q1.store_id='$the_main_store_id' OR q1.competitor_id='$the_main_store_id'
ORDER BY q1.item ASC,q1.competitor_id ASC,q1.store DESC,q1.store_id ASC
Basically the idea is to get one item per row, with today's price and yesterday's price...along with all of the competitors I'm tracking with their respective prices. This gets me exactly what I want until I add a new competitor...if that competitor doesn't have an entry for yesterday, nothing returns for that competitor in the loop. I'm pretty sure it has something to do with the INNER JOIN on the line "AND DATE(q2.datetime) = DATE(NOW() - INTERVAL 1 DAY)" but I'm not sure how to remedy it. Thank you for any help in advance!
What about using LEFT JOIN instead of INNER JOIN? This way, you will have some results with NULL for q2 fields, but you'll have the rows.
Depending on what you seek try LEFT JOIN if yhat fail try RIGHT JOIN

MS Access - Query design help

I need to design a query that finds "duplicates" in MS Access table. They are not true duplicates, in that every field is not identical, but it is highly unlikely that a patient would be seen twice within 60 days, so 2 records in that timespan are likely duplicates.
The relevant columns in the table are:
id integer autoincrement
patientid text
proceduredate date/time
I want to produce a list of patientid where the proceduredate is within 60 days of each other. I was able to find a list of all "duplicates" with the following query:
SELECT * FROM tblProcedures
WHERE patientid = ANY
(SELECT tblProcedures.patientid
FROM tblProcedures
GROUP BY tblProcedures.patientid
HAVING COUNT(tblProcedures.patientid) > 1)
ORDER BY tblProcedures.patientid, tblProcedures.proceduredate DESC
But I'm not sure how to limit the results to records involving the same patientid where the proceduredate is within 60 days of a previous procedure.
One solution is to join the table to itself:
Select *
From tblProcedures As P1
Inner Join tblProcedures As P2
On P2.PatientId = P1.PatientId
And P2.Id <> P1.Id
Where Abs(DateDiff("d", P1.ProcedureDate, P2.ProcedureDate)) <= 60
Order By P1.PatientId, P1.ProcedureDate Desc
I think you want something like:
SELECT *
FROM tblProcedures P1
LEFT JOIN tblProcedures P2
ON P1.patientid = P2.patientid
AND P1.proceduredate < P2.proceduredate
AND DateDiff("d", P1.proceduredate, P2.Proceduredate) <= 60
WHERE P2.patientid IS NOT NULL
ORDER BY P1.patientid, P1.proceduredate DESC