Select all but rows which match across two tables in query - php

What I'm trying to do
I have two tables, one for events, and one that says whether each event id has been seen. I want to create a query that joins both tables on event.id and seen.eventid. However, I would only like to return the rows which have not been seen, ordered by how recent they are.
What I have right now
This is the query which works for returning all the events which have already been seen:
SELECT *
FROM `events`
JOIN `seen` ON (`seen`.`event_id`=`event`.`id`)
//This is not part of the question but I might as well paste the entire code
//WHERE `user_id`='34'
//AND (`meta_id`='45' OR `meta_id`='37' OR `meta_id`='43')
GROUP BY `event_id`
ORDER BY `event`.`date` DESC
How do I reverse the query so that those returned are the rows in event that are not matched by this query?

We don't know your table structures so I'm just guessing...
SELECT *
FROM events e
LEFT
JOIN seen s
ON s.event_id = e.id)
AND s.user_id=34
WHERE e.meta_id IN (45,37,43)
AND s.event_id IS NULL
GROUP
BY e.event_id
ORDER
BY e.date DESC

Related

How to retrieve nested join select statement data from query

I have the following query and am unsure of how to retrieve the 'note' from the nested join query.
This Left join selects the latest note for this customer, but I am not sure how to echo this data...
LEFT JOIN (SELECT note AS latestnote, timestamp, renewalid FROM renewal_note ORDER BY timestamp DESC LIMIT 1) AS n ON n.renewalid=renewal.id
Full Query:
SELECT renewal.id AS rid, renewal.personid, renewal.enddate, renewal.assettype, renewal.producttype, renewal.vrm, renewal.make, renewal.model, renewal.submodel, renewal.derivative, renewal.complete, person.forename, person.surname, person.company, appointment.id AS appid, appointment.renewalid,
(SELECT COUNT(complete) FROM renewal WHERE complete=1 && enddate BETWEEN '".$month_start."' AND '".$month_end."' && dealershipid='".$dealership_id."' && assettype='U' && producttype!='CH' && complete=1) AS renewedcount
FROM renewal
LEFT JOIN person ON person.id=renewal.personid
LEFT JOIN appointment ON appointment.renewalid=renewal.id
LEFT JOIN (SELECT note AS latestnote, timestamp, renewalid FROM renewal_note ORDER BY timestamp DESC LIMIT 1) AS n ON n.renewalid=renewal.id
WHERE enddate BETWEEN '".$month_start."' AND '".$month_end."' && renewal.dealershipid='".$dealership_id."' && assettype='U' && producttype NOT LIKE '%CH%'
ORDER BY enddate ASC
The Below is currently what is returned in each loop (which is working fine), and I can access as normal; $row['COLUMNNAME'].
rid
personid
enddate
assettype
used/new
producttype
vrm
make
model
submodel
derivative
complete
forename
surname
company
appid
renewalid
renewedcount
BUT I also need to be able to get the NOTE from renewal_note (the third LEFT JOIN).
I dont have issues with normal inner joins, But I have never created a query with subqueries, so struggling to echo this data out within the php loop.
(Just to point out that the only reason I am nesting a query is that I need only the latest note for each customer to be returned.)
I have tried $row['latestnote'], (with no success) and I am sure this is definitely not the way to access this data.
Could someone please point me in the right direction?
UPDATE
With updates from the comments, I have tried a much more simplified query (with ALL columns included so can't miss anything out) specifically targetting the subquery:
SELECT
*
FROM
renewal
LEFT JOIN(
SELECT
*
FROM
renewal_note
ORDER BY
TIMESTAMP
DESC
LIMIT 1
) AS n
ON
n.renewalid = renewal.id
But this still returns NULL for every column on the renewal_note.
90% of 'renewal' records have a note linked to them in the renewal_note table, but none showing.
renewal table has a unique primary key; ID.
renewal_note table links via column name: renewalid.
I think I have sussed this out.
Thanks to the commenters :)
Purely posting this for anyone else with similar questions/issues.
To get data from a LEFT JOIN(SELECT... statement; include the alias into the outer SELECT statement. Then you can use $row['columnname'] as normal.
LIMIT 1 on the subquery returned only 1 record for the entire query. Remove LIMIT 1 to show all results for the subquery dependant on your 'link' to the main query.
From what I have tested, ORDER BY seems to be working, but not sure if this is a fluke, or if it just sorting naturally by the primary key; ID

MySQL Join - Sorting data, grouping data

I have two tables:
twitterusers table
twittergrowth Table
I am trying to do JOIN these 2 tables, get all fields from twitteruser and selective fields from twittergrowth, then fetch only the last 3 rows from this data.
Expected Output:
Current Output:
I.e the rows are repeating. I want rows unique by ID or usernames, and the last set of timestamps. So it would be the last 3 rows, which has the most recent timestamps.
The code I could type scribble out is :
SELECT
t1.*,
t2.new_followers_count,
t2.new_friends_count,
t2.new_timestamp
FROM twitterusers t1
JOIN twittergrowth t2 on (t1.username=t2.username)
Searched quite few pages/sites, but cant really figure out how to do it. I would appreciate any help. :)
Additionally, I would like to get a LIMIT parameter added to the final result, so that I can paginate the full result.
First you need to find a maximum new_timestamp (latest) within groups of the same user_id and username in twittergrowth table. This is a classic group-wise maximum problem and the subquery tgmax does that. Then you need to join back the same table (tg this time) to get other columns that aren't in the group by clause of subquery and are not used in aggregate functions (like max()). These columns are new_followers_count and new_friends_count.
If you tried to put them in the select of subquery mysql would return values from an unspecified row from the same group and not necessarily the same as the one with the latest timestamp. This is explained here.
Once you get desired output for twittergrowth table the only thing left is to join twitterusers table to get all other columns.
SELECT tu.*, tg.new_followers_count, tg.new_friends_count, tg.new_timestamp
FROM twitterusers tu
JOIN twittergrowth tg
ON tu.user_id = tg.user_id AND tu.username = tg.username
JOIN
( SELECT tgg.user_id, tgg.username, max(tgg.new_timestamp) as latest_timestamp
FROM twittergrowth tgg
GROUP BY tgg.user_id, tgg.username ) tgmax
ON tg.user_id = tgmax.user_id AND tg.username = tgmax.username
AND tg.new_timestamp = tgmax.latest_timestamp
Note that this query would benefit from a composite index on (user_id,username,new_timestamp) in the twittergrowth table.
You need to group by to achieve your expected output.
GROUP BY id
To limit, or split results into pages, you can simply add LIMIT X,Y where X is the starting record and Y is the total number of records.
So a query to pull the expected results you want, but only the first 10 would be like so:
SELECT
t1.*,
t2.new_followers_count,
t2.new_friends_count,
t2.new_timestamp
FROM twitterusers t1
JOIN twittergrowth t2 on t1.username=t2.username
GROUP BY t1.id
LIMIT 0,10

Query doesn't fetch all results

I have a bit of a problem with a query I made, it doesn't fetch all the results there are. It should at least come up with 3 rows but only comes up with one:
SELECT * FROM n_news WHERE article_id IN
(SELECT DISTINCT article_id FROM nk_article_category
WHERE category_id IN (2,10,11,12))
ORDER BY article_featured DESC,article_published DESC
Anybody an idea what I'm doing wrong? MySQL didn't find any errors. Thanks in advance
Left is article_id and right = category_id in table nk_article_category. There is one result way up but didn't see the point to show the whole table
What I see per your posted query data image, your query returning correct result. the rows are all distinct (it will be considered duplicate if both article_id and category_id are same in multiple rows).
BTW, you can change your posted query to a INNER JOIN instead of using sub query like
SELECT t1.*
FROM n_news t1
INNER JOIN nk_article_category t2
ON t1.article_id = t2.article_id
AND t2.category_id IN (2,10,11,12)
ORDER BY t1.article_featured DESC, t1.article_published DESC;

MySql - Joining another table with multiple rows, inserting a query into a another query?

I've been racking my brain for hours trying work out how to join these two queries..
My goal is to return multiple venue rows (from venues) based on certain criteria... which is what my current query does....
SELECT venues.id AS ven_id,
venues.venue_name,
venues.sub_category_id,
venues.score,
venues.lat,
venues.lng,
venues.short_description,
sub_categories.id,
sub_categories.sub_cat_name,
sub_categories.category_id,
categories.id,
categories.category_name,
((ACOS( SIN(51.44*PI()/180)*SIN(lat*PI()/180) + COS(51.44*PI()/180)*COS(lat*PI()/180)*COS((-2.60796 - lng)*PI()/180)) * 180/PI())*60 * 1.1515) AS dist
FROM venues,
sub_categories,
categories
WHERE
venues.sub_category_id = sub_categories.id
AND sub_categories.category_id = categories.id
HAVING
dist < 5
ORDER BY score DESC
LIMIT 0, 100
However, I need to include another field in this query (thumbnail), which comes from another table (venue_images). The idea is to extract one image row based on which venue it's related to and it's order. Only one image needs to be extracted however. So LIMIT 1.
I basically need to insert this query:
SELECT
venue_images.thumb_image_filename,
venue_images.image_venue_id,
venue_images.image_order
FROM venue_images
WHERE venue_images.image_venue_id = ven_id //id from above query
ORDER BY venue_images.image_order
LIMIT 1
Into my first query, and label this new field as "thumbnail".
Any help would really be appreciated. Thanks!
First of all, you could write the first query using INNER JOIN:
SELECT
...
FROM
venues INNER JOIN sub_categories ON venues.sub_category_id = sub_categories.id
INNER JOIN categories ON sub_categories.category_id = categories.id
HAVING
...
the result should be identical, but i like this one more.
What I'd like to do next is to JOIN a subquery, something like this:
...
INNER JOIN (SELECT ... FROM venue_images
WHERE venue_images.image_venue_id = ven_id //id from above query
ORDER BY venue_images.image_order
LIMIT 1) first_image
but unfortunately this subquery can't see ven_id because it is evaluated first, before the outer query (I think it's a limitation of MySql), so we can't use that and we have to find another solution. And since you are using LIMIT 1, it's not easy to rewrite the condition you need using just JOINS.
It would be easier if MySql provided a FIRST() aggregate function, but since it doesn't, we have to simulate it, see for example this question: How to fetch the first and last record of a grouped record in a MySQL query with aggregate functions?
So using this trick, you can write a query that extracts first image_id for every image_venue_id:
SELECT
image_venue_id,
SUBSTRING_INDEX(
GROUP_CONCAT(image_id order by venue_images.image_order),',',1) as first_image_id
FROM venue_images
GROUP BY image_venue_id
and this query could be integrated in your query above:
SELECT
...
FROM
venues INNER JOIN sub_categories ON venues.sub_category_id = sub_categories.id
INNER JOIN categories ON sub_categories.category_id = categories.id
INNER JOIN (the query above) first_image on first_image.image_venue_id = venues.id
INNER JOIN venue_images on first_image.first_image_id = venue_images.image_id
HAVING
...
I also added one more JOIN, to join the first image id with the actual image. I couldn't check your query but the idea is to procede like this.
Since the query is now becoming more complicated and difficult to mantain, i think it would be better to create a view that extracts the first image for every venue, and then join just the view in your query. This is just an idea. Let me know if it works or if you need any help!
I'm not too sure about your data but a JOIN with the thumbnails table and a group by on your large query would probably work.
GROUP BY venues.id

Find mysql entries without match in other tables

I have two tables in mysql with the following structure:
Events: id
Events_artists: event_id, more columns
I would like to find the event_ids in the table events_artists that do not have a match with the id in events.
The only thing I have come up with so far is this:
SELECT * FROM events,events_artists WHERE events_artists.event_id!=events.id
However, that is crap and basically returns the whole table.
Does anyone know how to solve this?
Thank you!
Charles
SOLUTION FOUND, thanks to Andrzej Bobak
select * from events_artists where event_id not in (select id from events)
How about this approach?
select * from events_artists where event_id is null or event_id not in (select id from events)
select * from events_artists a
left join events b on a.id = b.id
where b.id is null
your approach uses a cartesian product which joins every row with each other. So your where criteria just filters your result containing the rows that don't match, but that will be a lot because of the cartesian product

Categories