I want to filter my SQL query with some array of IDs.
so I have IDs for example: 2,3,4 and activity_meta.value 1,2,5;
And I want to find every activity where it has id in activity_meta.value
SELECT DISTINCT a.*, u.user_email, u.user_nicename, u.user_login, u.display_name
FROM wp_bp_activity a
LEFT JOIN wp_users u
ON a.user_id = u.ID
INNER JOIN wp_bp_activity_meta
ON (a.id = wp_bp_activity_meta.activity_id)
WHERE a.is_spam = 0
AND a.hide_sitewide = 0
AND a.type != 'activity_comment'
AND (wp_bp_activity_meta.meta_key = 'activity_tagz' )
DESC LIMIT 0, 20
I want to add AND (1,2,3 IN wp_bp_activity_meta.meta_value)
I just dont know how to treat serialized array;
The value of activity_meta.value is not normalized. It would be a better choice, to create a second table, where you assign the meta tags to your element.
So, it would look like:
wp_bp_activity_id | meta
1 | 1
1 | 2
1 | 3
let's cal this table "meta_relation". Then you simple could either use a subselect, like this:
... WHERE wp_bp_activity.id in (SELECT wp_bp_activity_id FROM meta_relation WHERE meta in (1,2,5))
Or you could implement this with another join, like
... INNER JOIN meta_relation ON wp_bp_activitiy.id = meta_relation.wp_bp_activity_id
(will return 3 rows then for 3 matching tags)
... WHERE meta in (1,2,5)
... GROUP BY wp_bp_activity.id
(will remove any duplicate result and unwanted tags)
For your current scenario you could use a workaround. However this requires to build up the query programmatically:
For each "Tag", you want to find (i.e. 1,2,5) you need to add another or-condition.
To make sure you are not matching the 2 within 125 you can sourround it by ,.
To make sure, you are not missing the FIRST or LAST item (which has no leading / trailing , you need to concatenate the column with 2 more , in the first place:
Query then contains the following additional criterias.
SELECT
....
WHERE
...
AND
(
CONCAT(CONCAT(",", activity_meta.value ), ",") LIKE "%,1,%" OR
CONCAT(CONCAT(",", activity_meta.value ), ",") LIKE "%,2,%" OR
CONCAT(CONCAT(",", activity_meta.value ), ",") LIKE "%,5,%"
)
if you want all 3 tags to appear, use AND instead of OR.
in your example, this will match :
,2,3,4, against ,1,
,2,3,4, against ,2, //match
,2,3,4, against ,5,
Order and / or gaps don't matter with this approach.
(Depending on whether its a 10 User Website or a Million-Customer-Portal, you can use either. Preferred Solution is to normalize your table.)
Related
Given two tables:
item_to_group ---> contain item_id , user_to_group_id
user_to_group ---> contain user_to_group_id , user_id, // other not relevant columns like group name...
Here my mysql code:
SELECT *,
(SELECT COUNT(*) FROM item_to_group x
WHERE x.user_to_group_id = u.user_to_group_id) as tots
FROM user_to_group u
WHERE u.user_id = // [ USERID HERE ]
ORDER BY u.user_to_group_id DESC, tots DESC
I'm trying to display a list of groups, where at the top I have the groups with items in them, then the empty groups.
Imagine these groups like wishlists of items in a ecommerce. As a user I can build multiple groups.
eg.
currently I display
group 99 contains 2 items
group 98 contains 0 items // <--- empty
group 97 contains 5 items
group 96 contains 1 items
group 94 contains 0 items // <--- empty
group 93 contains 9 items
group 92 contains 3 items
but i want to display
group 99 contains 2 items
group 97 contains 5 items
group 96 contains 1 items
group 93 contains 9 items
group 92 contains 3 items
-------------------------
group 98 contains 0 items
group 94 contains 0 items
So I'd like to display the groups that are not empty first, chronologically (i don't store a timestamp but the group id should suffice i guess) THEN all the rest of the list of empty groups.
The problem here is that if I sort by id THEN totsi don't get the result I want.
I hope I am clear.
You want to sort on an additional column in the very first place of your ORDER BY clause (to make it most relevant). You cannot sort on tots directly, because that would break the ordering on user_to_group_id. Thus you need to map all tots values other than 0 to a a single value — e.g. 1. This can be done using CASE, giving the following ORDER BY clause:
ORDER BY CASE WHEN tots = 0 THEN 0 ELSE 1 END DESC
, u.user_to_group_id DESC
, tots DESC
Now, those having tots <> 0, will be ranked first, then those where tots = 0. The remaining sorting is the same.
If you'd like to learn more about CASE have a look at an article I wrote here: http://modern-sql.com/feature/case
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' ))
Here's the table structure
id | name | parent_id
1 fruit 0
2 apple 1
3 banana 1
4 orange 1
5 vegetable 0
6 carrot 5
7 tomato 5
I need get every row where a parent_id is 0, but I also need to to set an array on each row which is equal to 0 with the name of all it's children.
So I would have something like:
id = 1, name = fruit, children = [apple, banana, orange]
I'm aware there are better table structures but I must use the table structure stated.
I've tried getting all rows from the db then looping through them, if parent_id = 0 then push that to an array, otherwise it's a child so find parent in array and add it to that.
But there must be a better way?
I thinks you should use:
SELECT pr.id, pr.name, (
SELECT GROUP_CONCAT(name) FROM test as ch WHERE ch.parent_id = pr.id GROUP BY ch.parent_id
) AS children
FROM test as pr
WHERE parent_id = 0
Without thinking about performance or beauty:
SELECT t.id, name, (
SELECT GROUP_CONCAT(name) FROM table WHERE parent_id = t.id GROUP BY parent_id
) AS children
FROM table t
WHERE parent_id = 0
Avoiding a sub query, just using a self join the following should do it:-
SELECT t1.id, t1.name, GROUP_CONCAT(CONCAT('name:', t2.name))
FROM test t1
LEFT OUTER JOIN test t2
ON t1.id = t2.parent_id
WHERE t1.parent_id = 0
GROUP BY t1.id, t1.name
SQL fiddle for it:-
http://www.sqlfiddle.com/#!2/d4479a/1
I had come across a similar issue, where I had to fetch all subordinates of a user in a recursive manner.
We wrote a function where we loop through each of the child records recursively until we reach the final subordinate. We keep on concatenating the subbordinates we find in each loop.
Hope through this you can think of a solution and resolve your problem.
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';