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...
Related
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
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 need to distinct the left table rows on a left join.
Instead of this
| name | last name | phone |
|--------|-------------|---------|
| name1 | lastname1 | 1234567 |
|--------|-------------|---------|
| name1 | lastname1 | 2345678 |
|--------|-------------|---------|
| name2 | lastname2 | 3456789 |
I need this
| name | last name | phone |
|--------|-------------|---------|
| name1 | lastname1 | 1234567 |
| | | 2345678 |
|--------|-------------|---------|
| name2 | lastname2 | 3456789 |
I tried with a SELECT DISTINCT but without success...
I tried also a GROUP BY but it hides the second row
Mariano, followed you here from your WP post that got closed.
<?php
$results = your_sql_query_here();
$data = array();
foreach( $results as $result ) {
// Make a new array node for each name
if ( ! isset( $data[$result['name']] )
$data[$result['name']] = array();
$data[$result['name']][] = $result['phone'];
}
This will give you something like this
Array(
['name1'] => Array(
[0] => 5123451,
[1] => 5123452
),
['name2'] => Array(
[0] => 5123453,
[1] => 5123454
) )
You can then just do a for loop of your $data array, using the key as your name1 value.
Or store entire data sets
<?php
$results = your_sql_query_here();
$data = array();
foreach( $results as $result ) {
// Make a new array node for each name
if ( ! isset( $data[$result['name']] )
$data[$result['name']] = array();
$data[$result['name']][] = $result;
}
Now you will have access to all nodes, but grouped by name.
Array(
['name1'] => Array(
[0] => Array( 'name' => 'name1', 'phone' => 4165123, 'another_field' => 1 ),
[1] => Array( 'name' => 'name1', 'phone' => 4165157, 'another_field' => 0 ),
[1] => Array( 'name' => 'name1', 'phone' => 4225157, 'another_field' => 0 )
),
['name2'] => Array(
[0] => Array( 'name' => 'name2', 'phone' => 4165123, 'another_field' => 1 ),
[1] => Array( 'name' => 'name2', 'phone' => 4572321, 'another_field' => 1 ),
[1] => Array( 'name' => 'name2', 'phone' => 5235157, 'another_field' => 0 )
) )
You need GROUP_CONCAT:
This function returns a string result with the concatenated non-NULL values from a group.
So, your code will be:
SELECT name,
last_name,
GROUP_CONCAT(phone) AS phones
FROM table1
GROUP BY name, last_name;
Use GROUP_CONCAT() but beware of that fact it has the limit on character length of 1024 default,but can can be increased
SELECT name,
last_name,
GROUP_CONCAT(DISTINCT phone SEPARATOR ' ')
FROM table1
GROUP BY name, last_name
See fiddle Demo
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.