Scenario:
We have 5 users. (users table)
Each user has up to 10 imgs. (image table)
These 10 images can be ordered 1 – 10. (image table)
Each img can be listed in multiple categories (say there are 5 categories – birds, bees, bunnies, brains, belugas (category table connected to img table via table that stores img_ids and category_ids)
In searching through the categories, say someone chooses bees. The search should find the images in that category that is listed CLOSEST to the #1 img for all users. So if each user has 3 images in the bees category, ordered as numbers 4, 7 & 9, the search should show the 4th as its closest to the number 1.
The results I keep getting are all over the place and almost seems like it is choosing the images via WHEN they were added to the DB.
SELECT i.img_name, i.ordered, a.user_name, c.keyword, c.cat_id
FROM images AS i JOIN artists AS a USING (user_id)
JOIN img_cat_table AS im USING ( img_id )
JOIN catkeys AS c USING (cat_id)
WHERE ( cat_id = 3) // THE BEES ID #
GROUP BY user_id ORDER BY user_name DESC
I'm also not sure if you want to show all of the relevant images in the right order, or only the top one. Assuming that it is the latter situation, you will need to join to a subquery or view that returns the min rank for each user, category:
SELECT i.img_name, i.ordered, a.user_name, c.keyword, c.cat_id
FROM images AS i JOIN artists AS a USING (user_id)
JOIN img_cat_table AS im USING ( img_id )
JOIN catkeys AS c USING (cat_id)
JOIN (
SELECT user_id, min(img_rank) img_rank
FROM images AS i
JOIN artists AS a on i.user_id = a.user_id
JOIN img_cat_table AS im on im.img_id = i.img_id
JOIN catkeys AS c on c.cat_id = i.cat_id
WHERE ( cat_id = 3) ) x on x.user_id = a.user_id and x.img_rank = img_rank
WHERE c.cat_id = 3
I'm not sure what the name of the column that holds the image ranking is. I called it img_rank. Hopefully this will give you the idea
though if you can post the table structure and data, that will be great but Here is what I haved tried
SELECT i.img_name, i.ordered, a.user_name, c.keyword, c.cat_id
from (
select img_name, ordered, img_id, user_id from
images
group by user_id
order by user_img ) as i
JOIN artists AS a USING (user_id)
JOIN img_cat_table AS im USING ( img_id )
JOIN catkeys AS c USING (cat_id)
WHERE ( cat_id = 3) // THE BEES ID #
Try removing DESC from your ORDER BY clause.
Related
I want to write a query that displays photos that user 5 did not like. Photos and likes are 2 tables joined by Photo_Id / Image_Id.
To display photos that user 5 did like I write:
Select
wp_photos.Photo_Id
From
wp_photos inner Join
wp_photos_likes On wp_photos.Photo_Id = wp_photos_likes.Image_Id
Where
wp_photos_likes.From_Id = 5
Group By
wp_photos.Photo_Id
I would like a query that displays all photos that user 5 (From_Id) did not like.
Trying
Where
wp_photos_likes.From_Id <> 5
Does not work as other users liked photos that user 5 liked and so returns these as well.
I tried some left joins but with no success.
What's the correct query?
You can use a left join antipattern:
select p.photo_id
from wp_photos p
left join wp_photos_likes l
on p.photo_id = l.image_id and l.from_id = 5
where l.image_id is null
This phrases as: check if each photo was liked by user 5, and filter on thoses who were not.
Another way to solve this is to use a not exists condition with a correlated subquery:
select p.photo_id
from wp_photos p
where not exists (
select 1
from wp_photos_likes l
where p.photo_id = l.image_id and l.from_id = 5
)
I have two tables - one shows user purchases, and one shows a product id with it's corresponding product type.
My client wants to make duplicate users inactive based on last name and email address, but wants to run the query by product type (based on what type of product they purchased), and only wants to include user_ids who haven't purchased paint (product ids 5 and 6). So the query will be run multiple times - once for all people who have purchased lawnmowers, and then for all people who have purchased leafblowers etc (and there will be some overlap between these two). No user_id that has purchased paint should be made inactive.
In terms of who should stay active among the duplicates, the one to stay active will be the one with the highest product id purchased (as products are released annually). If they have multiple records with the same product id, the record to stay active will be the one with most recent d_modified and t_modified.
I also want to shift the current value of 'inactive' to the 'previously_inactive' column, so that this can be easily reversed if need be.
Here is some sample table data
If the query was run by leafblower purchases, rows 5, 6, and 7 would be made inactive. This is the expected output:
If the query was run by lawnmower purchases, rows 1 and 2 would be made inactive. This would be the expected output:
If row 4 was not the most recent, it would still not be made inactive, as user_id 888 had bought paint (and we want to exclude these user_ids from being made inactive).
This is an un-optimised version of the query for 'leafblower' purchases (it is working, but will probably be too slow in the interface):
UPDATE test.user_purchases
SET inactive = 1
WHERE id IN (
SELECT z.id
FROM (SELECT * FROM test.user_purchases) z
WHERE z.product_id IN (
SELECT product_id
FROM test.products
WHERE product_type IN ("leafblower")
)
AND id NOT IN (
SELECT a.id
FROM (SELECT * FROM test.user_purchases) a
INNER JOIN (
SELECT r.surname, r.email
FROM (SELECT * FROM test.user_purchases) r
JOIN test.products s on r.product_id = s.product_id
WHERE s.product_type IN ("paint")
) b
WHERE a.surname = b.surname
AND a.email = b.email
)
AND id NOT IN (
SELECT MAX(z.id)
FROM (SELECT * FROM test.user_purchases) z
WHERE z.product_id IN (
SELECT product_id
FROM test.products
WHERE product_type IN ("leafblower")
)
AND id NOT IN (
SELECT a.id
FROM (SELECT * FROM test.user_purchases) a
INNER JOIN (
SELECT r.surname, r.email
FROM (SELECT * FROM test.user_purchases) r
JOIN test.products s on r.product_id = s.product_id
WHERE s.product_type IN ("paint")
) b
WHERE a.surname = b.surname
AND a.email = b.email
)
GROUP BY surname, email
)
)
Any suggestions on how I can streamline this query and optimise the speed of it would be much appreciated.
I am trying to optimise my php by doing as much work on the MySQL server as possible. I have this sql query which is pulling data out of a leads table, but at the same time joining two tags tables to combine the result. I am looking to add a company which is linked through a relations table.
So the table that holds the relationship between the two is relations_value which simply states (I add example data)
parenttable (companies) | parentrecordid (10) | childtable (leads) | childrecordid (1)
the companies table has quite a few columns but the only two relevant are;
id (10) | companyname (my company name)
So this query currently grabs everything I need but I want to bring the companyname into the query:
SELECT leads.id,
GROUP_CONCAT(c.tag ORDER BY c.tag) AS tags,
leads.status,
leads.probability
FROM `gs_db_1002`.leads
LEFT JOIN ( SELECT *
FROM tags_module
WHERE tagid IN ( SELECT id
FROM tags
WHERE moduleid = 'leads' ) ) as b
ON leads.id = b.recordid
LEFT JOIN `gs_db_1002`.tags as c
ON b.tagid = c.id
GROUP BY leads.id,
leads.status,
leads.probability
I need to be able to go into the relations_values table and pull parenttable and parentrecordid by selecting childtable = leads and childrecordid = 1 and somehow join these so that I am able to get companyname as a column in the above query...
Is this possible?
I have created a sqlfiddle: sqlfiddle.com/#!2/023fa/2 So I am looking to add companies.companyname as column to the query.
I don't know what your primary keys and foreign keys are that link each table together.. if you could give a better understanding of what ID's are linked to eachother it would make this a lot easier... however i did something that does return the correct result... but since all of the ID's are = 1 then it could be incorrect.
SELECT
leads.id, GROUP_CONCAT(c.tag ORDER BY c.tag) AS tags,
leads.status, leads.probability, companyname
FROM leads
LEFT JOIN (
SELECT * FROM tags_module WHERE tagid IN (
SELECT id FROM tags WHERE moduleid = 'leads' )
) as b ON leads.id = b.recordid
LEFT JOIN tags as c ON b.tagid = c.id
LEFT JOIN relations_values rv on rv.id = b.recordid
LEFT JOIN companies c1 on c1.createdby = rv.parentrecordid
GROUP BY leads.id,leads.status, leads.probability
I am working with MySql 5.1 and am building my first many-to-many database. I understand the concepts, I have 3 tables:
Albums, with a unique ID
Genres, with a unique ID
album_genres with columns for each of the ID's from Albums and Genres.
The issue I am having is that, of course, 1 album can have multiple genres. But when I do a search, I'm really in the dark about how to structure it so during searches, I get all the genres for each individual album. Please note, this is not the loading of 1 album, but doing a search that will net 1 or more albums.
Sorry I don't really have anything to show what I've tried because I'm not even sure where to begin.
I'm sure it's easy enough. But all the tutorials I could find only address the basics of M2M but not how to get multiple matching entries.
After looking at many great suggestions, I have built this query:
SELECT
album.album_title,
Concat(genre.genre_id, ',') as GenreName,
count(album.album_id) as GenreCount
FROM $this->album_tbl album
JOIN wp_musicmgr_albgnr albgnr ON albgnr.albgnr_album_fk = album.album_id
JOIN $this->genre_tbl genre ON genre.genre_id = albgnr.albgnr_genre_fk
GROUP BY album.album_id
Which is producing this:
[0] => stdClass Object
(
[album_title] => album q
[GenreName] => 1,
[GenreCount] => 3
)
However, as you can see. Despite having a count of 3 hits on genres, it is listing the first one.
If I got the question you need output like
AlbumName | Genre1,Genre2.........
FOr this you need to use GroupBy
SELECT A.album_name as AlbumName, GROUP_CONCAT(G.genre_name) as GenreName, count(A.ID) as GenreCount
FROM Album A
JOIN album_genres AG
ON (A.ID = AG.album_ID)
JOIN Genre G
ON (G.ID = AG.genre_ID)
Group by A.ID
Join on the two tables.
SELECT cols FROM Albums
JOIN album_genres USING (albumID)
JOIN Genres USING (genreID)
WHERE albumName LIKE :search
Select * from Albums a, Genres g, album_genres ag
where a.unique_id = ag.a_unique_id and g.unique_id = ag.g_unique_id and a.name like '%album-name%'
album-name is the value of album in table album
Also in place of star(*) in Select *, please do put columns name's from Gener table.
To get all Genres of an Album the query would be
SELECT A.album_name, G.genre_name FROM Album A
JOIN album_genres R
ON (A.ID = R.album_ID)
JOIN Genre G
ON (G.ID = R.genre_ID)
WHERE A.ID = '5'
Where 5 would be the album unique id. The basic concept is that you should join the 3 tables, and use JOIN - ON to match the ids of each columns in the relationship table to the entity table one
EDIT: If you want a list of every album with every genre like this one:
Album - Genre
Al01 - Rock
Al02 - Rock
Al01 - Rock
Al01 - Metal
Al03 - Metal
just remove the WHERE A.ID = '5'.
Updated (New Answer):
Note: Tested.
select a.name, group_concat(g.name separator ', ')
from albums a
inner join album_genre ag on a.id = ag.albumid
inner join genres g on ag.genreid = g.id
group by a.id;
Note: GROUP_CONCAT has a maximum length limit. Default limit is 1024, but can changed by modifying group_concat_max_len. Read more: http://dev.mysql.com/doc/refman/5.7/en/group-by-functions.html#function_group-concat
Old answer:
Search for Albums:
SELECT a.album_name, a.AlbumId FROM `Albums` a
INNER JOIN `album_genres` ag on a.AlbumId = ag.AlbumId
INNER JOIN `Genres` g on ag.GenreId = g.GenreId
WHERE
a.album_name = 'Michael Learns To Dance' AND
g.genre_name = 'ROCK'
GROUP BY a.AlbumId;
Foreach row... perform another search query:
SELECT g.genre_name FROM `Genres` g
INNER JOIN `album_genres` ag on g.GenreId = ag.GenreId
WHERE ag.AlbumId = 1;
....[continue for next row]....
....
WHERE ag.AlbumId = 2;
WHERE ag.AlbumId = 3;
WHERE ag.AlbumId = 4;
......
WHERE ag.AlbumId = 1000;
I'm working on a mysql query in a Drupal database that pulls together users and two different cck content types. I know people ask for help with groupwise maximum queries all the time... I've done my best but I need help.
This is what I have so far:
# the artists
SELECT
users.uid,
users.name AS username,
n1.title AS artist_name
FROM users
LEFT JOIN users_roles ur
ON users.uid=ur.uid
INNER JOIN role r
ON ur.rid=r.rid
AND r.name='artist'
LEFT JOIN node n1
ON n1.uid = users.uid
AND n1.type = 'submission'
WHERE users.status = 1
ORDER BY users.name;
This gives me data that looks like:
uid username artist_name
1 foo Joe the Plumber
2 bar Jane Doe
3 baz The Tooth Fairy
Also, I've got this query:
# artwork
SELECT
n.nid,
n.uid,
a.field_order_value
FROM node n
LEFT JOIN content_type_artwork a
ON n.nid = a.nid
WHERE n.type = 'artwork'
ORDER BY n.uid, a.field_order_value;
Which gives me data like this:
nid uid field_order_value
1 1 1
2 1 3
3 1 2
4 2 NULL
5 3 1
6 3 1
Additional relevant info:
nid is the primary key for an Artwork
every Artist has one or more Artworks
valid data for field_order_value is NULL, 1, 2, 3, or 4
field_order_value is not necessarily unique per Artist - an Artist could have 4 Artworks all with field_order_value = 1.
What I want is the row with the minimum field_order_value from my second query joined with the artist information from the first query. In cases where the field_order_value is not valuable information (either because the Artist has used duplicate values among their Artworks or left that field NULL), I would like the row with the minimum nid from the second query.
The Solution
Using divide and conquer as a strategy and mysql views as a technique, and referencing this article about groupwise maximum queries, I solved my problem.
Create the View
# artists and artworks all in one table
CREATE VIEW artists_artwork AS
SELECT
users.uid,
users.name AS artist,
COALESCE(n1.title, 'Not Yet Entered') AS artist_name,
n2.nid,
a.field_image_fid,
COALESCE(a.field_order_value, 1) AS field_order_value
FROM users
LEFT JOIN users_roles ur
ON users.uid=ur.uid
INNER JOIN role r
ON ur.rid=r.rid
AND r.name='artist'
LEFT JOIN node n1
ON n1.uid = users.uid
AND n1.type = 'submission'
LEFT JOIN node n2
ON n2.uid = users.uid
AND n2.type = 'artwork'
LEFT JOIN content_type_artwork a ON n2.nid = a.nid
WHERE users.status = 1;
Query the View
SELECT
a2.uid,
a2.artist,
a2.artist_name,
a2.nid,
a2.field_image_fid,
a2.field_order_value
FROM (
SELECT
uid,
MIN(field_order_value) AS field_order_value
FROM artists_artwork
GROUP BY uid
) a1
JOIN artists_artwork a2
ON a2.nid = (
SELECT
nid
FROM artists_artwork a
WHERE a.uid = a1.uid
AND a.field_order_value = a1.field_order_value
ORDER BY
uid ASC, field_order_value ASC, nid ASC
LIMIT 1
)
ORDER BY artist;
A simple solution to this can be to create views in your database that can then be joined together. This is especially useful if you often want to see the intermediate data in the same way in some other place. While it is possible to mash together the one huge query, I just take the divide and conquer approach sometimes.