MySql LEFT JOIN within LEFT JOIN - mysql

I am trying to do the following MySql query for a search box. I am trying to return the "Album" info (Title etc) while including a thumbnail of the first image in that album. However, I have to look up two tables to get the image info. First, the photos_albums that contain all the images in that album, get the first image ID from that table, then look up that image info in the photos table. I believe the problem I am having, is that I need to tell the first LEFT JOIN to limit the query to 1, but I have had no luck doing this. I think I need to do a JOIN within a JOIN? Any help on this would be much appreciated.
SELECT albums.title, albums.title_url, photos.path, photos.medType, photos.vpath
FROM albums
LEFT JOIN photos_albums
ON photos_albums.album_id = albums.id
LEFT JOIN photos
ON photos_albums.photo_id = photos.id
WHERE albums.user = '$site_user'
AND (
albums.title LIKE '$keyword%'
OR albums.title LIKE '% $keyword%')
LIMIT 6

You can try something like this
SELECT a.title, a.title_url, q.path, q.medType, q.vpath
FROM albums a LEFT JOIN
(
SELECT pa.album_id, pa.photo_id, p.path, p.medType, p.vpath
FROM
(
SELECT album_id, MIN(photo_id) photo_id
FROM photos_albums
GROUP BY album_id
) pa JOIN photos p
ON pa.photo_id = p.id
) q
ON a.id = q.album_id
WHERE a.user = '$site_user'
AND (
a.title LIKE '$keyword%'
OR a.title LIKE '% $keyword%')
LIMIT 6

Related

Mysql query that handles both a tagging system and favorites

I'm not really a developer but I'm developing an app as a side project and have become stumped with an SQL query I'm attempting to tackle. This app has collections of images that are tagged. My table structure looks something like this:
images
------
image
path
tag_map
------
image
tag
tags
-------
tag
tag_name
favorites
-------
user
image
My query is working well for the tagging system, however, I am not succeeding in adding a query for the favorites. Currently my query looks like this:
SELECT i.*, IF (c.image IS NOT NULL,1,0) as favorited
FROM (images i, tag_map m, tags t)
LEFT JOIN favorites as c ON c.image = i.image
WHERE m.tag = t.tag
AND (t.tag_name IN ('umbrella'))
AND i.image = m.image
AND c.image = i.image
GROUP BY i.image
HAVING COUNT( i.image ) = 1
While this query works, it excludes rows that do note have an entry in the favorites table. The following simplified (sans tag system) query works:
SELECT i.*, c.*,
IF(c.image IS NOT NULL,1,0) as fave
FROM (images as i)
LEFT JOIN collection as c ON c.image = i.image
Any suggestions or points in the right direction would be awesome! Thanks!
As it would turn out, my query works fine once I removed the superfluous "AND c.image = i.image" I had inadvertently left in there.
The working query is:
SELECT i.*, IF (c.image IS NOT NULL,1,0) as favorited
FROM (images i, tag_map m, tags t)
LEFT JOIN collections as c ON c.image = i.image
WHERE m.tag = t.tag
AND (t.tag_name IN ('umbrella'))
AND i.image = m.image
GROUP BY i.image
HAVING COUNT( i.image ) = 1

MySQL Query show results based on multiple filters/tags

This has been asked in different ways before, but I can't seem to get something that works for what I need exactly.
The goal here is to make a search query that returns Photos based on tags that are selected. Many tags can be applied to the filter simultaneously, which would need to make it so that the query only returns photos that have ALL of the tags selected. Think of any major web shop where you are narrowing down results after performing a basic keyword search.
Table1: Photos
ID|Title|Description|URL|Created
Table2: PhotosTagsXref
ID|PhotoId|TagId
Table3: PhotosTags
ID|Title|Category
What I have:
SELECT p.* FROM `PhotosTagsXref` AS pt
LEFT JOIN `Photos` AS p ON p.`ID` = pt.`PhotoId`
LEFT JOIN `PhotosTags` AS t ON pt.`TagId` = t.`ID`
WHERE p.`Description` LIKE "%test%" AND
????
GROUP BY p.`ID`
ORDER BY p.`Created` DESC LIMIT 20
The ???? is where I've tried a bunch of things, but stumped. Problem is I can easily find a result set that contains photos with one tag or another, but if applying 2, 3, or 4 tags we'd need to only return photos that have entries for all of those tags in the database. I think this will involve combining result sets but not 100% sure.
Example:
Photo 1 Tags: Blue, White, Red
Photo 2 Tags: Blue
Searching for a photo with tags of 'blue' returns both photos, searching for a photo with tags of 'blue' and 'white' returns only Photo 1.
Supposing the requested set of tags is (red,blue) you can do:
SELECT * FROM `Photos`
WHERE `Description` LIKE "%test%"
AND `ID` IN (
SELECT pt.`PhotoId` FROM `PhotosTagsXref` AS pt
JOIN `PhotosTags` AS t ON pt.`TagId` = t.`ID`
WHERE t.Title in ('red','blue') /* your set here */
GROUP BY pt.`PhotoId` HAVING COUNT(DISTINCT t.`TagId`)=2 /* # of tags */
)
ORDER BY `Created` DESC LIMIT 20
Apparently, the tag set needs to be created dynamically, as well as its count.
Note: I'm counting DISTINCT TagIDs because I don't know your table's constraints. If PhotosTagsXRef had a PK/UNIQUE (PhotoId,TagId) and PhotosTags had a PK/UNIQUE (TagId), then COUNT(*) would suffice.
Admittedly a bit ugly. But assuming that PhotosTags.Category has the 'Blue', 'White', etc, try something along this line.
SELECT p.*
From `Photos` AS p
WHERE p.`Description` LIKE "%test%" AND
AND Exists
( Select 1 FROM `PhotosTagsXref` AS pt
Inner JOIN `PhotosTags` AS t ON pt.`TagId` = t.`ID`
Where pt.`PhotoId` = p.`ID`
And t.Category = 'FirstCatToSearch'
)
AND Exists
( Select 1 FROM `PhotosTagsXref` AS pt
Inner JOIN `PhotosTags` AS t ON pt.`TagId` = t.`ID`
Where pt.`PhotoId` = p.`ID`
And t.Category = 'SecondCatToSearch'
)
AND Exists
( ...
)
...
SELECT p.* FROM `PhotosTagsXref` AS pt
LEFT JOIN `Photos` AS p ON p.`ID` = pt.`PhotoId`
LEFT JOIN `PhotosTags` AS t ON pt.`TagId` = t.`ID`
inner join (select PhotoId from PhotosTagsXref
LEFT JOIN `PhotosTags` AS t
ON pt.`TagId` = t.`ID`
where (t.title = 'cond 1' or t.title = 'cond 2' ...)
--where t.title in (list condition) **this works as well**
having count(1) = (count of conditions) ) filter
on filter.photoID = pt.PhotoID
WHERE p.`Description` LIKE "%test%"
GROUP BY p.`ID`
ORDER BY p.`Created` DESC LIMIT 20
That should work, I made some assumptions on what column to use for the filter and joins, you may need to retool...the inner join functions as a filter and should pull out only records that have the number of matches equal to the total of the number of matches submitted. Now you just need a language to plug in those conditions and condition count values.

Select two table and order by date

i am trying to list out the latest update from two table doc_to_do and doc_bug_tracker below is my table structure
doc_to_do
doc_bug_tracker
and this is my current query :
$sth = $this->db->prepare('SELECT p.*,
dtd.projects_id as dtd_projects_id, dtd.content as dtd_content, dtd.date_modified as dtd_date_modified,
dbt.projects_id as dbt_projects_id, dbt.content as dbt_content, dbt.date_modified as dbt_date_modified
FROM `projects` p LEFT JOIN `doc_to_do` dtd ON p.id=dtd.projects_id
LEFT JOIN `doc_bug_tracker` dbt ON p.id=dbt.projects_id
where p.id="'.$project_id.'"');
so now how to order by date_modified either from table doc_to_do or doc_bug_tracker ?
To get only the latest date (as opposed to all dates, ordered descending) try this:
SELECT
p.id,
MAX(GREATEST(dtd.date_modified, dbt.date_modified)) AS MaxDate
FROM projects p
LEFT JOIN doc_to_do dtd ON p.id = dtd.projects_id
LEFT JOIN doc_bug_tracker dbt ON p.id = dbt.projects_id
WHERE p.id = <project_id>
GROUP BY p.id
If you need additional columns in your SELECT, be sure to include them in your GROUP BY.
This should work :
'SELECT x.* FROM
(
SELECT p.*,
dtd.projects_id as dtd_projects_id, dtd.content as dtd_content, dtd.date_modified as dtd_date_modified,
dbt.projects_id as dbt_projects_id, dbt.content as dbt_content, dbt.date_modified as dbt_date_modified
FROM `projects` p LEFT JOIN `doc_to_do` dtd ON p.id=dtd.projects_id
LEFT JOIN `doc_bug_tracker` dbt ON p.id=dbt.projects_id
where p.id="'.$project_id.'"
) x
ORDER BY x.date_modified ASC'

MYSQL Inner join for three to four tables

Please I need to figure out what I am doing wrong. I created this inner join code for mysql. it works but it gives me repeated values like repeating a particular row twice or categoryid twice. each of the tables(users,paymentnotification,monthlyreturns) has the categoryid used to check and display the username(users.pname) from the user table, then check and display those that have made payment from the monthly returns and payment table using the categoryid.
$r="SELECT monthlyreturns.categoryid, monthlyreturns.month, monthlyreturns.quarter, monthlyreturns.year,paymentnotification.amount, users.pname, monthlyreturns.ototal, paymentnotification.payee, status
FROM paymentnotification
INNER JOIN (monthlyreturns INNER JOIN users ON monthlyreturns.categoryid=users.categoryid)
ON monthlyreturns.categoryid=paymentnotification.categoryid
ORDER BY monthlyreturns.categoryid DESC";
I think the query you want is more like this:
SELECT b.categoryid, b.month, b.quarter, b.year, a.amount, c.pname, b.ototal, a.payee, status
FROM paymentnotification a
INNER JOIN monthlyreturns b
ON a.categoryid = b.categoryid
INNER JOIN users c
ON b.categoryid = c.categoryid
ORDER BY b.categoryid DESC
The way you are doing the correlations doesn't seem clear and may cause problems. Try this one out and see what happens. If its still doing duplicates, perhaps the nature of the data require further filtering.
Assuming I understand what you're trying to do, you are not joining your tables properly. Try joining one at a time
SELECT DISTINCT monthlyreturns.categoryid, monthlyreturns.month, monthlyreturns.quarter, monthlyreturns.year,paym entnotification.amount, users.pname, monthlyreturns.ototal, paymentnotification.payee, status
FROM paymentnotification
INNER JOIN monthlyreturns
ON paymentnotification.categoryid = monthlyreturns.categoryid
INNER JOIN users
ON monthlyreturns.categoryid = users.categoryid
ORDER BY monthlyreturns.categoryid DESC
I don't see any problem.. I get 4 result rows: check this fiddle http://sqlfiddle.com/#!2/165a22/5
this is the query I used:
SELECT m.categoryid, m.month, m.quarter, m.year,p.amount, u.pname, m.ototal, p.payee, m.status
FROM paymentnotification p JOIN monthlyreturns m ON p.categoryid = m.categoryid
JOIN users u ON u.categoryid = m.categoryid
ORDER BY m.categoryid DESC
there are no duplicated rows, just "unique" rows if you consider every column you choose.
Hope it helps
SELECT M.categoryid, M.month, M.quarter, M.year, M.ototal,
P.amount, P.payee, P.status,
U.pname
FROM paymentnotification AS P
INNER JOIN monthlyreturns AS M ON P.categoryid = M.categoryid
INNER JOIN users AS U ON M.categoryid = U.categoryid
ORDER BY M.categoryid DESC

Simple MySQL Join problem

Im stumped by this simple query because its one I have not tried before.
Ive got a User table, User_Widget table and a Widget table.
A simple inner join shows me what widgets they have by joining user_widget.user_id = user.user_id.
How would I show the widgets in the Widget table that they dont have?
Look up WHERE NOT EXISTS with a subselect in your documentation..
Use a CROSS JOIN and a LEFT OUTER JOIN ( this is from my MS SQL experience, but the concept should hold ).
It works like this. The sub-query gets all possible combinations of user and widget.
The LEFT OUTER JOIN brings your User_Widgets associations into play.
The IS NULL part of the WHERE CLAUSE will exclude widgets that the user does have, giving you only the ones that don't.
SELECT allpossible.User_ID, allpossible.Widget_ID FROM
(
SELECT User_ID, Widget_ID FROM
Users
CROSS JOIN
Widgets
) allpossible
LEFT OUTER JOIN
User_Widgets uw
ON
allpossible.User_ID = uw.User_ID
AND allpossible.Widget_ID = uw.Widget_ID
WHERE
uw.UserID IS NULL
SELECT * FROM widgets WHERE id NOT IN
(
SELECT widget_id FROM user_widgets WHERE user_id = 1
)
(where 1 is the id of the user you're interested in)
This is a guess, (I haven't tried it), but try This:
Select Distinct u.*, Z.*
From User u
Left Join
(Select u.UserId, w.*
From Widget w
Where Not Exists
(Select * From User_Widget
Where userId = u.UserId
And widgetId = w.WidgetId)) Z
On Z.userId = u.UserId
Thanks to Bart Janson I got the query down to:
SELECT * FROM widgets
WHERE NOT EXISTS (
SELECT * FROM widget_user
WHERE widgets.widget_id = widget_user.widget_id
AND user_id = "ID NUMBER OF PERSON YOU WANT"
)
ORDER BY RAND()
LIMIT 10
Cheers guys
SELECT *
FROM widgets w
LEFT OUTER JOIN user_widget uw
ON w.id = uw.widget_id AND uw.user_id = 1 // or whatever user u want
WHERE uw.widget_id IS NULL;