mysql select based on maths on values linked - mysql

I'm trying to output the top x items in my database based on a value of linked 'tags'
http://sqlfiddle.com/#!9/f23ad2
The function I'm currently using (note: :userId is the id from the user table):
SELECT
item.id,
item.name
FROM
item
LEFT JOIN
item_tag
ON
item.id = item_tag.item
LEFT JOIN
tag
ON
item_tag.tag = tag.id
LEFT JOIN
user_tag
ON
user_tag.user = :userId
AND
item_tag.tag = user_tag.tag
LEFT JOIN
item_tag_rating
ON
item_tag.tag = item_tag_rating.tag
AND
TRUNCATE(user_tag.val,0) = item_tag_rating.user_tag
ORDER BY
tag_rating DESC
LIMIT 10
The idea is that the user_tags table will tell you the user's tag_level (1-5) or if they don't currently have one use a default of 3 and based on that you get the value for that level in the item_tags table.
If there are 3 tags for instance you end up with 3 decimal numbers (percentage rating converted to decimal) which is then combined to get an overall percentage rating for the item. I then want to use that overall percentage number to output the top x items.
Any suggestions on how I could do this?

Related

Disacrding multiple duplicate records from a left join

I have two tables:-
gallery
gallery_favorite
The user_id in gallery table means the user who posted the item. The user_id in gallery_favorite means the user who added the item in his favorite list. If favorite = 0, then it means the user had initially added the item in favorite list but later removed it.
Now, I want to fetch all the gallery items along with its favorite status. Here is my query:-
Select distinct `gallery`.`id`, `gallery`.`caption`, `gallery`.`type`,
`gallery`.`video`, `gallery`.`image`, `gallery`.`type`,
`gallery`.`created_date`, `gallery`.`modified_date`,
`gallery_favorite`.`favorite`, `gallery`.`user_id`
from `gallery`
left join `gallery_favorite` on `gallery_favorite`.`gallery_id` = `gallery`.`id`
where
(`gallery`.`type` = 'i'
and `gallery`.`status` = 1
and `gallery`.`deleted` = 0)
and
((`gallery`.`user_id` != 11 and `gallery`.`private` = 0)
or `gallery`.`user_id` = 11)
limit 20 offset 0
But a syou can see, I am getting duplicate records depending upon the number of rows wrt to a gallery item in the gallery favorite table. How can I modify the query to get only one record (along with my own favorite status)?
I guess you are getting duplicate records because you have not joined both the table on user_id -
Try below query -
Select distinct `gallery`.`id`, `gallery`.`caption`, `gallery`.`type`,
`gallery`.`video`, `gallery`.`image`, `gallery`.`type`,
`gallery`.`created_date`, `gallery`.`modified_date`,
`gallery_favorite`.`favorite`, `gallery`.`user_id`
from `gallery`
left join `gallery_favorite` on `gallery_favorite`.`gallery_id` = `gallery`.`id`
and `gallery_favorite`.`user_id` = `gallery`.`user_id`
where
(`gallery`.`type` = 'i'
and `gallery`.`status` = 1
and `gallery`.`deleted` = 0)
and
((`gallery`.`user_id` != 11 and `gallery`.`private` = 0)
or `gallery`.`user_id` = 11)
limit 20 offset 0
Assuming the gallery as many favorites from different users. It makes no sense to display exclusively a hit favorite to a user and gallery alone. Counting them makes sense.
Select `gallery`.`id`, `gallery`.`caption`, `gallery`.`type`,
`gallery`.`video`, `gallery`.`image`, `gallery`.`type`,
`gallery`.`created_date`, `gallery`.`modified_date`,
sum(gallery_favorite`.`favorite`) as total_favorites -- count them group function aggregate
from `gallery`
left join `gallery_favorite` on `gallery_favorite`.`gallery_id` = `gallery`.`id`
where
(`gallery`.`type` = 'i'
and `gallery`.`status` = 1
and `gallery`.`deleted` = 0)
and
((`gallery`.`user_id` != 11 and `gallery`.`private` = 0)
or `gallery`.`user_id` = 11)
and gallery_favorite`.`favorite` = 1 -- count only the favorites
GROUP BY `gallery`.`id` -- GROUP CLAUSE
This is how a join works. You get all rows matching the condition. You can either group the result by the fields in the left table (thus eliminating duplicates in the output) or join with a table that has one entry per gallery item -- this requires joining with a (SELECT ... FROM gallery_favorite GROUP BY gallery_id)

MySQL - unique rows, corresponding to one of 3 tables only

The following query pulls data correctly as expected, however the left join with lnk_cat_isrc table and through that to catalogue table, brings back repeated data if there is more than one item in catalogue which has the same isrcs from isrc table:
SELECT
isrc.ISRC,
isrc.Track_Name,
isrc.ArtistName,
isrc.TitleVersion,
isrc.Track_Time,
`isrc_performer`.`PerformerName` ,
`performer_category`.`PerformerCategory` ,
`isrc_performer`.`PerformerRole` ,
`isrc`.`isrc_ID`,
`isrc_performer`.`Perf_ID`
FROM `isrc`
LEFT JOIN `isrc_performer` ON (isrc.isrc_ID = isrc_performer.isrc_ID)
LEFT JOIN `performer_category` ON (performer_category.PerfCat_ID = isrc_performer.PerfCat_ID)
LEFT JOIN `lnk_cat_isrc` ON (lnk_cat_isrc.isrc_ID = isrc.isrc_ID)
LEFT JOIN `catalogue` ON (catalogue.ID = lnk_cat_isrc.cat_id)
ORDER BY isrc_ID desc LIMIT 0 , 10
";
I cannot use group by on isrc, because the isrc_performer table can have more than one performer to an isrc.
So the relations are like this:
Few items from catalogue table can have several identical items from isrc table. In turn, each isrc can have more than one entry in isrc_performer table.
What I want is to display all corresponding data from isrc_performer in relation to each isrc, but not repeating it for each item from catalogue table.
I also want to display all the rest "empty" isrcs (those which don't have any data in isrc_performer table)
Can you give me any ideas?
P.S. despite I'm not pulling any data from catalogue table itself, I'm using it to search by a catalogue number, when user defines search criteria for $where_condition variable, hence I need to keep it in the query.
i.e. $where_condition = "catalogue.Catalogue LIKE '%test%' OR ISRC LIKE '%test%' OR Track_Name LIKE '%test%' OR ArtistName LIKE '%test%' OR TitleVersion LIKE '%test%' OR PerformerName LIKE '%test%' OR PerformerCategory LIKE '%test%' OR PerformerRole LIKE '%test%'";
------UPD:
trying to graphically represent possible variation in these 3 tables relations:
cat1 - isrc1 - performer1
isrc2 - performer1
- performer2
- performer3
cat2 - isrc2 - performer1
- performer2
- performer3
- isrc3 - performer2
- performer4
cat3 - isrc4
- isrc1 - performer1
UPD (pics added)
Here are screen prints. As you can see on picture 1 there are 9 rows with same isrc number, however there are 3 repeated performers Jason, David, Paul.
This is because 3 different catalogue items have this exact isrc with 3 different performers as per pic 2
= 1(isrc) * 3(catalogue) * 3(performers) = 9 row on output
All I want is that Performers grid would only display 3 rows of this isrc for each performer.
---Rearrange the answer to put the "best" option up top.. .but is all of this for naught.. w/o any data from lnk_cat_isrc or catalogue being returned, why does filtering on catalog make a difference? we're returning all isrc regardless of any filtering because it's a left join...
So this brings into question given sample data what are the expected results.
Possibly more elegant... (but not sure if it would be faster) moving away from exists and simply using a distinct in a subquery so catalog queries always return 1 row per isrc; solving the 1-M problem keeping the left join thereby keeping the isrc records not in the catalog limits.
Return all isrc information performer information if it exists, performer category info if it exists and catalogue information If, and only if it matches the catalog filters.
SELECT isrc.ISRC
, isrc.Track_Name
, isrc.ArtistName
, isrc.TitleVersion
, isrc.Track_Time
,`isrc_performer`.`PerformerName`
,`performer_category`.`PerformerCategory`
,`isrc_performer`.`PerformerRole`
,`isrc`.`isrc_ID`
,`isrc_performer`.`Perf_ID`
FROM `isrc`
LEFT JOIN `isrc_performer`
ON isrc.isrc_ID = isrc_performer.isrc_ID
LEFT JOIN `performer_category`
ON performer_category.PerfCat_ID = isrc_performer.PerfCat_ID
LEFT JOIN (SELECT distinct lnk_cat_isrc.isrc_ID
FROM `lnk_cat_isrc`
INNER JOIN `catalogue`
ON catalogue.ID = lnk_cat_isrc.cat_id
WHERE...) DCat
ON Dcat.isrc_ID = isrc.isrc_ID
ORDER BY isrc_ID desc
LIMIT 0 , 10;
As you pointed out the join is causing the problem. So eliminate the join and use the exists notation. Distinct would also work since you're not selecting any values from catalog; though exists should be faster.
Fast but doesn't include all isrc records... (not sure why the or not exists should bring them back in...)
SELECT isrc.ISRC
, isrc.Track_Name
,isrc.ArtistName
,isrc.TitleVersion
,isrc.Track_Time
,`isrc_performer`.`PerformerName`
,`performer_category`.`PerformerCategory`
,`isrc_performer`.`PerformerRole`
,`isrc`.`isrc_ID`
,`isrc_performer`.`Perf_ID`
FROM `isrc`
LEFT JOIN `isrc_performer`
ON (isrc.isrc_ID = isrc_performer.isrc_ID)
LEFT JOIN `performer_category`
ON (performer_category.PerfCat_ID = isrc_performer.PerfCat_ID)
WHERE EXISTS (SELECT *
FROM `lnk_cat_isrc`
INNER JOIN `catalogue`
ON catalogue.ID = lnk_cat_isrc.cat_id
--and your other criteria
WHERE (lnk_cat_isrc.isrc_ID = isrc.isrc_ID)
)
OR NOT EXISTS (SELECT *
FROM `lnk_cat_isrc`
WHERE lnk_cat_isrc.isrc_ID = isrc.isrc_ID
ORDER BY isrc_ID desc
LIMIT 0 , 10
Or using select distinct simple straight forward; but slow
SELECT isrc.ISRC
, isrc.Track_Name
,isrc.ArtistName
,isrc.TitleVersion
,isrc.Track_Time
,`isrc_performer`.`PerformerName`
,`performer_category`.`PerformerCategory`
,`isrc_performer`.`PerformerRole`
,`isrc`.`isrc_ID`
,`isrc_performer`.`Perf_ID`
FROM `isrc`
LEFT JOIN `isrc_performer`
ON (isrc.isrc_ID = isrc_performer.isrc_ID)
LEFT JOIN `performer_category`
ON (performer_category.PerfCat_ID = isrc_performer.PerfCat_ID)
LEFT JOIN `lnk_cat_isrc`
ON (lnk_cat_isrc.isrc_ID = isrc.isrc_ID)
LEFT JOIN `catalogue`
ON (catalogue.ID = lnk_cat_isrc.cat_id)
--AND (other criteria on catalog here, cause in a where clause you left joins will behave like inner joins)
ORDER BY isrc_ID desc
LIMIT 0 , 10;

Mysql left join, get all right table columns and 2 columns from left table based on left table max id

I have a book series table (bs) that holds basic series info.
I then have a book series photo table (bsp) that holds the thumbnail for each bs based on the bs id. The bs can only have one thumbnail, so the bsp table gets an
insert for every photo update and when it needs to be displayed it gets the max id from that table. I now need to get every bs and the max id photo for it. My query returns all correct rows, but its not limiting to 1 per bs, its returning every bs/bsp combo. Here is the query:
select bs.*, bsp.Id AS BookSeriesPhotoId, bsp.Ext AS BookSeriesPhotoExt
FROM BookSeries AS bs
LEFT JOIN BookSeriesPhotos bsp ON bsp.BookSeriesId = bs.Id
WHERE(
SELECT MAX(Id) FROM BookSeriesPhotos GROUP BY bsp.Id
)
This returns 6 rows, 4 from the first series which had 4 photo updates and 2 for the second series which had 2 photo updates. I want it to return the max photo id per series, so it should only return 2 rows. How can I achieve this?
Try this:
SELECT bs.*, bsp.Id AS BookSeriesPhotoId, bsp.Ext AS BookSeriesPhotoExt
FROM BookSeries AS bs
LEFT JOIN (SELECT bsp.BookSeriesId, bsp.Id, bsp.Ext
FROM BookSeriesPhotos bsp
INNER JOIN (SELECT bsp.BookSeriesId, MAX(bsp.Id) ID
FROM BookSeriesPhotos bsp GROUP BY bsp.BookSeriesId
) AS A ON bsp.BookSeriesId = A.BookSeriesId AND bsp.Id = A.Id
) AS bsp ON bsp.BookSeriesId = bs.Id

Trying to building optimised Query for Group and Subgroup for a user

i am trying to write the Query for three things .My table structure is like that
You can see Schema at http://sqlfiddle.com/#!2/56c2d/1
I am trying to write the query in MYSQL
user:- table
user_id
user_fname
This is User tabke which will save User Information
group:- "group" and "subgroup" is maintain in same table using column "group_parent_group_id"
group_id
group_title
group_parent_group_id(INT)
This is group table which will save Group and Subgroups
user_group: table
user_group_id
user_group_user_id
user_group_group_id
This ill store both User and Group relation using their Id
I am trying to write the Query for three things. Fetching Users Groups, Subgroups
1) Query to fetch list of All Groups for User Register. Query is gelow and is giving error
Query:
select user.id, user.user_fname, group.group_id, group.group_title
from `user`
inner join user_group on user_group.user_group_user_id = user.user_id
inner join group on group.group_id = user_group.user_group_group_id
where user_group.user_group_user_id = 1 and user_group.group_parent_group_id = 0
2) I am Looking the query to fetch all subgroups(For Whom user is already Register) for Group Id 1,2 or 1
3) I am Looking the query to fetch all subgroups(For Whom user is Not Register yet) for Group Id 1,2 or 1. Ideal is for giving him randomly suggest a subgroup to add
Please Help. I am a newbie in DB :(
Your query is probably failing as you have a table called group, which is a reserved word. You can use back tics to delimit the name to get away with this (as follows) but it would be a better idea to change the table name.
SELECT user.id, user.user_fname, `group`.group_id, `group`.group_title
FROM `user`
INNER JOIN user_group ON user_group.user_group_user_id = user.user_id
INNER JOIN `group` ON `group`.group_id = user_group.user_group_group_id
WHERE user_group.user_group_user_id = 1
AND user_group.group_parent_group_id = 0
EDIT updated for queries I think the OP requires.
First query will get a list of all the groups (ones that have no parent group id) that a user (in this case id 28) is a member of
SELECT y2m_user.user_id, y2m_user.user_first_name, y2m_group.group_id, y2m_group.group_title
FROM y2m_user
INNER JOIN y2m_user_group ON y2m_user_group.user_group_user_id = y2m_user.user_id
INNER JOIN y2m_group ON y2m_group.group_id = y2m_user_group.user_group_group_id
WHERE y2m_user.user_id = 28
AND y2m_group.group_parent_group_id = 0
This query will get a list of all the sub groups (ones where the parent group id is greater than 0) that a user (in this case id 28) is a member of
SELECT y2m_user.user_id, y2m_user.user_first_name, y2m_group.group_id, y2m_group.group_title
FROM y2m_user
INNER JOIN y2m_user_group ON y2m_user_group.user_group_user_id = y2m_user.user_id
INNER JOIN y2m_group ON y2m_group.group_id = y2m_user_group.user_group_group_id
WHERE y2m_user.user_id = 28
AND y2m_group.group_parent_group_id > 0
This query will get a list of all the sub groups (ones where the parent group id is greater than 0) that a user (in this case id 28) is NOT a member of
SELECT y2m_user.user_id, y2m_user.user_first_name, y2m_group.group_id, y2m_group.group_title
FROM y2m_user
CROSS JOIN y2m_group
LEFT OUTER JOIN y2m_user_group ON y2m_user_group.user_group_user_id = y2m_user.user_id AND y2m_group.group_id = y2m_user_group.user_group_group_id
WHERE y2m_user.user_id = 28
AND y2m_group.group_parent_group_id > 0
AND y2m_user_group.user_group_id IS NULL
Please excuse any typos as not tested (with your test data there are no sub groups).

SQL Query to Calculate Values based on Conditions

I have three relevant tables:
Evaluations:
EvalID
WorktypeID
PointsPossible
Items:
EvalID
ItemID
Value
QuestionID
Question_Worktype:
WorkTypeID
QuestionID
Points
There are multiple Items for each Evaluation, and each item has a QuestionID/WorkTypeID pair that relates to an entry in Question_Worktype, where the score lies.
Items.Value is whether an Item was correct or not.
I would like a query that returns the total Points of all Items for an Evaluation, Points only are awarded if the Value of the Item is 1.
My goal would be to get:
Eval_ID, Total Points (calculated), Points Possible
I can't seem to wrap my brain around it.
Edit: I forgot an important part:
I have an additional table:
qa_edits:
EditID
ItemID
NewValue
Date
If someone edits the value, it's stored here, and I really need it to calculate based on the newest edit for the item if there is one, or the item value if there isn't.
Start with your edit table, to get the latest edit for each item:
SELECT
M.ItemId,
COALESCE(Q.Value, I.Value) as Value,
I.QuestionId
FROM (
SELECT
I.ItemId,
MAX(Date) as LastEditDate
FROM Items as I
LEFT OUTER JOIN QA_Edits as E ON
I.ItemId = E.ItemId
GROUP BY
I.ItemId
) as M
LEFT OUTER JOIN QA_Edits as Q ON
M.ItemId = Q.ItemId
AND M.LastEditDate = Q.Date
LEFT OUTER JOIN Items as I ON
M.ItemId = Q.ItemId
AND M.LastEditDate IS NULL
I'd suggest a view for that - let's call it LatestItems.
Then, back to Blorgbeard's:
select
e.Eval_ID,
sum(qw.Points) as TotalPoints,
e.PointsPossible
from Evaluations e
join LatestItems i on
e.Eval_ID = i.Eval_ID
and i.Value = 1
join Questions_WorkType qw on
qw.QuestionID = i.QuestionID
group by
e.Eval_ID, e.PointsPossible
I think this should work:
select e.Eval_ID, sum(qw.Points) as TotalPoints, e.PointsPossible
from Evaluations e
left join Items i on e.Eval_ID = i.Eval_ID and i.Value = 1
join Questions_WorkType qw on qw.QuestionID = i.QuestionID
group by e.Eval_ID, e.PointsPossible