Group only if condition is met - mysql

I have a table with fields TYPES, AMOUNT and COMMENT
I want to group by the TYPES field, but if the 'TYPES' value is "other" AND 'COMMENT' is not empty, I want those records to show up separately.
Example result:
+-----------+-----------+-----------+
| types | amount | comment |
+-----------+-----------+-----------+
| type1 | 27 | |
| type2 | 65 | |
| type3 | 45 | |
| other | 4 | blabla |
| other | 8 | something |
-------------------------------------
So instead of grouping the two "other" records, I want those records to show up separately (but only if they also have a comment)

If I understand correctly, you want all rows with a given type to grouped together unless the type is 'Other' and the comment is not NULL.
A close approximation is:
select types,
(case when types = 'Other' and comment is not null
then comment end) as comment,
sum(amount) as amount
from table t
group by types,
(case when types = 'Other' and comment is not null
then comment end);
The only issue is that rows with types = 'Other' and the same comment will be grouped together. To fix this correctly, you need a unique identifier on each row, which MySQL does not readily provide.

In your case I see two separate data sets. Try with union all as below
select types, amount,comment
from tab
where (types = 'other' and comment is not null)
or types <> 'other'
group by types
union all
select types, amount,comment
from tab
where types = 'other'
and comment is null

Related

How to select both sum value of all rows and values in some specific rows?

I have a record table and its comment table, like:
| commentId | relatedRecordId | isRead |
|-----------+-----------------+--------|
| 1 | 1 | TRUE |
| 2 | 1 | FALSE |
| 3 | 1 | FALSE |
Now I want to select newCommentCount and allCommentCount as a server response to the browser. Is there any way to select these two fields in one SQL?
I've tried this:
SELECT `isRead`, count(*) AS cnt FROM comment WHERE relatedRecordId=1 GROUP BY `isRead`
| isRead | cnt |
| FALSE | 2 |
| TRUE | 1 |
But, I have to use a special data structure to map it and sum the cnt fields in two rows to get allCommentCount by using an upper-layer programming language. I want to know if I could get the following format of data by SQL only and in one step:
| newCommentCount | allCommentCount |
|-----------------+-----------------|
| 2 | 3 |
I don't even know how to describe the question. So I got no any search result in Google and Stackoverflow. (Because of My poor English, maybe)
Use conditional aggregation:
SELECT SUM(NOT isRead) AS newCommentCount, COUNT(*) AS allCommentCount
FROM comment
WHERE relatedRecordId = 1;
if I under stand you want show sum of newComments Count and all comments so you can do it like
SELECT SUM ( CASE WHEN isRead=false THEN 1 ELSE 0 END ) AS newComment,
Count(*) AS AllComments From comments where relatedRecord=1
also you can make store procedure for it.
To place two result sets horizontally, you can as simple as use a subquery for an expression in the SELECT CLAUSE as long as the number of rows from the result sets match:
select (select count(*) from c_table where isread=false and relatedRecordId=1 ) as newCommentCount,
count(*) as allCommentCount
from c_table where relatedRecordId=1;

Select different column in a row depending on the value

I am trying to return a friend list for my users and I'm trying to gather the value matching their user_id.
| ID | user1_id | user1_status | user2_id | user2_status |
| 1 | 1 | 0 | 2 | 0 |
| 2 | 4 | 1 | 5 | 1 |
| 3 | 4 | 1 | 6 | 0 |
| 4 | 1 | 0 | 4 | 1 |
Here is the problem I have, the value I'm look for can be in either "user1_id"/"user2_id" and then I need to return the "user_status" ONLY for the other user. n. I made this table really simple. In my version there is a lot more columns I want my server to avoid returning.
Let's say that the client user_id is 4, so I need the select all the row with user1_id/user2_id equal to 4 and return the other person user_status. In the table, the first case of the value equal to 4 is in user1_id, I need that row to return the user2_id and user2_status.
Here is what I have so far, but it doesn't work:
SELECT `id`
CASE
WHEN `user1_id`='4' THEN `user2_id` AND `user2_status`
WHEN `user2_id`='4' THEN `user1_id` AND `user1_status`
ELSE NULL
END
from 'relationship'
where `user1_id`='4' OR `user2_id`='4'
How do I write this query statement?
If you refer to the CASE syntax you will see that it's defined to return a single column and not a tuple. Additionally, in your query you are trying to get either (user2_id, user_status) or NULL. Here you get a mismatch in the number of columns which is not allowed either.
If you really really want to use CASE you could do:
SELECT `id`
CASE
WHEN `user1_id`='4' THEN `user2_id`,
ELSE NULL
END
CASE
WHEN `user2_id`='4' THEN `user1_id`
ELSE NULL
END
CASE
WHEN `user1_id`='4' THEN `user1_status`
ELSE NULL
END
CASE
WHEN `user2_id`='4' THEN `user2_status`
ELSE NULL
END
FROM 'relationship'
where `user1_id`='4' OR `user2_id`='4'
yes, clunky and confusing. Much Simpler if you use UNION.
SELECT id, user2_id AS uid, user2_status as ustatus FROM relationship WHERE user1_id = 4
UNION
SELECT id, user1_id AS uid, user1_status as ustatus FROM relationship WHERE user2_id = 4

Select userids from mysql table if user has at least 3 fields filled

I have a mysql user table that holds user data like that:
userid | title | content
----------------------------------
1 | about | I am from ...
1 | location | Norway
1 | name | Mark
1 | website |
2 | about |
2 | location |
2 | name |
2 | website |
3 | ...
As you see the content is empty for userid 2, and also for many more users in the table.
My goal is to select only the userids that have at least 3 fields filled. All others should be ignored.
As my mysql knowledge is still weak I could not find a solution for this. I only found the opposite and just with count: Find the count of EMPTY or NULL columns in a MySQL table
What is the magic mysql query? Any help appreciated, thank you.
You would use aggregation and a having clause for this:
select u.userId
from users u
where content > '' and content is not null
group by u.userId
having count(*) >= 3;
I added the non-blank check as well as the null check. The null check is redundant, but it makes the intention clearer.

Filter by one value in unstructured table but sorting by another

This is perhaps a fairly straight-forward SQL query, but I've not done much SQL/database querying before and have inherited an issue that I'm struggling to understand and describe properly (thus the vague title....)
USER META
======================================
id | user_id | field | value
======================================
1 | 1 | color | red
2 | 1 | year | 1923
3 | 1 | ... | ...
4 | 3 | color | purple
5 | 3 | year | 2013
6 | 3 | ... | ...
7 | 7 | color | red
8 | 7 | year | 1982
9 | 7 | ... | ...
Given that I have a table structured like the above example, how would I query for a list of user_id's filtered by a specific 'color', but sorted by 'year'?
NOTE: I'm dealing with a legacy project, so I'm not in position to make schema changes.
You can do self-join:
SELECT DISTINCT t1.user_id
FROM TableName t1
JOIN TableName t2 ON t1.user_id = t2.user_id
WHERE t1.field = 'color' AND t1.value = 'red' AND t2.field = 'year'
ORDER BY t2.value
One way is with aggregation:
select user_id
from usermeta
group by user_id
having sum((case when field = 'color' then value end) = 'purple') > 0
order by max(case when field = 'year' then value+0 end);
The having clause is counting the number of rows that meet the particular condition, and making sure there is at least one for a given user_id.
The order by is returning the year. The +0 just converts it to a numeric value, so it sorts correctly. The year is being stored as a string. (For year, this may not be important because presumably all are four digits, but for other numerics it could be important.)
I think that one solution would be to create temp tables dynamically, one with columns user_id and color and another with columns user_id and year based upon this table and then do a join on them. Even if you find a syntactically composite single statement solution, it will internally have to do something similar.
It is possible to insert into a table based upon the output of a select. Sorry, I am not giving you the exact syntax here but the direction hopefully helps.

Find a class with exactly specific students

I'm no SQL expert, but I'm not a total amateur, yet this is a query on a single table with 2 fields that I don't know how to approach.
Suppose you have a table with class # & student #. How do you find the classes that have only exactly students x, y & z?
My real problem is more like a table of catalogs & item numbers, and how to find all the catalogs that have exactly (mo more or less) the specified items.
My only thought revolved around matching on GROUP_CONCAT, but there must b731e a more elegant way...
EDIT:
I misstated the problem, so I will provide table structure as well. The issue is more like products in boxes, where a box could contain more than one of a particular product, and you want to find boxes that have exactly the specified content. So the table, for example, is:
+------------+------------+------+-----+---------+----------------+
| Field | Type | Null | Key | Default | Extra |
+------------+------------+------+-----+---------+----------------+
| id | bigint(20) | NO | PRI | NULL | auto_increment |
| box_id | bigint(20) | YES | | NULL | |
| product_id | bigint(20) | YES | MUL | NULL | |
+------------+------------+------+-----+---------+----------------+
I want to find all boxes that contain exactly 2 items of product ID 22, one of 17, and one of 55. No more, no less.
You could use a having clause:
select *
from YourTable
group by
class
having count(distinct student) = 3
and max(case when student = 'X' then 1 end) = 1
and max(case when student = 'Y' then 1 end) = 1
and max(case when student = 'Z' then 1 end) = 1
I do have an answer that works, but it is far from efficient OR elegant, so I present it for anyone looking for a sub-optimal but correct solution to this problem, and an enticement to anyone else to provide a better one.
SELECT box_id, GROUP_CONCAT( product_id
ORDER BY product_id DESC
SEPARATOR ',' ) AS contents
FROM box_product
GROUP BY box_id
HAVING contents = '17,22,22,55';