I have this table:
video_id tag_id
10 7
6 7
10 5
6 5
9 5
9 4
10 4
I want to write a query which will give me video_id which have both tag_id 7 and 5. So video_id 10 and 6 should be selected, but not 9.
I tried where tag_id IN (7, 5) condition but obviously that includes 9.
Will it work for you?
SELECT video_id
FROM table1
WHERE tag_id iN (7,5)
GROUP BY video_id
HAVING COUNT(DISTINCT tag_id) =2;
Update.
If you have a unique constraint on (video_id,tag_id), no need for COUNT(DISTINCT); COUNT(*) will work as well
Arbitrary solution -
Below is an answer for sql server, with mysql you would need a count() on video_id and then look for rows with the value needed.
I don't have mysql to test but feel free to add an edit of a correct mysql solution.
With a table called neededtag with one column (tag) that has the list of what you want
SELECT video_id
FROM
(
SELECT video_id,
row_number() OVER (PARTITION BY vidio_id ORDER BY video_ID) as RN
FROM table
JOIN neededtag ON tag_id = tag
) sub
WHERE RN = (SELECT COUNT(*) FROM neededtag)
or maybe your needed tag has a criteraname column... then it would look like this:
SELECT video_id
FROM
(
SELECT video_id,
row_number() OVER (PARTITION BY vidio_id ORDER BY video_ID) as RN
FROM table
JOIN neededtag ON tag_id = tag and criteraname = "friday tag list"
) sub
WHERE RN = (SELECT COUNT(*) FROM neededtag WHERE criteraname = "friday tag list")
Prior answer
SELECT video_id
FROM table
WHERE tag_id = 5
INTERSECT
SELECT video_id
FROM table
WHERE tag_id = 7
or
SELECT video_id
FROM table
WHERE tag_id = 5
AND video_id IN
(
SELECT video_id
FROM table
WHERE tag_id = 7
)
or
SELECT v1.video_id
FROM table v1
JOIN table v2 WHERE v1.video_id = v2.video_id AND v2.tag_id = 7
WHERE tag_id = 5
There may be faster ways to do it, but this is straightforward. I'm assuming there's a videos table with primary key video_id, so I don't have to SELECT DISTINCT.
SELECT v.video_id FROM videos as v WHERE
EXISTS (SELECT * FROM tagtable as t WHERE t.video_id = v.video_id and tag_id = 7)
AND EXISTS (SELECT * FROM tagtable as t WHERE t.video_id = v.video_id and tag_id = 5)
Related
Structure is:
CREATE TABLE current
(
id BIGINT NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
symbol VARCHAR(5),
UNIQUE (id), INDEX (symbol)
) ENGINE MyISAM;
id
symbol
1
A
2
B
3
C
4
C
5
B
6
A
7
C
8
C
9
A
10
B
I am using the following
SELECT *
FROM current
WHERE id
IN
(
SELECT MAX(id)
FROM current
GROUP BY symbol
)
to return the last records in a table.
id
symbol
8
C
9
A
10
B
How can I return the next-to-last results in a similar fashion?
I know that I need
ORDER BY id DESC LIMIT 1,1
somewhere, but my foo is weak.
I would want to return
id
symbol
5
B
6
A
7
C
For versions of MySql prior to 8.0, use a subquery in the WHERE clause to filter out the max id of each symbol and then aggregate:
SELECT MAX(id) id, symbol
FROM current
WHERE id NOT IN (SELECT MAX(id) FROM current GROUP BY symbol)
GROUP BY symbol
ORDER BY id;
See the demo.
SELECT *
FROM current
WHERE id IN (
SELECT DISTINCT T.id FROM current AS T
WHERE id=(
SELECT id FROM current
WHERE symbol=T.symbol
ORDER BY id DESC LIMIT 1,1
)
)
Easy if your MySql can use ROW_NUMBER. (MySql 8)
Just make it sort descending, then take the 2nd.
WITH CTE AS (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY symbol ORDER BY id DESC) AS symbol_rn
FROM current
)
SELECT id, symbol
FROM CTE
WHERE symbol_rn = 2
ORDER BY id;
In MySql 7.5 you can simply self-join on the symbol, and group by.
Then the 2nd last will have 1 higher id.
SELECT c1.id, c1.symbol
FROM current c1
LEFT JOIN current c2
ON c2.symbol = c1.symbol
AND c2.id >= c1.id
GROUP BY c1.id, c1.symbol
HAVING COUNT(c2.id) = 2
ORDER BY c1.id;
id
symbol
5
B
6
A
7
C
db<>fiddle here
The performance will really benefit from an index on symbol.
You can try this;
SELECT *
FROM current
WHERE id
IN (SELECT MAX(id)
FROM current
GROUP BY symbol)
ORDER BY id DESC LIMIT 1,3
limit 1,3 says; get the last 3 results excluding the last result. You can change the numbers.
We have a table with two columns - art_id and cat_id.
We need to select rows WHERE cat_id = 12, 13, 15
I tried to do something:
SELECT art_id
FROM table
WHERE cat_id IN (12,13,15)
GROUP BY art_id
HAVING COUNT(cat_id) > 2
but this selection also select art_id = 4 AND 9.
Expected Output:
art_id = 1 and 7
Assuming you do not have know beforehand the art_ids, perhaps this?
SELECT art_id, GROUP_CONCAT(cat_id SEPARATOR ',') as concatenated
FROM table
GROUP BY art_id
HAVING concatenated = '12,13,15'
If the sequence in each group can be different. E.g. 13,12,15 then you'll need to sort the combinations too.
SELECT art_id,
GROUP_CONCAT(DISTINCT cat_id ORDER BY cat_id ASC SEPARATOR ',') AS concatenated
FROM table
GROUP BY art_id
HAVING concatenated = '12,13,15'
I hope this will work for you
SELECT art_id
FROM table
WHERE cat_id IN (12,13,15)
AND (art_id = 4 OR art_id = 7)
GROUP BY art_id
HAVING COUNT(cat_id) > 2
You can try this, but it works only if you haven't art_id, cat_id repetition eg: it doesn't work if you add in your sample data INSERT INTO T1 VALUES (9,15) two times
SELECT DISTINCT T1.art_id
FROM T1
INNER JOIN (SELECT art_id, COUNT(*) AS RC
FROM T1
WHERE CAT_ID IN (12,13,15)
GROUP BY art_id) X ON T1.art_id = X.art_id
WHERE X.RC>2
Output:
art_id
-----------
1
7
I have a table like the following, named matches:
match_id ( AUTO INCREMENT )
user_id ( INT 11 )
opponent_id ( INT 11 )
date ( TIMESTAMP )
What I have to do is to SELECT the count of the rows where user_id and opponent_id are a unique pair. The goal is to see the count of total matches started between different users.
So if we have:
user_id = 10 and opponent_id = 11
user_id = 20 and opponent_id = 22
user_id = 10 and opponent_id = 11
user_id = 11 and opponent_id = 10
The result of the query should be 2.
In fact we only have 2 matches that have been started by a couple of different users. Match 1 - 3 - 4 are the same matches, because played by the same couple of user IDs.
Can anyone help me with this?
I have done similar queries but never on pairs of IDs, always on a single ID.
FancyPants answer is correct, but I prefer to use DISTINCT when no aggregate function is used:
SELECT COUNT(DISTINCT
LEAST(user_id, opponent_id),
GREATEST(user_id, opponent_id)
)
FROM yourtable;
is sufficient.
SELECT COUNT(*) AS nr_of_matches FROM (
SELECT
LEAST(user_id, opponent_id) AS pl1,
GREATEST(user_id, opponent_id) AS pl2
FROM yourtable
GROUP BY pl1, pl2
) sq
see it working in an sqlfiddle
I have the following tables:
TABLE product
id int(11)
title varchar(400)
TABLE tag
id int(11)
text varchar(100)
TABLE product_tag_map
product_id int(11)
tag_id int(11)
PRODUCT_TAG_MAP maps tags to product. The distribution of tags in the system isn't normal, i.e., some tags have much more products than others.
I'm trying to write an SQL that will fetch 25 random products: 5 products per tag, for 5 tags (so that's 5x5 = 25).
Found an answer here: How can I get an even distribution using WHERE id IN(1,2,3,4)
But this doesn't yield random products - it always fetches the same products per tag.
Here is the SQL I have:
SET #last_tag = 0;
SET #count_tag = 0;
SELECT DISTINCT id FROM (
SELECT
product.*,
#count_tag := IF(#last_tag = product_tag_map.tag_id, #count_tag, 0) + 1 AS tag_row_number,
#last_tag := product_tag_map.tag_id
FROM product
LEFT JOIN product_tag_map ON (product_tag_map.product_id=product.id)
WHERE
product_tag_map.tag_id IN (245,255,259,281,296)
) AS subquery WHERE tag_row_number <= 5;
How do I make it return random products per tag?
Any help would be much appreciated! Thanks.
There is a lot of tricks in this query :
Add a level of nesting for use a LIMIT in a subquery : mySQL subquery limit
Add a row_number functionality for MySQL : How to select the first/least/max row per group in SQL
The final result is a lot of subquery:
SELECT tag.Name, t0.Id as MapId
FROM
(
SELECT *
, #num := if(#type = tag_id, #num + 1, 1) as row_number
, #type := tag_id as dummy
FROM (
SELECT *
FROM map m
WHERE tag_id in
(
SELECT *
FROM
(
SELECT id
FROM tag
ORDER BY RAND() LIMIT 5
) t
)
ORDER BY tag_id, RAND()
) mainTable
, (SELECT #num:=0) foo
, (SELECT #type:=0) foo2
) t0
INNER JOIN tag
ON t0.tag_id = tag.id
WHERE row_number <= 5
SQL Fiddle
The idea is to select first 5 random tags. This is not difficult, just a simple ORDER BY RAND() LIMIT 5.
Then the tricky part is too simulate a ROW_NUMBER() OVER(PARTITION BY tag_id, RAND()), because ranking each item randomly, but partition by tag is exactly what you need. So you declare variable and do as the query show.
Finally, filter the row_number, and you have your 25 random items!
I also want to offer the "brute" force approach. This will work in most databases (although the rand() function may be named something else).
select content_item_id from content_item where tag_id = 245 order by RAND() limit 5
union all
select content_item_id from content_item where tag_id = 255 order by RAND() limit 5
union all
select content_item_id from content_item where tag_id = 259 order by RAND() limit 5
union all
select content_item_id from content_item where tag_id = 281 order by RAND() limit 5
union all
select content_item_id from content_item where tag_id = 206 order by RAND() limit 5
The performance for this might be ok, if you have an index on content_item(tag_id).
I have this table
id fruit
---------
1 apple
2 banana <--
3 apple
4 apple
5 apple
6 apple
7 banana <----
8 apple
9 banana
10 apple
And I want to select rows until 2 bananas are found, like
SELECT id FROM table_fruit UNTIL number_of_bananas = 2
So the result would be 1,2,3,4,5,6,7
How could I achieve this?
thanks
I wish I could give credits to all of you who answered my question. I'v tested all of them, and they all work perfectly (got the expected result).
Though answers of Devart and ypercube seem a little bit complex and difficult for me to understand.
And since AnandPhadke was the first one provided a working solution, I'll choose his answer as accepted.
You guys are awesome, thanks!
Try this query -
SELECT id, fruit FROM (
SELECT
b.*, #b:=IF(b.fruit = 'banana', 1, 0) + #b AS banana_number
FROM
bananas b,
(SELECT #b := 0) t
ORDER BY id) t2
WHERE
banana_number < 2 OR banana_number = 2 AND fruit = 'banana'
SQLFiddle demo
select * from tables where id <=
(
select id from (
select id from tables where fruit='banana'
order by id limit 2) a order by id desc limit 1
)
SQLFIDDLE DEMO
#Devart's answer is perfect but it's an alternative option to we can use:
SELECT * FROM table_fruit WHERE id <=
(
SELECT id FROM
(SELECT id FROM table_fruit WHERE fruit='banana' ORDER BY id LIMIT 2) a
ORDER BY ID DESC LIMIT 1
);
Or using MAX
SELECT * FROM table_fruit WHERE id <=
(
SELECT MAX(id) FROM
(SELECT id FROM table_fruit WHERE fruit='banana' ORDER BY id LIMIT 2) a
);
See this SQLFiddle
select * from table_fruit where id <=
(
select max(id) from
(select id from table_fruit where fruit='banana' order by id limit 2) t
)
If there are less than 2 rows with 'banana', this will return all rows of the table:
SELECT t.*
FROM table_fruit AS t
JOIN
( SELECT MAX(id) AS id
FROM
( SELECT id
FROM table_fruit
WHERE fruit = 'banana'
ORDER BY id
LIMIT 1 OFFSET 1
) AS lim2
) AS lim
ON t.id <= lim.id
OR lim.id IS NULL ;