How to use multiple IN - Where IN (...) and IN (...) - mysql

I'm tuning performance of my script and I changed the way posts categories are sorted.
I was using successfully the following query.
It selects posts with (130|3|4|5) categories and checks if it also has (73) category init as one post can have multiple categories.
SELECT *
FROM post
LEFT JOIN post_plus
ON ( post.id = post_plus.news_id )
WHERE category REGEXP '[[:<:]](130|3|4|5)[[:>:]]'
AND category REGEXP '[[:<:]](73)[[:>:]]'
AND approve = 1
ORDER BY fixed DESC,
date DESC
LIMIT 0, 7
Now with a new query I need to perform multiple IN (...) queries instead of AND category REGEXP '...', but for some reason additional AND categoryid IN always returns empty result.
SELECT *
FROM post
LEFT JOIN post_plus
ON ( post.id = post_plus.news_id )
LEFT JOIN post_category
ON ( post_category.postid = post.id )
WHERE categoryid IN ( 130, 3, 4, 5 )
AND categoryid IN ( 73 )
AND approve = 1
ORDER BY fixed DESC,
date DESC
LIMIT 0, 7;
post_category structure
+-----+--------+------------+
| cid | postId | categoryId |
+-----+--------+------------+
| 824 | 7 | 10 |
| 825 | 7 | 13 |
| 826 | 7 | 16 |
| 827 | 7 | 29 |
| 828 | 7 | 71 |
+-----+--------+------------+

To get the postid for posts that are both in category 73 and in at least one of category id 130, 3, 4, 5 you can use
SELECT postid
FROM post_category
GROUP BY postid
HAVING MAX(CASE
WHEN categoryid = 73 THEN 1
END) = 1
AND MAX(CASE
WHEN categoryid IN ( 130, 3, 4, 5 ) THEN 1
END) = 1
This can be used in a derived table to join onto your wider query.
Or another possibility is
SELECT c1.postid
FROM post_category c1
JOIN post_category c2
ON c1.postid = c2.postid
WHERE c1.categoryid IN ( 130, 3, 4, 5 )
AND c2.categoryid = 73

You can use an INTERSECT-compound statement:
SELECT postid
FROM post_category
WHERE categoryid = 73
INTERSECT
SELECT postid
FROM post_category
WHERE categoryid IN (130, 3, 4, 5)
This will select only those postids that have a categoryid equal to 73 and at least one of categoryid = 130, 3, 4, or 5.
This query will probably run faster than a aggregate-query (GROUP BY), since the sql-server can use the index on categoryid (which you hopefully have) to get the relevent postids and then uses a temporary index to compute the intersection (at least thats what sqlite does). If there there are no postids with categoryid 73, then the second part of the query is never executed, since the intersection of an empty set with anything else is always just the empty set.
To get your posts, use a subquery:
SELECT post.*
FROM post
WHERE post.id IN (SELECT postid
FROM post_category
WHERE categoryid = 73
INTERSECT
SELECT postid
FROM post_category
WHERE categoryid IN (130, 3, 4, 5))
Also note that you can add more INTERSECT-compounds as you like.

Related

How to display a query in a certain way?

I'm currently new to queries and below I have a query that I have made
QUERY:
select TITLE_ID, TITLE,NAME, JOB_CATEGORY AS ROLE
FROM MOVIES
NATURAL JOIN NEW_NAMES
WHERE JOB_CATEGORY = 'writer'
OR JOB_CATEGORY = 'director'
ORDER BY TITLE_ID ASC;
Which Displays:
TITLE_ID | TITLE | NAME | ROLE |
753595 | 2F2F | ROB | WRITER |
753595 | 2F2F | YAS | DIRECTOR|
However I would like it to display in this format below:
TITLE_ID | TITLE | WRITER | DIRECTOR|
753595 | 2F2F | ROB | YAS |
You join your NEW_NAMES table in twice. Once for director, once for writer. When you join a table more than once it's necessary that you give the table an Alias, here we use writer and director.
select TITLE_ID, TITLE,writer.NAME as writer_name, director.NAME as director_name
FROM MOVIES
LEFT OUTER JOIN NEW_NAMES as writer
ON MOVIES.TITLE_ID = writer.TITLE_ID
AND writer.JOB_CATEGORY = 'writer'
LEFT OUTER JOIN NEW_NAMES as director
ON MOVIES.TITLE_ID = director.TITLE_ID
AND director.JOB_CATEGORY = 'director'
ORDER BY TITLE_ID ASC;
I've made some assumptions in those ON clauses about which columns in your tables you are joining on. You may need to edit that.
According to error message you got, it is about Oracle, not MySQL.
Anyway, such a (classic?) principle should work: aggregate!
SQL> with
2 -- sample data
3 movies (title_id, title_name) as
4 (select 753595, '2F2F' from dual union all
5 select 123456, '1Z1Z' from dual
6 ),
7 new_names (title_id, job_category, name) as
8 (select 753595, 'writer', 'ROB' from dual union all
9 select 753595, 'director', 'YAS' from dual
10 )
11 -- query you need
12 select m.title_id,
13 m.title_name,
14 max(case when n.job_category = 'writer' then n.name end) as writer,
15 max(case when n.job_category = 'director' then n.name end) as director
16 from movies m left join new_names n on m.title_id = n.title_id
17 group by m.title_id, m.title_name
18 order by m.title_id;
TITLE_ID TITL WRI DIR
---------- ---- --- ---
123456 1Z1Z
753595 2F2F ROB YAS
SQL>

Find duplicates from same table and constraint them from another table in sql

Oh, my title is not the best one and as English is not my main language maybe someone can fix that instead of downvoting if they've understood the issue here.
Basically i have two tables - tourneyplayers and results. Tourneyplayers is like a side table which gathers together tournament information across multiple tables - results, tournaments, players etc. I want to check duplicates from the results table over column day1_best, from single tournament and return all the tourneyplayers who have duplicates.
Tourneyplayers contain rows:
Tourneyplayers
tp_id | resultid | tourneyid
1 | 2 | 91
2 | 21 | 91
3 | 29 | 91
4 | 1 | 91
5 | 3 | 92
Results contains rows:
Results:
r_id | day1_best
1 | 3
2 | 1
3 | 4
.. | ..
21 | 1
.. | ..
29 | 2
Now tourney with id = 91 has in total 4 results, with id's 1,2,21 and 29. I want to return values which have duplicates, so currently the result would be
Result
tp_id | resultid | day1_best
1 | 2 | 1
2 | 21 | 1
I tried writing something like this:
SELECT *
FROM tourneyplayers
WHERE resultid
IN (
SELECT r1.r_id
FROM results AS r1
INNER JOIN results AS r2 ON ( r1.day1_best = r2.day1_best )
AND (
r1.r_id <> r2.r_id
)
)
AND tourneyid =91
But in addition to values which had the same day1_best it chose two more which did not have the same. How could i improve my SQL or rewrite it?
First you JOIN both tables, so you know how the data looks like.
SELECT *
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`;
Then using the same query you GROUP to see what tourneyid, day1_best combination has multiple rows
SELECT `tourneyid`, `day1_best`, count(*) as total
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`
GROUP BY `tourneyid`, `day1_best`;
Finally you use the base JOIN and perform a LEFT JOIN to see what rows has a match and show only those rows.
SELECT t.`tp_id`, r.`r_id`, r.`day1_best`
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`
LEFT JOIN (SELECT `tourneyid`, `day1_best`, count(*) as total
FROM tourney_players t
JOIN results r
ON t.`resultid` = r.`r_id`
GROUP BY `tourneyid`, `day1_best`
HAVING count(*) > 1) as filter
ON t.`tourneyid` = filter.`tourneyid`
AND r.`day1_best` = filter.`day1_best`
WHERE filter.`tourneyid` IS NOT NULL;
SQL DEMO
OUTPUT
Please try this :
Select tp.tp_id , tp.resultid ,r.day1_best from (Select * from Tourneyplayers
where tourneyid = 91)as tp inner join (select * from Result day1_best in(select
day1_best from result group by day1_best having count(*)>1 ) )as r on tp.resultid
= r.r_id ;

MySQL Max with wrong fields

When executing below query
SELECT `game_turns`.`in_darts`, `game_turns`.`date`, MAX(game_turns.score) AS max_score
FROM `game_turns`
JOIN `games` ON `games`.`id` = `game_turns`.`game_id` AND `games`.`training` = 1
WHERE `game_turns`.`uid` = 2
AND `game_turns`.`out` = 1
AND `game_turns`.`in_darts` = 3
ORDER BY `game_turns`.`score` DESC
LIMIT 1
I get the max score for that user id (uid) and out in 3 darts, but the rest (date) is wrong.
Fields are
Score Uid GameID Score out in_darts date
121 2 4 8 1 3 2015-07-21 13:52:12
8465 2 142 100 1 3 2015-09-05 19:46:29
It returns the score 100 from row ID 8465 but the rest is from row ID 121
I have googled it and came on some Stackoverflow results saying that I should use ORDER BY and LIMIT 1, but looks like it aint working for me.
Order by Date also didn't do the trick.
A simple order by and limit should do what you want:
SELECT gt.`in_darts`, gt.`date`, gt.score
FROM `game_turns` gt JOIN
`games` g
ON g.`id` = gt.`game_id` AND g.`training` = 1
WHERE gt.`uid` = 2 AND gt.`out` = 1 AND gt.`in_darts` = 3
ORDER BY gt.`score` DESC
LIMIT 1;
There is no need for aggregation.
If seeking a solution that would work for multiple UID's then aggregation becomes useful - via a subquery.
SQL Fiddle
MySQL 5.6 Schema Setup:
CREATE TABLE Table1
(`Score_A` int, `Uid` int, `GameID` int, `Score_B` int, `out` int, `in_darts` int, `date` datetime)
;
INSERT INTO Table1
(`Score_A`, `Uid`, `GameID`, `Score_B`, `out`, `in_darts`, `date`)
VALUES
(121, 2, 4, 8, 1, 3, '2015-07-21 13:52:12'),
(8465, 2, 142, 100, 1, 3, '2015-09-05 19:46:29')
;
Query 1:
SELECT
t.*
FROM table1 t
INNER JOIN (
SELECT Uid, max(Score_B) as Score_B
FROM table1
GROUP BY uid
) msb ON t.Uid = msb.Uid and t.Score_B = msb.Score_B
Results:
| Score_A | Uid | GameID | Score_B | out | in_darts | date |
|---------|-----|--------|---------|-----|----------|-----------------------------|
| 8465 | 2 | 142 | 100 | 1 | 3 | September, 05 2015 19:46:29 |

wordpress custom mysql query

In wordpress How can we fetch records in specific order within meta field value for eg I need to fetch all records where zip_code(meta key name) is all fields with 60007 first than all 60143 than 60191
Table drrr_posts
Id | post_name | content
2
3
4
22
32
43
Table drrr_postmeta
id | meta_key | meta_value | post_id
1 | zip_code | 60143 | 2
2 | zip_code | 60007 | 3
3 | zip_code | 60191 | 4
4 | zip_code | 60143 | 22
5 | zip_code | 60007 | 32
6 | zip_code | 60143 | 43
I think issue is I am passing ORDER BY FIELD( drrr_postmeta.meta_value not zip_code which is meta_key
In mysql I can easily pass the values in ORDERBY like
ORDER BY FIELD(name, 'B', 'A', 'D', 'E', 'C')
here is what I have now
My Query is
SELECT SQL_CALC_FOUND_ROWS drrr_posts.ID
FROM drrr_posts
INNER JOIN drrr_term_relationships ON ( drrr_posts.ID = drrr_term_relationships.object_id )
INNER JOIN drrr_postmeta ON ( drrr_posts.ID = drrr_postmeta.post_id )
INNER JOIN drrr_postmeta AS mt1 ON ( drrr_posts.ID = mt1.post_id )
WHERE 1 =1
AND drrr_posts.ID NOT
IN ( 91, 89, 87, 66 )
AND (
drrr_term_relationships.term_taxonomy_id
IN ( 11 )
)
AND drrr_posts.post_type = 'doctors'
AND (
(
drrr_posts.post_status = 'publish'
)
)
AND (
drrr_postmeta.meta_key = 'zip_code'
AND (
mt1.meta_key = 'zip_code'
AND CAST( mt1.meta_value AS SIGNED )
IN (
'60007', '60143', '60191', '60005', '60106', '60157', '60173', '60008', '60056', '60172'
)
)
)
GROUP BY drrr_posts.ID
ORDER BY FIELD( drrr_postmeta.meta_value, '60007', '60143', '60191', '60005', '60106', '60157', '60173', '60008', '60056', '60172' )
LIMIT 0 , 8
You can order your results by meta_key by adding one more conditional order by ,also your group by statement doesn't make any sense in absence of aggregate function ,for now you can do this
ORDER BY
drrr_postmeta.meta_key ='zip_code' desc,
FIELD( drrr_postmeta.meta_value, '60007', '60143', '60191', '60005', '60106', '60157', '60173', '60008', '60056', '60172' )
Using drrr_postmeta.meta_key ='zip_code' will result as a boolean and returns true if both matches so it will order results first by zip_code matching meta_key and then you field() function will order results accordingly

MySQL SELECT, JOIN, GROUP BY query optimizing

I am trying to select DISTINCT products from within categories with category ids (1, 5, 12), ORDERED by cat_order + prod_order from MySQL database
The problem:
if a product is found in more than 1 category I need to show the first result,
ie: product number 1 is assigned to categories 1 and 5, I need to display product number 1 from category 1 along with its prod_order and skip the listing in category 5,
essentually I need to display all products from category 1, than move on to category 5 and display all products from there, where product id was not shown previously, and move on to another category in the list (12)
if I run something like:
SELECT
prod_to_cat.prod_id AS prod_to_cat_prod_id,
prod_to_cat.prod_order AS prod_to_cat_prod_order,
prod_to_cat.cat_id AS prod_to_cat_cat_id,
prod_to_cat.cat_order AS prod_to_cat_cat_order,
products.id,
products.name
FROM
prod_to_cat, products
WHERE
prod_to_cat.prod_id = products.id
AND prod_to_cat.cat_id IN (1, 5, 12)
GROUP BY
prod_to_cat.prod_id
ORDER BY
prod_to_cat_cat_order ASC,
prod_to_cat_prod_order DESC
I get inconsistent results (product 1 will not be selected from the first category in the list), that is why I opted to select without "GROUP BY prod_id" and wrap that with another select which than groups by prod_id.
like so:
SELECT
prod_to_cat_prod_id,
prod_to_cat_prod_order,
prod_to_cat_cat_id,
name
FROM
(
SELECT
prod_to_cat.prod_id AS prod_to_cat_prod_id,
prod_to_cat.prod_order AS prod_to_cat_prod_order,
prod_to_cat.cat_id AS prod_to_cat_cat_id,
prod_to_cat.cat_order AS prod_to_cat_cat_order,
products.id,
products.name
FROM
prod_to_cat, products
WHERE
prod_to_cat.prod_id = products.id
AND prod_to_cat.cat_id IN (1, 5, 12)
ORDER BY
prod_to_cat_cat_order ASC,
prod_to_cat_prod_order DESC
) AS prod
GROUP BY
prod_to_cat_prod_id
ORDER BY
prod_to_cat_cat_order ASC,
prod_to_cat_prod_order DESC
LIMIT 0, 10;
What I am trying to do:
I am trying to find a more efficiant way to do this.
Table structure:
prod_to_cat:
prod_id | cat_id | cat_order | prod_order |
1 1 1 2
2 1 1 0
3 1 1 0
1 5 2 4
4 5 2 0
products:
id | name | descr | price |
1 name_1
2 name_2
3 name_3
4 name_4
each product can be in any number of categories, for example product id 1 is in categories id 1 and 5 in the example above.
Thanks a lot for any replies.
Pasha
You want the groupwise minimum:
SELECT prod_to_cat.*,
products.name
FROM prod_to_cat NATURAL JOIN (
SELECT prod_id,
MIN(cat_id) AS cat_id
FROM prod_to_cat
WHERE cat_id IN (1, 5, 12)
GROUP BY prod_id
) t
JOIN products ON t.prod_id = products.id
ORDER BY prod_to_cat.cat_order ASC,
prod_to_cat.prod_order DESC
See it on sqlfiddle.