MySQL GROUP BY kills ORDER BY - mysql

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)

Related

Only get data when value matches 100% of multiple other values SQL

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

SQL - Query count for related table affected by other join

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

mysql ColumnName AS 1, ColumnName AS 2, with WHERE, using UNION for all columns get AS 1

Have table like this
IdRows | UpperLevelIdRows | CategoriesName |
-------------------------------------------------
2 | 0 | Transport
4 | 2 | Cars
12 | 4 | Alfa Romeo
Query
SELECT IdRows AS IdRows1, CategoriesName AS CategoriesName1 FROM categories
WHERE UpperLevelIdRows = ?
UNION
SELECT IdRows AS IdRows2, CategoriesName AS CategoriesName2 FROM categories
WHERE UpperLevelIdRows = ?
Data for placeholders is
Array
(
[0] => 2
[1] => 4
)
So
SELECT IdRows AS IdRows1 .... WHERE UpperLevelIdRows = 2
and
SELECT IdRows AS IdRows2 .... WHERE UpperLevelIdRows = 4
As result expect get array like
[0] => Array
(
[IdRows1] => 4
[CategoriesName1] => Cars
)
[1] => Array
(
[IdRows2] => 12
[CategoriesName2] => Alfa Romeo
)
But get array like this
[0] => Array
(
[IdRows1] => 4
[CategoriesName1] => Cars
)
[1] => Array
(
[IdRows1] => 12
[CategoriesName1] => Alfa Romeo
)
Instead of IdRows2 see IdRows1
If i execute only the second SELECT IdRows AS IdRows2 ..., then see as expected [CategoriesName2] => Alfa Romeo
Where is my mistake? What need to correct?
From the data i want to create select/option boxes. Like
First select box
echo '<select name="upper_level_id0" id="upper_level_id0" >
<option value="'.$IdRows1.'">'.$CategoriesName1.'</option>
</select>';
Second select box
echo '<select name="upper_level_id1" id="upper_level_id1" >
<option value="'.$IdRows2.'">'.$CategoriesName2.'</option>
</select>';
At the moment found solution using transaction. Loop through all SELECT ...
$db->beginTransaction();
foreach ( $sql_get_id_name as $k_sql => $val_sql ) {
$stmt_get_id_name = $db->prepare( $val_sql );
$stmt_get_id_name->execute( array( $data_get_id_name[$k_sql] ) );
$id_name[] = $stmt_get_id_name->fetchAll(PDO::FETCH_ASSOC);
}
$roll_back = $db->rollBack();
Use this select
select cp.IdRows p_id, cp.UpperLevelIdRows p_parent_id, cp.CategoriesName p_name,
cc.IdRows p_id, cc.UpperLevelIdRows c_parent_id, cc.CategoriesName c_name
from categories cc left join categories cp on cp.IdRows = cc.UpperLevelIdRows
where cc.UpperLevelIdRows = 4
This way you will get all the sons of a parent. The first 3 columns from the result are the parent (in your row data it will be the Cars) and the second its childs (in your row data the Alfa Romeo)

MySQL select row and join to multiple records

I have 2 Tables and can't get my MySQL Query to work:
First Table users:
user_id name
------------
1 Bob
2 Alice
Second Table fields:
id unser_id field_id value
-----------------------------
1 1 4 foo
2 1 5 10
3 2 4 bar
Now I would like to join the Tables in a way that outputs:
[0] => (
[user_id] => 1
[name] => Bob
[fields] => (
[4] => foo
[5] => 10
)
[1] => (
[user_id] => 2
[name] => Alice
[fields] => (
[4] => bar
)
)
Thanks a lot!
I tried:
SELECT u.user_id,
u.name
FROM users as u
INNER JOIN fields as f on u.user_id = d.user_id
But I get for every user just the first fields entry.
SELECT user_id,name,GROUP_CONCAT(value SEPARATOR ',')as field FROM users u JOIN fields f
ON u.user_id=f.unser_id GROUP BY user_id
Maybe something like this?
select u.user_id,u.name,f.field_id,f.value from users u LEFT JOIN fields f ON u.user_id = f.unser_id

sql select multiple rows in subselect

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.