I have the following 3 tables:
(PK = Primary Key, FK = Foreign Key)
Files Table
File ID (PK) File Name ...
------------ ---------
1 a.jpg ...
2 b.png ...
3 c.jpg ...
. .
. .
. .
Tags Table
Tag ID (PK) Tag Name ...
----------- ----------
1 Melbourne ...
2 April ...
3 2010 ...
. .
. .
. .
Files_Tags Table
File ID (FK) Tag ID (FK)
------------ -----------
1 1
1 5
1 7
2 2
2 4
3 3
. .
. .
. .
In PHP, I want to get a list of all tags along with the number of times the tag appears (i.e. the number of files that have this tag).
Is that possible to do with one MySQL query ?
Try GROUP BY on your tag id. Use a LEFT JOIN to include tags that exist in the tags table but aren't ever used.
SELECT
Tag_Name,
COUNT(Files_Tags.Tag_ID) AS cnt
FROM Tags
LEFT JOIN Files_Tags
ON Tags.Tag_ID = Files_Tags.Tag_ID
GROUP BY Tags.Tag_ID
Result:
Melbourne 1
April 1
2010 1
... ...
You may also want to add an ORDER BY Tag_Name or an ORDER BY COUNT(*) if you want the rows returned in sorted order.
Daniel Vassello also submitted an answer but deleted it. However his answer is quite easy to adapt to meet your new requirements. Here is his solution, modified to use a LEFT JOIN instead of an INNER JOIN:
SELECT t.tag_id,
t.tag_name,
IFNULL(d.tag_count, 0) AS tag_count
FROM tags t
LEFT JOIN
(
SELECT tag_id, COUNT(*) tag_count
FROM files_tags
GROUP BY tag_id
) d ON d.tag_id = t.tag_id;
You shouldn't use too much of GROUP BY, ORDER BY and * JOIN as those query are very heavy and it's not something you should base your code on.
If I was you, I would do multiple simple SELECT query and group the stuff together using PHP algorithms. This way, you're DB won't be hit by very slow query.
So basically, in your specific question I would have more than 1 query.
I would start by doing a
SELECT * FROM "tags_table".
In php, I would created a foreach loop that would count appearance of every tag in your "files_tags" table:
SELECT FILE_ID COUNT(*) FROM TAGS_TABLE WHERE TAG_ID = 'tag_uid'
It's mostly pseudo-code so I wouldn't expect those query to work but you get the general idea.
Related
I know that this question already asked many times here but after all I could't found my answer that what I want.
My Question is:
I have two tables and the structure of these tables is as:
table1:
item_id, store,title,available,shipping
table2:
item_id, review_rate,user_id,review_title
These tables should be join as one to many relation.
For example if the data in these tables is as:
table1:
item_id store title available shipping
-------------------------------------------------------
11 glasses ..........................
12 dresses ..........................
.
.
.
table2:
item_id review_rate user_id review_title
--------------------------------------------------
11 3 10023 good item
11 5 10024 nice item
12 1 10024 nice one
.
.
.
then the result should be as after joining:
afterJoin:
item_id store title available shipping rate people_reviewed
-----------------------------------------------------------------------
11 .................................... 4 2
12 .................................... 1 1
The query I tried to join is as:
CREATE OR REPLACE VIEW afterJoin AS
SELECT i.*,round(AVG(r.review_rate)) as rate,count(r.user_id) as people_reviewed
FROM table1 i
RIGHT JOIN table2 r ON i.item_id = r.item_id
but this return only one row.
Your query is missing a GROUP BY clause. Without it your database is aggregating all of the records together.
SELECT i.*, round(AVG(r.review_rate)) as rate, count(r.user_id) as people_reviewed
FROM table1 i
RIGHT JOIN table2 r ON i.item_id = r.item_id
GROUP BY i.item_id
The GROUP BY instructs the db to aggregate for each item_id.
I have some mysql tables like below,
1)videos
id name
1 test1
2 test2
2)tags
id name
1 theme1=test1
2 theme1=test2
3 theme2=test1
4 theme2=test2
5 age=senior
6 age=children
3)tags_to_items
vid tagid
1 1
1 5
Here in table tags_to_item you see videos.id=1 has 2 tags
1)theme1=test1 AND 2)age=senior. It means video.id=1 has 2 tags.
Now here is one conflict,
what i am doing now is, i am searching for theme's only. It means if i search where theme1=theme1 then it need to also search theme1 in theme2 and vice versa. like this
WHERE tagid=1 OR tagid=2
This is functioning proper, but now i want to search if video have multiple tag with AND condition like this
WHERE tagid=5 AND tagid IN (1,2)
It should return video.id=1
So, probably it need to search where video 1 contains tag tag age=senior AND theme1=theme1 OR theme2=theme1. But its not working, anyone have idea how can i do like this?
I think i got the answer.
I need to do join like this,
INNER JOIN tags_to_items t2 ON (v.id = t2.tagid AND t2.tagid = 5)
INNER JOIN tags_to_items t3 ON (v.id = t3.tagid AND t3.tagid in ('27' ,'69','84' ,'99' ))
I'm using this query to collate two sets of results but I now need to use JOIN instead of UNION to get the second part of the data from another table.
However I need quite a lot of fields and can't seem to find a way to maintain the use of SELECT * when using JOIN.
mysql_query("SELECT * FROM table.products WHERE category='$cat' GROUP BY product_id ORDER BY id UNION ALL SELECT * FROM table.products WHERE type='red' GROUP BY product_id ");
Table - products
product_id | title | category | id
0 one home 10
1 two home 11
1 two - a home 12
2 three work 13
Table - product_details
product_id | type | size |
0 blue S
1 blue M
1 red L
Ultimately I need to list every product in the first table for a given category e.g home,
as there is sometimes two entries or more for a single product id, I need to only select one row for each product id value. I also need to join the second table so I can get the size info, however I must be able to get the size info by preferring a type e.g red.
So for this example I would get a list like:
product_id | title | category | type | size
0 one home blue S
1 two home red L
This excludes product_id 2 as it's not in the home category, the first entry for product_id equaling 1 is selected because of the GROUP BY and ORDER BY and the information on size for product_id 1 is L because it is of type red not blue.
Assuming you are using MySQL, you want a join with an aggregation or aggressive filtering. Here is an example using join and aggregation:
select p.product_id, p.title, p.category,
substring_index(group_concat(pd.type order by pd.type = 'red' desc, pd.type), ',', 1) as type,
substring_index(group_concat(pd.size order by pd.type = 'red' desc, pd.type), ',', 1) as size
from products p join
product_details pd
on p.product_id = qpd.product_id
where p.category = 'home'
group by p.product_id;
The expression substring_index(group_concat(. . .)) is choosing one type (and one size) with precedence given to the red type.
Your query can be simplified like below since you are using the same table table.products. Not sure why you need to UNION them.
SELECT * FROM table.products
WHERE category='$cat'
and type='red'
GROUP BY product_id
EDIT:
With your edited post, the query should look like
select p.product_id,p.title,p.category,q.type,q.size
from products p join product_details q
on p.product_id = q.product_id
where p.category = 'home'
and q.type = 'red'
I think this will be an easy one for those using MYSQL a lot, but I just can't quite get it...mainly through not knowing the right terminology to search for.
Basically I have a table mapping tag ids to photo ids called tag_map. When I perform this query:
SELECT * FROM tag_map WHERE tag_id='1' OR tag_id='5';
I get the following results:
ph_id tag_id
1 1
2 1
5 1
7 5
8 1
9 5
10 5
11 1
12 1
13 1
But what I really want is to only select ph_id that have a tag_id of BOTH '1' and '5'.
So, as you can probably see, I am trying to filter down selections based on multiple tags. I want to end up with:
ph_id tag_id
7 1
7 5
8 1
8 5
11 1
11 5
So ph_id 7, 8 and 11 reference tag_id 1 AND 5.
Hope that makes sense.
Thanks.
Solution
Due to the dynamic nature of my query (user selecting any number of available tags to 'narrow down selection) I went with a PHP solution, as suggested by #Y U NO WORK
Basically I get the tag_id of all selected tags from table 'tags'
Then I selected all photo ids (ph_id) that are mapped to the selected tag_ids from my table tag_map.
Then I reduce this down to ph_ids that occur the same number of times as the number of selected tags:
$numTags = count($arTagId); //$arTagId is an array of the selected tags
// get only photo ids that match both tags
$arPhId = array();
// in arPhId, find ph_ids that have occurances equal to $numTags
$arPhIdCnt = array_count_values($arPhIdAll); //$arPhIdAll is array of all ph_id that match all selected tag_ids
foreach($arPhIdCnt as $pid => $pidQty) {
if($pidQty == $numTags) {
$arPhId[] = $pid;
}
}
So I end up with an array of only the ph_ids that match both tag_ids.
Thanks for everyone's help.
You will have to join the table with itself, the code could be kinda complicated. PHP would be an easier, but not such a performant solution.
You have to join the table with itself based on ph_id, then check that the tab_id col of table1 instance equals 1 and that tab_id of table2 instance equals 5.
SELECT t1.* FROM tag_map t1, tag_map t2
WHERE t1.ph_id = t2.ph_id
AND t1.tag_id='1'
AND t2.tag_id='5';
with inner join if you prefer
SELECT t1.* FROM tag_map t1
INNER JOIN tag_map t2 on t2.ph_id=t1.ph_id
WHERE t1.tag_id='1'
AND t2.tag_id='5';
Any time there is an update within my music community (song comment, artist update, new song added, yadda yadda yadda), a new row is inserted in my "updates" table. The row houses the artist id involved along with other information (what type of change, time and date, etc).
My users have a "favorite artists" section where they can do just that -- mark artists as their favorites. As such, I'd like to create a new feature that shows the user the changes made to their various favorite artists.
How should I be doing this efficiently?
SELECT *
FROM table_updates
WHERE artist_id = 1
OR artist_id = 500
OR artist_id = 60032
Keep in mind, a user could have 43,000 of our artists marked as a favorite.
Thoughts?
This depends on how your database is setup. If I had my way, I'd set it up with a table like so:
Table: user_favourite_artist
user_id | artist_id
---------------------
1 | 2
1 | 8
1 | 13
2 | 2
3 | 6
6 | 20
6 | 1
6 | 3
user_id and artist_id together would be a composite primary key. Each row specifies a user, by id, and an artist they have as a favourite, by id. A query like so:
SELECT artist_id FROM user_favourite_artist WHERE user_id = 1
Would give you the artist_id's 2, 8, and 13. This is a very simple query that will scale to your expectations.
On the reverse, when an artist is updated, you'd run this query:
SELECT user_id FROM user_favourite_artist WHERE artist_id = 2
And you would get the user_id's 1 and 2. This will tell you which users to notify. This query is also simple and will scale.
Maybe you can try this:
SELECT *
FROM table_updates
WHERE artist_id IN(1, 500, 60032)
If you have the marked artists in a secondary table, I would recomend rather using a join.
Something like
SELECT *
FORM table_updates tu INNER JOIN
table_marked_by_user tmbu ON tu.artist_id = tmbu.artist_id
WHERE tmbu.user_id = $user_id
If you're on SQL Server, you can use a nested select statement:
select * from table_updates where artist_id in
(select artist_id from favorites_table where user_id = 10)
If you don't mind doing dirty reads, you can speed it up with (nolock).
select * from table_updates (nolock) where artist_id in
(select artist_id from favorites_table (nolock) where user_id = 10)