displaying rows with not null columns excluded - mysql

I have a MySQL table named products with default NULL value for size, weight, dimensions
I need to fetch it to get rows with non NULL values the desired result would be like for first row
:
SKU name price size
1 ,product1 ,2.00 , 2
and the second row
SKU name price weight
2 ,product2 ,3.00 , 3
I tried COALESCE but I need also to get the corresponding column name for the non NULL value
SKU name price size weight dimensions
1 product1 2.00 2 NULL NULL
2 product2 3.00 NULL 3 NULL
3 product3 4.00 NULL NULL 2x11x22

Use CONCAT() to combine the column name with the column value. If the value is NULL, the concatenation will also be NULL, and CONCAT_WS() will ignore that argument.
CONCAT_WS(', ',
CONCAT('size = ', size),
CONCAT('weight = ', weight),
CONCAT('dimensions = ', dimensions)
) AS attributes
CONCAT_WS() will ignore any null values in the arguments, so you won't get ,, in the result.

A solution using COALESCE.
COALESCE returns the first non-NULL value in the list, or NULL if there are no non-NULL values.
SELECT
SKU,
name,
price,
COALESCE(size,weight,dimensions) as extraInfo
FROM mytable;
see: DBFIDDLE
output:
SKU
name
price
extraInfo
1
product1
2.00
2
2
product2
3.00
3
3
product3
4.00
2x11x22

Related

MySQL - How to return distinct IDs where all rows for the same ID have null field value

I have a query with two joins that returns this data:
ID Score
1 NULL
1 5
1 6
2 NULL
2 NULL
3 5
3 8
3 3
3 NULL
3 NULL
3 7
4 NULL
4 NULL
4 3
4 9
I would like to return the unique IDs which have a NULL value in the Score column for each of the rows with the same ID. In this case, the query should only return one row with the ID of 2 since that is the only ID which has all NULL values in the Score column.
Thank you!
You could aggregate your original query by making it as a sub select and aplly count() on score column which has null values, So if all values are NULL for a particular ID then count will return 0, thus using having clause you can filter your results using result of count(Score)
select ID,
count(Score) count_null
from (your query)t
group by ID
having count_null = 0
Demo Based on your sample data set

MySQL count identical values

I have this table named prizes with the following structure
`id` (PRIMARY)
`id_multiple`
`desc`
`winner`
I want to select those who don't have a winner (NULL) and display them together if they have the same id_multiple showing the count of how many left to win of that id_multiple.
So for example, there's this values:
id_multiple | winner | desc
1 | NULL | voucher
1 | jonh | voucher
2 | NULL | car
2 | NULL | car
And I want to display:
Left to win:
1 Voucher
2 Car
(The desc will be the same for all id_multiple so it might be ambiguous to use id_multiple?)
Something like:
SELECT id_multiple,count(id_multiple),`desc`
FROM `yourtable`
WHERE `winner` IS NULL
GROUP BY `id_multiple`
You could count a case expression:
SELECT id_multiple, COUNT(CASE WHEN winner IS NULL THEN 1 END) AS left_to_win, `desc`
FROM mytable
GROUP BY id_multiple, `desc`
Or, even simpler, with a sum expression that takes advantage of the fact that true is interpreted as 1 and false as 0 in numerical contexts:
SELECT id_multiple, SUM(winner IS NULL) AS left_to_win, `desc`
FROM mytable
GROUP BY id_multiple, `desc`

MySQL return min value but not null

I have a table where there are columns students and grade obtained(A-F). A student can appear for test more than once. Sometimes students register but do not appear for test so the grade is not entered but student record entry is made.
I want to get best grade of each student. When I do min(grade) if there is any record with null, null gets selected instead of 'A-F' which indicate proper results. I want to get min of grade if grade exists or null if there are no grades.
SELECT `name`,min(grade) FROM `scores` group by `name`
Id | Name | Grade
1 | 1 | B
2 | 1 |
3 | 1 | A
4 | 2 | C
5 | 2 | D
For name 1 it is fetching second record not the third one having 'A'.
As per the conversations in the comments, the easiest solution may be to convert your empty strings to null, and let the builtin min function do the heavy lifting:
ALTER TABLE scores MODIFY grade VARCHAR(1) NULL;
UPDATE scores
SET grade = null
WHERE grade = '';
SELECT name, MIN(grade)
FROM scores
GROUP BY name
If this is not possible, a dirty trick you could use is to have a case expression convert the empty string to a something you know will come after F:
SELECT name,
MIN(CASE grade WHEN '' THEN 'DID NOT PARTICIPATE' ELSE grade END)
FROM scores
GROUP BY name
And if you really need the empty string back, you can have another case expression around the min:
SELECT name, CASE best_grade WHEN 'HHH' THEN '' ELSE best_grade END
FROM (SELECT name,
MIN(CASE grade WHEN '' THEN 'HHH' ELSE grade END) AS
best_grade
FROM scores
GROUP BY name) t
Change your query slightly to -
SELECT `name`,min(grade) FROM `scores` WHERE grade <> "" group by `name`
If the name has a grade/s assigned to it then the lowest will be returned else the resultset will be null

Sum columns depending on another column value

I'm having trouble with summing the fields values based on another fields value.
I need to SUM(activities.points) based on activities.activity_type if it's used_points or added_points and put it in AS used_points/added_points.
Table activities:
id | subscription_id | activity_type | points
--------------------------------------------------
1 | 1 | used_points | 10
2 | 1 | used_points | 50
3 | 1 | added_points | 20
4 | 1 | added_points | 30
5 | 2 | used_points | 20
6 | 2 | used_points | 45
7 | 2 | added_points | 45
8 | 2 | added_points | 45
Table subscriptions:
id | name | current_points
-------------------------------------
1 | card_1 | 700
2 | card_2 | 900
What I need:
name | current_points | used_points | added_points
-----------------------------------------------------------
card_1 | 700 | 60 | 50
card_2 | 900 | 65 | 90
What I tried :
SELECT
subscriptions.name,
subscriptions.current_points,
IF(activities.activity_type="used_points", SUM(activities.points), null)
AS used_points,
IF(activities.activity_type="added_points", SUM(activities.points), null)
AS added_points
FROM activities
JOIN subscriptions
ON activities.subscription.id = subscription.id
GROUP BY subscriptions.name
Which is wrong.
Thanks
You want to use SUM(IF( )). You want to add up the values returned from the IF. You want that IF expression to be evaluated for each individual row. Then, use the SUM aggregate to add up the value returned for each row.
Remove the SUM aggregate from inside the IF expression and instead, wrap the IF inside a SUM.
Followup
Q But why SUM() inside of IF doesn't work ?
A Well, it does work. It's just not working the way you want it to work.
The MySQL SUM function is an "aggregate" function. It aggregates rows together, and returns a single value.
For an expression of this form: IF(col='foo',SUM(numcol),0)
What MySQL is doing is aggregating all the rows into the SUM, and returning a single value.
Other databases would pitch a fit, and throw an error with the reference to the non-aggregate col in that expression. MySQL is more lenient, and treats the col reference like it was an aggregate (like MIN(col), or MAX(col)... working on a group of rows, and returning a single value. In this case, MySQL is selecting a single, sample row. (It's not determinate which row will be "chosen" as the sample row.) So that reference to col is sort of like a GET_VALUE_FROM_SAMPLE_ROW(col). Once the aggregates are completed, then that IF expression gets evaluated once.
If you start with this query, this is the set of rows you want to operate on.
SELECT s.name
, s.current_points
, a.activity_type
, a.points
, IF(a.activity_type='used_points',a.points,NULL) AS used_points
, IF(a.activity_type='added_points',a.points,NULL) AS added_points
FROM subscriptions s
JOIN activities a
ON a.subscription_id = s.id
When you add a GROUP BY clause, that's going to aggregate some of those rows together. What you will get back for the non-aggregates is values from a sample row.
Try adding GROUP BY s.name to the query, and see what is returned.
Also try adding in some aggregates, such as SUM(a.points)
SELECT s.name
, s.current_points
, a.activity_type
, a.points
, IF(a.activity_type='used_points',a.points,NULL) AS used_points
, IF(a.activity_type='added_points',a.points,NULL) AS added_points
, SUM(a.points) AS total_points
FROM subscriptions s
JOIN activities a
ON a.subscription_id = s.id
GROUP BY s.name
Finally, we can add in the expressions in your query into the SELECT list:
, IF(a.activty_type='used_points',SUM(a.points),NULL) AS if_used_sum
, IF(a.activty_type='added_points',SUM(a.points),NULL) AS if_added_sum
What we discover is that the value returned from these expressions will either be SUM(a.points), which will match the total_points, or it will be NULL. And we can see the value of the activity_type column, retrieved from a single, sample row for each group, and we can see that this is expression is "working", it's just not doing what we you really want to happen: for the conditional test to run on each individual row, returning a value for points or a null, and then summing that up for the group.
Your code is only slightly out:
SELECT
subscriptions.name,
subscriptions.current_points,
SUM(IF(activities.activity_type="used_points", 0, activities.points))
AS used_points,
SUM(IF(activities.activity_type="added_points", 0, activities.points))
AS added_points
FROM activities
JOIN subscriptions
ON activities.subscription_id = subscription.id
GROUP BY subscriptions.name, subscriptions.current_points
Note the fixed typo in the second last line - you wrote subscription.id instead of subscription_id. Also you only grouped by name instead of name and current_points, not sure if that's allowed in mysql (I use T-SQL), it's good practice to have it there anyway.
Well, I did it not using the IF statement. Here's the example (http://sqlfiddle.com/#!2/076c3f/12):
SELECT
subs.name,
subs.current_points,
(SELECT SUM(points) FROM activities WHERE type = 1 AND subs_id = subs.id) AS used_points,
(SELECT SUM(points) FROM activities WHERE type = 2 AND subs_id = subs.id) AS added_points
FROM activities
JOIN subs ON activities.id = subs.id
GROUP BY subs.name
NOTE: I changed the type from VARCHAR to INT to simplify.
Try change
IF(activities.activity_type="used_points", null, SUM(activities.points))
AS used_points,
IF(activities.activity_type="added_points", null, SUM(activities.points))
AS added_points
To next
SUM(IF(activities.activity_type="used_points", activities.points, 0))
AS used_points,
SUM(IF(activities.activity_type="added_points", activities.points, 0))
AS added_points
In this way you check column and sum points or 0
To sum a column of integer values(c1) based on another column of character values(c2). And if you need to sum only not null values, the below code will help.
SELECT SUM(c1) FROM table_name WHERE c2 <> '' AND c2 IS NOT NULL

Order MySql query using

I, I would like to make a query and sorting my members in a special way... Could someone help ?
Here's the problem.
I would like to select members in my table using a special sort order.
The profile fields values are stored in a table wp_bp_xprofile_data like this :
id | field_id | user_id | value
--------+----------+---------+----------
For example, I have 3 fields
NICKNAME (field_id = 1)
FIRSTNAME (field_id = 2)
LASTNAME (field_id = 3)
The table rows will look like this :
id | field_id | user_id | value
--------+----------+---------+----------
2544 1 100 fib
2545 2 100 john
2546 3 100 arenzich
2547 1 200 dog
2548 2 200 rick
2549 3 200 zarenburg
2550 1 300 fox
2551 2 300 frank
2552 3 300 arenzich
I've got this query to sort them using one field, for example to sort them by nickname alphabetically :
SELECT *
FROM wp_bp_xprofile_data u WHERE u.field_id = 1 ORDER BY u.value ASC
So they will be sorted like this : dog(200),fib(100), then fox(300).
Now, I would like to sort them not one but several fields (firstname and lastname; to differenciate people with same lastname) so that the query returns the users in this order :
frank arenzich (300), john arenzich (100), frank arenzich (200).
Any idea for doing this ?
Thanks A LOT !!!
This will probably need to be done by first pivoting this into a proper table by column, and then ordering that on multiple columns.
Note: this will not produce output in the same format as your original table, but is arguably a lot more flexible and useful as it combines all information about each user_id into a single row.
/* A column-wise pivot of NICKNAME, FIRSTNAME, LASTNAME */
SELECT
user_id,
MAX(CASE WHEN field_id = 1 THEN value ELSE null END) AS NICKNAME,
MAX(CASE WHEN field_id = 2 THEN value ELSE null END) AS FIRSTNAME,
MAX(CASE WHEN field_id = 3 THEN value ELSE null END) AS LASTNAME
FROM wp_bp_xprofile_data
GROUP BY user_id
/* Include the HAVING if you only want those who have both first & last names specified */
HAVING
FIRSTNAME IS NOT NULL
AND LASTNAME IS NOT NULL
/* Pivoted columns can then be treated in the ORDER BY */
ORDER BY
FIRSTNAME,
LASTNAME
Here is a demonstration...
It looks like this is a Wordpress table, so you may not be in a position to change its structure. But if you do have the option of modifying it, I would recommend changing the structure to resemble the pivot's output to begin with.