I have two tables that I join like this:
SELECT * FROM color_photo c
LEFT JOIN photos p
ON c.photo_id = p.id
WHERE c.color_id IN (4,9,11)
Above gives the following result:
Array
(
[id] => 4
[photo_id] => 4
[color_id] => 4
[path] => photos/419d7f6ce1043f472faf44ee517c3c67.jpg
[preview] => images/photos/previews/preview-9eba598915ea34de6d80b537a25610f3.jpg
[thumbnail] => images/photos/thumbnails/thumb-9eba598915ea34de6d80b537a25610f3.png
[name] => golden retriever mix dog walking in the forest.jpg
[extension] => jpg
[photographer_id] => 1
[popularity] => 0
[order] => 500
[created_at] => 2021-07-16 11:32:55
[updated_at] => 2021-07-16 11:32:55
)
Array
(
[id] => 2
[photo_id] => 2
[color_id] => 9
[path] => photos/604af943f1565e02de8f3d98202f6226.jpg
[preview] => images/photos/previews/preview-5fbba3dc932f4304712dfc65860679d1.jpg
[thumbnail] => images/photos/thumbnails/thumb-5fbba3dc932f4304712dfc65860679d1.png
[name] => 97903847_m.jpg
[extension] => jpg
[photographer_id] => 1
[popularity] => 8
[order] => 500
[created_at] => 2021-06-23 10:08:49
[updated_at] => 2021-07-16 15:01:01
)
Array
(
[id] => 3
[photo_id] => 3
[color_id] => 9
[path] => photos/9bc2dc45f3473252d4353446c8dbc168.jpg
[preview] => images/photos/previews/preview-9bc2dc45f3473252d4353446c8dbc168.jpg
[thumbnail] => images/photos/thumbnails/thumb-9bc2dc45f3473252d4353446c8dbc168.png
[name] => 94507149_m.jpg
[extension] => jpg
[photographer_id] => 1
[popularity] => 7
[order] => 499
[created_at] => 2021-06-23 10:09:46
[updated_at] => 2021-07-20 10:09:42
)
Array
(
[id] => 1
[photo_id] => 1
[color_id] => 11
[path] => photos/c3b2e455ded9f954fed6a4e039dc6cfa.png
[preview] => images/photos/previews/preview-c3b2e455ded9f954fed6a4e039dc6cfa.jpg
[thumbnail] => images/photos/thumbnails/thumb-c3b2e455ded9f954fed6a4e039dc6cfa.png
[name] => a2logo_opacity_big.png
[extension] => png
[photographer_id] => 1
[popularity] => 7
[order] => 500
[created_at] => 2021-06-23 10:08:21
[updated_at] => 2021-07-04 18:33:14
)
Array
(
[id] => 3
[photo_id] => 3
[color_id] => 11
[path] => photos/9bc2dc45f3473252d4353446c8dbc168.jpg
[preview] => images/photos/previews/preview-9bc2dc45f3473252d4353446c8dbc168.jpg
[thumbnail] => images/photos/thumbnails/thumb-9bc2dc45f3473252d4353446c8dbc168.png
[name] => 94507149_m.jpg
[extension] => jpg
[photographer_id] => 1
[popularity] => 7
[order] => 499
[created_at] => 2021-06-23 10:09:46
[updated_at] => 2021-07-20 10:09:42
)
The important rows for my question are photo_id and color_id. A photo can have multiple colors. As you can see photo_id: 3 is present 2 times both with a different color_id. These color_ids are posted to this query from another page, so the (4,9,11) are dynamic and can be any combination.
I want to be able to filter photos by color. So for example a photo of a dog has three colors, 4,9,11, the dog with photo_id 2 should show up. But when posting a color id that the dog photo does not have, for example 12, so you get 4,9,11,12 I want the dog to be removed from the result.
I know IN is not the correct SQL for this because it will just retrieve data that matches any of those color_ids. I want to retrieve only the photos that match all color_ids, and if there is one that doesn't match, remove this photo_id from the result entirely. How can I do this? I'm using MYSQL.
I've tried it like this:
SELECT * FROM color_photo c
LEFT JOIN photos p
ON c.photo_id = p.id
WHERE c.color_id = 4 AND c.color_id = 9
And
SELECT * FROM color_photo c
LEFT JOIN photos p
ON c.photo_id = p.id
WHERE c.color_id IN (4) AND c.color_id IN (9)
But both return no data.
A simple method for an exact match uses aggregation and filtering using GROUP_CONCAT():
SELECT p.*
FROM color_photo c LEFT JOIN
photos p
ON c.photo_id = p.id
GROUP BY p.id
HAVING GROUP_CONCAT(c.color_id ORDER BY c.color_id) = '1,4,9';
If you wanted a subset match (so, there could be more colors in the photo), I would instead recommend:
SELECT p.*
FROM color_photo c LEFT JOIN
photos p
ON c.photo_id = p.id
WHERE c.color_id IN (1, 4, 9)
GROUP BY p.id
HAVING COUNT(*) = 3;
In order to exclude a photo with 4, 9 and 4, 9, 11, 999 when searching for 4, 9, 11 you could use the following:
SELECT p.id
FROM photos p
INNER JOIN color_photo c ON p.id = c.photo_id
GROUP BY p.id
HAVING COUNT(*) = 3
AND COUNT(CASE WHEN c.color_id IN (4, 9, 11) THEN 1 END) = 3
Related
I would like to join 2 tables (contact and contact_meta) in ONE sql query knowing that contact_meta has several rows
Contact
id fname lname email
1 Nick John njohn#gmail.com
2 Laura Pitt lpitt#gmail.com
Contact_meta
id_contact contact_meta_key contact_meta_value
1 Newsletter yes
1 Level weak
2 Newsletter yes
I tried
SELECT * FROM contact as c
JOIN contact_meta as cm ON c.id = cm.id_contact
but where there are more than one row in contact_meta, I only get the last one.
Array (
[0] => stdClass Object ( [id] => 1 [lname] => Nick [fname] => John [email] => njohn#gmail.com [contact_meta_key] => level [contact_meta_value] => weak )
[1] => stdClass Object ( [id] => 2 [lname] => Laura [fname] => pitt [email] => lpitt#gmail.com [contact_meta_key] => newsletter [contact_meta_value] => yes
);
Moreover, I would like [newsletter] => yes instead of [contact_meta_key] => newsletter [contact_meta_value] => yes
What I wish :
Array (
[0] => stdClass Object ( [id] => 1 [lname] => Nick [fname] => John [email] => njohn#gmail.com [level] => weak [newsletter] => yes)
[1] => stdClass Object ( [id] => 2 [lname] => Laura [fname] => pitt [email] => lpitt#gmail.com [newsletter] => yes
);
You need to pivot the meta table.
SELECT c.*,
MAX(IF(contact_meta_key = 'Newsletter', contact_meta_value, NULL)) AS newsletter,
MAX(IF(contact_meta_key = 'Level', contact_meta_value, NULL)) AS level
FROM contact AS c
LEFT JOIN contact_meta AS cm ON c.id = cm.id_contact
GROUP BY c.id
DEMO
The thing is today John has 2 entries in contact_meta but tomorrow it can be 5 and Laura 9, I store many datas in it. If it possible to get all meta datas contact from users in 1 SQL query.
I found a solution but I think not good, because 2 SQL queries (I use Wordpress) :
$user = $wpdb->get_results( "SELECT * FROM contact as c JOIN relation_browser_contact as rbc ON c.contact_id = rbc.id_contact JOIN browser as b ON rbc.id_browser = b.browser_id");
$fellows = array();
$i=0;
foreach ($user as $key => $value) {
$data=array();
foreach ($user[$i] as $key => $value) {
if ( Helper::encrypt_decrypt('decrypt', $value) ){
$data[$key] = Helper::encrypt_decrypt('decrypt', $value);
}else{
$data[$key] = $value;
}
$metas = $wpdb->get_results( "SELECT contact_meta_key, contact_meta_value FROM contact_meta WHERE id_contact = ".$user[$i]->contact_id );
foreach ($metas as $key => $value) {
$data[$value->contact_meta_key] = $value->contact_meta_value;
}
}
$fellows[] = $data;
$i++;
}
print_r($fellows);
In this case, I have all I want, but just wondering if SQL query can do it...
The tables in this query are as follows:
Post
User
Comment
Tag
Tagged_Post
Post_Category
I'm trying to query all relevant information about a post which has relations such as the User who made the post, comments on that specific Post, the many or none tags on the Post, and category that the Post is in.
Here is my SQL query:
$sql = "SELECT post.*, user.name, user.avatar, group_concat(DISTINCT tag.slug) as tags, post_category.slug as category, count(comment.post_id) as comments
FROM post
INNER JOIN user on user.id = post.user_id
INNER JOIN post_category on post_category.id = post.category_id
LEFT JOIN tagged_post on tagged_post.post_id = post.id
LEFT JOIN tag on tagged_post.tag_id = tag.id
LEFT OUTER JOIN comment on post.id = comment.post_id
GROUP BY post.id";
This outputs the following:
Array
(
[0] => Array
(
[id] => 1
[user_id] => 1
[category_id] => 1
[title] => Hi, I'm Bob Ross. AMA
[body] => That's right. I'm bob ross and this is my post. I'm not dead btw
[date_created] => 2018-09-02 11:45:29
[date_modified] =>
[name] => bob_ross
[avatar] =>
[tags] => painting,ama
[category] => news-and-politics
[comments] => 6
)
[1] => Array
(
[id] => 2
[user_id] => 2
[category_id] => 2
[title] => I'm Saul Goodman!!
[body] => woohoo
[date_created] => 2018-09-02 12:12:12
[date_modified] =>
[name] => saul_goodman
[avatar] =>
[tags] =>
[category] => general-discussion
[comments] => 0
)
[2] => Array
(
[id] => 3
[user_id] => 3
[category_id] => 4
[title] => yo im jesse
[body] => test
[date_created] => 2018-09-02 12:24:45
[date_modified] =>
[name] => jesse_pinkman
[avatar] =>
[tags] => ama,painting
[category] => animals-and-nature
[comments] => 4
)
)
The amount of tags seems to be affecting the count on the comments. For instance, on the first post, there are 3 comments and 2 tags. The count for comments on post with ID of 1, is showing 6. If I were to add an additional tag on this post (3 total tags), then the comment count would display 9 (3 tags x 3 comments).
Can someone help me understand why this is happening?
The reason is that using multiple JOINs acts like a Cartesian product, so you get 2*3=6 rows for the group. When you apply count, you get 6 valid (non-null) values and that's your result.
To fix, use:
... COUNT(DISTINCT comment.comment_id) as comments
I have a messages table with following columns
id | message | toId | fromId | {few more columns}
I am trying to list recent conversations list. Consider a case where i with id 1 has a chat with id 2
some rows will be like
1 | First Message | 1 | 2 | {few more columns}
2 | Sec Message | 2 | 1 | {few more columns}
3 | Third Message | 1 | 3 | {few more columns}
Here is my code to get unique conversations based on messages
$result = $this->Message->find('all', array(
'conditions' => $conditions,
'joins' => array(
array(
'table' => 'users',
'alias' => 'from',
'type' => 'LEFT',
'conditions' => array(
'from.id = Message.from',
)
),
array(
'table' => 'users',
'alias' => 'to',
'type' => 'LEFT',
'conditions' => array(
'to.id = Message.to',
)
),
),
'fields' => array('from.id','from.firstname','from.username', 'from.lastname','from.imagePath',
'to.id','to.firstname','to.username', 'to.lastname','to.imagePath',
'Message.*'),
'order' => array('Message.created DESC'),
'limit' => 20,
'offset' => $offset * 20,
));
This above code is just simply fetching all the messages and attaching their users. However i am not sure how can i put condition based on unique conversations.
In above example there are two conversations, between 1 and 2 and between 1 and 3
How can i list only 2 messages in response that have unique combination
"SELECT From.id, From.firstname, From.username,
From.lastname, From.imagePath, To.id, To.firstname,
To.username, To.lastname, To.imagePath, Message.* FROM
xxxx.messages AS Message LEFT JOIN xxxx.users AS To
ON (Message.to = To.id) LEFT JOIN xxxx.users AS From
ON (Message.from = From.id) LEFT JOIN xxxx.messages AS
message ON (((Message.from = message.from AND Message.to
= message.to) OR (Message.from = message.to AND Message.to = message.from)) AND Message.created <
message.created) WHERE message.id IS NULL ORDER BY
Message.created DESC LIMIT 20",
To get the latest conversation from messages per unique toId and fromId you can use self join with some conditional clauses in on() part
SELECT m.*
FROM
message m
LEFT JOIN message m1 ON (
(
(m.fromId = m1.fromId AND m.toId = m1.toId )
OR
(m.fromId = m1.toId AND m.toId = m1.fromId )
)
AND m.created < m1.created
)
WHERE m1.id IS NULL
ORDER BY m.id
DEMO
Later on you could join you users table with above query to get user related information
I have a problem and there are already some similar questions but they didn't solve my problem.
I have several hotels and pictures in it. The pictures are ordered by a number. I want all hotels and the picture with the lowest number.
When I try following query I get the right order:
SELECT s25.entry_id AS id,
s25.value AS title,
s35.file AS picture,
s86.value AS picture_sort
FROM sym_entries_data_25 AS s25
LEFT JOIN sym_entries_data_34 AS s34 ON (s25.entry_id = s34.relation_id)
LEFT JOIN sym_entries_data_35 AS s35 ON (s34.entry_id = s35.entry_id)
LEFT JOIN sym_entries_data_86 AS s86 ON (s34.entry_id = s86.entry_id)
ORDER BY s86.value
I will get the Array
[0] => Array
(
[id] => 243
[title] => Hotel
[picture] => louis2.jpg
[picture_sort] => 1
)
[1] => Array
(
[id] => 243
[title] => Hotel
[picture] => louis1.jpg
[picture_sort] => 2
)
[2] => Array
(
[id] => 243
[title] => Hotel
[picture] => louis3.jpg
[picture_sort] => 3
)
...And so on
But I only want one result per hotel, so I tried a GROUP BY:
SELECT s25.entry_id AS id,
s25.value AS title,
s35.file AS picture,
s86.value AS picture_sort
FROM sym_entries_data_25 AS s25
LEFT JOIN sym_entries_data_34 AS s34 ON (s25.entry_id = s34.relation_id)
LEFT JOIN sym_entries_data_35 AS s35 ON (s34.entry_id = s35.entry_id)
LEFT JOIN sym_entries_data_86 AS s86 ON (s34.entry_id = s86.entry_id)
GROUP BY s25.value
ORDER BY s86.value
Then I only get one Result but a random one, not the first one:
[0] => Array
(
[id] => 243
[title] => Hotel
[picture] => louis3.jpg
[picture_sort] => 3
)
What could be the problem here?
when you are doing group by, you have no guarantee regarding the values of the columns you are not grouping by.
for example if you have a table with columns a and b, and data like:
a | b
-----
1 | 2
1 | 3
if you'll do select * from table group by a, you can get either 2 or 3 as the value of b.
if you only want the top result for the hotel, what you need to do is not group by, but take the result where picture_sort equals to the min(picture_sort)
First things first, I know this is incorrect currently. I know this because it doesn't work. So with that said, any assistance in getting me closer to my desired result is appreciated.
I have a query:
$query = "SELECT cat.*,
prod.title AS product,
cat1.title AS category,
(SELECT cat2.*
FROM ic_store_catalog AS cat2
WHERE cat2.parentid = cat.parentid) AS children
FROM ic_store_catalog AS cat
LEFT JOIN ic_products AS prod
ON cat.productid = prod.productid
LEFT JOIN ic_product_categories AS cat1
ON cat.categoryid = cat1.categoryid
WHERE `storeid` = $storeid
AND `column` = $column
AND `parentid` = 0
ORDER BY `the_order` ASC";
This errors out giving me: Operand should contain 1 column(s) as the problem. This exists in the sub-select guaranteed.
Now what I need to achieve when I use print_r in PHP is an Array similar in structure to this:
[0] => Array
(
[catalogID] => 165
[storeID] => 0
[categoryID] => 7
[parentID] => 0
[productID] => 4
[title] =>
[column] => 1
[the_order] => 1
[cat_order] => 1
[category] => Cookies & Brownies
[children] => Array
(
[0] => Array (
[catalogID] => 166
[storeID] => 0
[categoryID] => 8
[parentID] => 7
[productID] => 5
[the_order] => 1
[cat_order] => 1
[category] => Brownies
)
)
)
Any help in getting me closer to this result is appreciated.
(SELECT cat2.*
FROM ic_store_catalog AS cat2
WHERE cat2.parentid = cat.parentid)
You should replace cat2.* by only one column that will represent children.