MySQL INNER JOIN ordering by multiple matches first - php

I have the following tables
gifts - a list of products
tags - a list of tags that can be applied to products
tags_gifts - a join for gifts and tags if they are applicable
Here's two tag examples (id, name):
508 - jewellery
7 - gold
I have the following SQL for results:
SELECT * FROM gifts
LEFT JOIN tags_gifts ON tags_gifts.gift_id = gifts.gift_id
INNER JOIN tags ON tags.id = tags_gifts.tag_id
WHERE published = '1' AND ( (tags_gifts.tag_id = '508' OR tags_gifts.tag_id = '7') )
GROUP BY gifts.gift_id
ORDER BY gift_popularity DESC LIMIT 0,20
This works fine and shows all results with matches of 'jewellery' or 'gold' and orders them by popularity, but I'd like to show multiple matches first eg. A product matches both tags, and then order the remaining products by popularity.
Can't figure out how to do this - thanks

You can use COUNT() in ORDER BY ,so if count returns 2 it means product has both tags
SELECT * FROM gifts
LEFT JOIN tags_gifts ON tags_gifts.gift_id = gifts.gift_id
INNER JOIN tags ON tags.id = tags_gifts.tag_id
WHERE published = '1' AND ( (tags_gifts.tag_id = '508' OR tags_gifts.tag_id = '7') )
GROUP BY gifts.gift_id
ORDER BY COUNT(*) DESC , gift_popularity DESC
LIMIT 0,20

This will work and will be fast, you can adjust "a LIMIT 20 OFFSET 0" as you wish.
SELECT a.* FROM (
SELECT * FROM gifts
LEFT JOIN tags_gifts ON tags_gifts.gift_id = gifts.gift_id
INNER JOIN tags ON tags.id = tags_gifts.tag_id
WHERE published = '1' AND ( (tags_gifts.tag_id = '508' AND tags_gifts.tag_id = '7') )
GROUP BY gifts.gift_id
ORDER BY gift_popularity DESC
) UNION (
SELECT * FROM gifts
LEFT JOIN tags_gifts ON tags_gifts.gift_id = gifts.gift_id
INNER JOIN tags ON tags.id = tags_gifts.tag_id
WHERE published = '1' AND ( (tags_gifts.tag_id = '508' OR tags_gifts.tag_id = '7') )
GROUP BY gifts.gift_id
ORDER BY gift_popularity DESC
) a LIMIT 0,20

Related

LIMIT LEFT join to last updated row from multiple rows

This is my code i am trying to left join the latest team data, not every piece of data. i have tried just using limit 1 but doesnt return anything
ORDER BY updated DESC LIMIT 1
this doesnt work
Any ideas?
$sql = "SELECT
events.id, events.time,events.status, events.home_team,events.away_team,events.league,
ht.id as home_id,ht.name as home_name,at.name as away_name,
statistics.home_goals,statistics.away_goals,statistics.time as game_time,
leagues.id as league_id,leagues.name as league_name,leagues.type as league_type,
country.name as country_name,country.logo,
hts.home_scored, ats.away_scored,
hts.home_conceeded,ats.away_conceeded,
hts.home_win,ats.away_win,
hts.home_15,ats.away_15,
hts.home_25,ats.away_25,
hts.home_btts, ats.away_btts,
hts.home_fts, ats.away_fts,
hts.home_cs, ats.away_cs,
hts.home_corners_for, ats.away_corners_for,
hts.home_corners_against, ats.away_corners_against,
hts.home_cards, ats.away_cards
FROM events
LEFT JOIN teams ht
ON ht.id = events.home_team
LEFT JOIN teams at
ON at.id = events.away_team
LEFT JOIN leagues
ON leagues.id = events.league
LEFT JOIN country
ON country.id=leagues.country
LEFT JOIN ( SELECT team,home_scored,home_conceeded,home_win,home_15,home_25,home_btts,home_fts,home_cs,home_corners_for,home_corners_against,home_cards FROM team_quick_stats ORDER BY updated DESC) hts
ON ht.id=hts.team
LEFT JOIN ( SELECT team,away_scored,away_conceeded,away_win,away_15,away_25,away_btts,away_fts,away_cs,away_corners_for,away_corners_against,away_cards FROM team_quick_stats ORDER BY updated DESC) ats
ON at.id=ats.team
LEFT JOIN statistics
ON statistics.event_id=events.id
WHERE (events.time BETWEEN $start AND $end) ORDER BY country.list_order, leagues.country ASC , leagues.id ASC, events.time ASC, home_name ASC";
Here's one way. Replace LEFT JOIN (SELECT team... etc....) ats with...
LEFT
JOIN
( SELECT x.team
, x.etc...
FROM team_quick_stats x
JOIN
( SELECT team
, MAX(updated) updated
FROM team_quick_stats
GROUP
BY team
) y
ON y.team = x.team
AND y.updated = x.updated
) ats...

Select INNER JOIN mysql DataBase

I want to select database with condition but not working.
I have this query :
SELECT c.*
,s.*
,f.*
,count(c.id) as instock_count
,totals.orders_total
,GROUP_CONCAT(c.sku SEPARATOR '<br/>') AS sku
FROM produse_com c
INNER JOIN (SELECT * , SUM(stoc) as stoc_sum
FROM stocuri_mentor
GROUP BY sku ) s
ON c.sku = s.sku
INNER JOIN (SELECT count(id) as orders_total, id_comanda
FROM produse_com
WHERE NOT ridicat = 'Da'
GROUP BY id_comanda ) totals
ON totals.id_comanda = c.id_comanda
INNER JOIN (SELECT data_add, status, id_comanda
FROM comenzi
WHERE NOT (status = 'Impachetat' OR status = 'Stornat' OR status = 'Anulat')
) f
ON f.id_comanda = c.id_comanda
WHERE stoc_sum >= c.qty
GROUP BY c.id_comanda
HAVING instock_count = orders_total
The issue is I have a condition WHERE NOT ridicat = 'Da'
But the result contain all result with ridicat = 'Da'
EDIT :
If I remove this query INNER JOIN (SELECT * ,SUM(stoc) as stoc_sum FROM stocuri_mentor GROUP BY sku ) s ON c.nume_produs = s.sku AND WHERE stoc_sum >= c.qty the result is OK
EDIT 2 : SOLUTION = add 1 more condition WHERE stoc_sum >= c.qty AND ridicat <> 'Da'
You can try this query.
SELECT count(id) as orders_total, id_comanda, ridicat
FROM produse_com
WHERE ridicat != 'Da'
GROUP BY id_comanda;

SELECT product variants SQL

This is my query without variant options
SELECT p.*, pd.`name` AS `product_name`
FROM `product` AS `p`
LEFT JOIN `product_description` AS `pd` ON p.`id` = pd.`product_id`
LEFT JOIN `product_to_variant` AS `pv` ON p.`id` = pv.`product_id`
WHERE p.`status` = 0
GROUP BY p.`id`
ORDER BY p.`id` DESC;
SQLFiddle: http://sqlfiddle.com/#!9/8955b/5
and the follwing query has variant options but it doesn't work
SELECT p.*, pd.`name` AS `product_name`
FROM `product` AS `p`
LEFT JOIN `product_description` AS `pd` ON p.`id` = pd.`product_id`
LEFT JOIN `product_to_variant` AS `pv` ON p.`id` = pv.`product_id`
WHERE p.`status` = 0
AND (pv.`feature_id` = 2 AND pv.`variant_id` = 6)
AND (pv.`feature_id` = 3 AND pv.`variant_id` = 11)
GROUP BY p.`id`
ORDER BY p.`id` DESC;
and I also trying to query but there is no output
SELECT pv.* FROM `product_to_variant` AS `pv`
WHERE (pv.`feature_id` = 2 AND pv.`variant_id` = 2)
AND (pv.`feature_id` = 3 AND pv.`variant_id` = 11)
Do you have any other idea how to receive the products: 14, 15 by specific variant_id 6 AND 11 http://prntscr.com/ect2oh
Here is one method to do what you want:
SELECT p.*, pd.name AS product_name
FROM product p LEFT JOIN
product_description pd
ON p.id = pd.product_id JOIN
product_to_variant pv
ON p.id = pv.product_id
WHERE p.status = 0 AND
((pv.feature_id = 2 AND pv.variant_id = 6) OR
(pv.feature_id = 3 AND pv.variant_id = 11)
)
GROUP BY p.id
HAVING COUNT(DISTINCT feature_id) = 2
ORDER BY p.id DESC;
Notes:
No row can meet your original conditions. Because a column cannot have two values at the same time. Hence the OR rather than AND.
The HAVING clause checks that both values match.
There is no need for a LEFT JOIN to pv, because you are checking values from that table in the WHERE clause -- there have to be matches.
The LEFT JOIN to pa is probably also unnecessary.
Using backticks everywhere just makes the query harder to write and to read.
Maybe like this:
SELECT pv.* FROM `product_to_variant` AS `pv`
WHERE (pv.`feature_id` = 2 AND pv.`variant_id` = 2)
OR (pv.`feature_id` = 3 AND pv.`variant_id` = 11)
Instead of AND, which says, you need both feature_id and variant_id in the results, use OR because it takes both.

single mysql query for choosing 25 records (9+16) using union all and based on 2 different conditions

I need to fetch 25 records using mysql out of which first 9 must be based on the descending order of likes count (chosen randomly) from 200 top appreciated and balance 16 randomly from the remaining items (excluding 9, that are already filtered). Is it possible to do this using a single mysql query? Any help will be appreciated.
Here is my query...
(SELECT * FROM (SELECT tiles.,users.first_name,users.last_name, users.mosaicname,users.country,users.city,users.state,users.profile_image,COUNT(tile_appreciations.tile_id) AS appreciation_count FROM tiles LEFT JOIN tile_appreciations ON tile_appreciations.tile_id = tiles.id INNER JOIN users ON users.id = tiles.user_id LEFT JOIN user_settings ON user_settings.user_id = tiles.user_id WHERE tiles.view_mode = 'PB' AND users.status = 'Y' AND tiles.moved_stat = '1' AND user_settings.public_profile = 'Y' GROUP BY tiles.id ORDER BY appreciation_count DESC LIMIT 200) as t1 ORDER BY RAND() LIMIT 9) UNION ALL (SELECT tiles.,users.first_name,users.last_name,users.mosaicname,users.country,users.city,users.state,users.profile_image,COUNT(tile_appreciations.tile_id) AS appreciation_count FROM tiles LEFT JOIN tile_appreciations ON tile_appreciations.tile_id = tiles.id INNER JOIN users ON users.id = tiles.user_id LEFT JOIN user_settings ON user_settings.user_id = tiles.user_id WHERE tiles.view_mode = 'PB' AND users.status = 'Y' AND tiles.moved_stat = '1' AND user_settings.public_profile = 'Y' GROUP BY tiles.id ORDER BY RAND() LIMIT 16)
I don't know if using UNION ALL is a hard requirement, but SQL already has a very good system for filtering out results from the first query in the second query: it is called UNION. You can choose the remaining 16 by taking the union of 9 of the best 200 and 25 of the whole set and then limiting the total result to 25. I'm assuming here that UNION will remove duplicates from the second set and not the first.
Try something like this:
SELECT * FROM (
SELECT * FROM (
SELECT tiles.*,users.first_name,users.last_name, users.mosaicname,users.country,users.city,users.state,users.profile_image,COUNT(tile_appreciations.tile_id) AS appreciation_count
FROM tiles LEFT JOIN tile_appreciations ON tile_appreciations.tile_id = tiles.id INNER JOIN users ON users.id = tiles.user_id LEFT JOIN user_settings ON user_settings.user_id = tiles.user_id
WHERE tiles.view_mode = 'PB' AND users.status = 'Y' AND tiles.moved_stat = '1' AND user_settings.public_profile = 'Y'
GROUP BY tiles.id
ORDER BY appreciation_count DESC LIMIT 200
) as best200
ORDER BY RAND()
LIMIT 9
) UNION (
SELECT tiles.*,users.first_name,users.last_name,users.mosaicname,users.country,users.city,users.state,users.profile_image,COUNT(tile_appreciations.tile_id) AS appreciation_count
FROM tiles LEFT JOIN tile_appreciations ON tile_appreciations.tile_id = tiles.id INNER JOIN users ON users.id = tiles.user_id LEFT JOIN user_settings ON user_settings.user_id = tiles.user_id
WHERE tiles.view_mode = 'PB' AND users.status = 'Y' AND tiles.moved_stat = '1' AND user_settings.public_profile = 'Y'
GROUP BY tiles.id
ORDER BY RAND()
LIMIT 25
)
LIMIT 25;

select n posts per category in a single query

In my home page I show different posts from different categories.
My current implementation is to call query_posts many times (once per category)
How can I use one query to pull out the data?
My tries
1 - this method works(ignore the ugly very long sql,I have many categories...)
Thanks the post: http://www.xaprb.com/blog/2006/12/07/how-to-select-the-firstleastmax-row-per-group-in-sql/
( SELECT *
FROM `wp_posts` p,`wp_term_relationships` rel
WHERE rel.object_id = p.ID
AND rel.term_taxonomy_id = '3'
ORDER BY p.post_date DESC
LIMIT 2)
UNION ALL
( SELECT *
FROM `wp_posts` p,`wp_term_relationships` rel
WHERE rel.object_id = p.ID
AND rel.term_taxonomy_id = '4'
ORDER BY p.post_date DESC
LIMIT 2)
SELECT i.*
FROM wp_term_relationships rel
INNER JOIN (
SELECT p.*, rel2.*
FROM wp_posts p
INNER JOIN wp_term_relationships rel2
ON (rel2.object_id = p.ID)
WHERE rel2.term_taxonomy_id = rel.term_taxonomy_id
ORDER BY p.post_date DESC
LIMIT 2 OFFSET 0 ) i ON (i.object_id = rel.object_id)
WHERE rel.term_taxonomy_id IN ('3','4')
ORDER BY i.term_taxonomy_id ASC, i.post_date DESC

Categories