MySQL: Updating multiple columns using if else - mysql

I need help to set a mySQL update query for my little project like below:
UPDATE TABLE
IF SUBSTRING_INDEX(REFERENCE, '_', -1)= 'A' THEN
{
TYPE = 1
COLOR = 'BLUE'
}
ELSEIF SUBSTRING_INDEX(REFERENCE, '_', -1)= 'B' THEN
{
TYPE = 2
COLOR = 'RED'
},
ID = SUBSTRING_INDEX(REFERENCE, '_', 1)
WHERE REFERENCE IS NOT NULL
In my table, there is a column REFERENCE which contains varchar like
A_1, A_2, A_10
B_1, B_4, B_12
etc...
where the character before the symbol '_' determines certain values for other columns like stated above.
I am still fairly new to mySQL, so appreciates any help!

You can use case expressions:
UPDATE TABLE
SET TYPE = (CASE WHEN SUBSTRING_INDEX(REFERENCE, '_', -1) = 'A'
THEN 1
WHEN SUBSTRING_INDEX(REFERENCE, '_', -1) = 'B'
THEN 2
ELSE TYPE
END),
COLOR = (CASE WHEN SUBSTRING_INDEX(REFERENCE, '_', -1) = 'A'
THEN 'BLUE'
WHEN SUBSTRING_INDEX(REFERENCE, '_', -1) = 'B'
THEN 'RED'
ELSE COLOR
END),
ID = SUBSTRING_INDEX(REFERENCE, '_', 1)
WHERE REFERENCE IS NOT NULL;

Related

How to parse a string and get the value after "=" character

So I have string that should contains "Object.Name" once in a row , if I see it ,I have to get the value after "=" character. If doesn't match it anywhere in the string i should move hardcoded value.
Here is example of the string:
Object.Name=ASDD||Product.Name=DSA
Product.Name=QWE||Object.Name=WSXS
Storage.Name=12345||Object.Name=WERR||Product.Name=QAZ
I know that I should use case for that but doesn't know how to proceed the string
case
when (match the string ) then (value after the "=")
else (hardcoded value)
end
In Oracle, you can use:
SELECT value,
CASE
WHEN start_pos = 0
THEN NULL
ELSE SUBSTR(
'||' || value || '||',
start_pos + LENGTH('||Object.Name='),
end_pos - start_pos - LENGTH('||Object.Name=')
)
END AS object_name
FROM (
SELECT value,
INSTR(
'||' || value || '||',
'||Object.Name='
) AS start_pos,
INSTR(
'||' || value || '||',
'||',
INSTR('||' || value || '||', '||Object.Name=')+LENGTH('||Object.Name=')
) AS end_pos
FROM table_name
)
Which, for the sample data:
CREATE TABLE table_name (value) AS
SELECT 'Object.Name=ASDD||Product.Name=DSA' FROM DUAL UNION ALL
SELECT 'Product.Name=QWE||Object.Name=WSXS' FROM DUAL UNION ALL
SELECT 'Storage.Name=12345||Object.Name=WERR||Product.Name=QAZ' FROM DUAL;
Outputs:
VALUE
OBJECT_NAME
Object.Name=ASDD||Product.Name=DSA
ASDD
Product.Name=QWE||Object.Name=WSXS
WSXS
Storage.Name=12345||Object.Name=WERR||Product.Name=QAZ
WERR
db<>fiddle here
Since you changed the tags, in MySQL:
SELECT value,
CASE
WHEN start_pos = 0
THEN NULL
ELSE SUBSTRING(
CONCAT('||', value, '||'),
start_pos + LENGTH('||Object.Name='),
end_pos - start_pos - LENGTH('||Object.Name=')
)
END AS object_name
FROM (
SELECT value,
LOCATE(
'||Object.Name=',
CONCAT('||', value, '||')
) AS start_pos,
LOCATE(
'||',
CONCAT('||', value, '||'),
LOCATE('||Object.Name=', CONCAT('||', value, '||'))
+ LENGTH('||Object.Name=')
) AS end_pos
FROM table_name
) t
db<>fiddle here

Pivot Table Omitting Rows that Have Null values

I am solving a problem very similar to this only in my case, I am not summing any values.
I have been able to write a select that works using solution from this page
SELECT
id,
GROUP_CONCAT(if(colID = 1, value, NULL)) AS 'First Name',
GROUP_CONCAT(if(colID = 2, value, NULL)) AS 'Last Name',
GROUP_CONCAT(if(colID = 3, value, NULL)) AS 'Job Title'
FROM tbl
GROUP BY id;
However, I want to omit rows that have the value to be null
I assume you want to drop the result row if any of the source rows has value IS NULL.
You should be able to achieve that with bit_and() in the HAVING clause:
SELECT id
, max(CASE WHEN colID = 1 THEN value END) AS fn
, max(CASE WHEN colID = 2 THEN value END) AS ln
, max(CASE WHEN colID = 3 THEN value END) AS jt
FROM tbl
GROUP BY id
HAVING bit_and(value IS NOT NULL);
Alternative:
...
HAVING count(*) = count(value);
I didn't spell out ELSE NULL in the CASE statements because (per documentation):
If there was no matching result value, the result after ELSE is returned, or NULL if there is no ELSE part.
Just add this constraint to the where statement of your query, like this:
SELECT
id,
GROUP_CONCAT(if(colID = 1, value, NULL)) AS 'First Name',
GROUP_CONCAT(if(colID = 2, value, NULL)) AS 'Last Name',
GROUP_CONCAT(if(colID = 3, value, NULL)) AS 'Job Title'
FROM tbl
WHERE value IS NOT NULL
GROUP BY id;
EDIT
After some tests I could make a solution to work, but it seems interesting why value is not null won't work.
Solution link: http://sqlfiddle.com/#!2/b7a445/3
SELECT
id,
max(case when colID = 1 then value else '' end) AS fn,
max(case when colID = 2 then value else '' end) AS ln,
max(case when colID = 3 then value else '' end) AS jt
FROM tbl
where not exists (select * from tbl b where tbl.id=b.id and value is null)
group by id

MySql, Postgres, Oracle and SQLServer ignoring IS NOT NULL filter

While I was preparing an answer to one of our fellows here on SO I've encounter an odd situation, at least to me. The original question is here: Pivot Table Omitting Rows that Have Null values
I've modified the query to use max instead of group_concat in order to show the "problem" in all databases.
SELECT
id,
max(case when colID = 1 then value else '' end) AS fn,
max(case when colID = 2 then value else '' end) AS ln,
max(case when colID = 3 then value else '' end) AS jt
FROM tbl
GROUP BY id
The result of this query is this:
ID FN LN JT
1 Sampo Kallinen Office Manager
2 Jakko Salovaara Vice President
3 (null) Foo No First Name
The user asks to filter the row with id 3 because the field value is null.
When it seems pretty obvious that only it needs to do was to add a WHERE value IS NOT NULL constraint on that query to achieve what the user expect. It won't work.
So I start to test it on the other databases to see what happens (Queries with the WHERE CLAUSE)
SELECT
id,
max(case when colID = 1 then value else '' end) AS fn,
max(case when colID = 2 then value else '' end) AS ln,
max(case when colID = 3 then value else '' end) AS jt
FROM tbl
WHERE value is not null
GROUP BY id
Mysql: http://sqlfiddle.com/#!2/78395/1
Postgres: http://sqlfiddle.com/#!15/78395/1
SQLServer: http://sqlfiddle.com/#!6/78395/1
Oracle: http://sqlfiddle.com/#!4/78395/1
For my surprise the result was the same, none worked.
Then I tried a different version of the same query:
SELECT * FROM (
SELECT
id,
max(case when colID = 1 then value else '' end) AS fn,
max(case when colID = 2 then value else '' end) AS ln,
max(case when colID = 3 then value else '' end) AS jt
FROM tbl
GROUP BY id
) T
WHERE fn IS NOT NULL
AND ln IS NOT NULL
AND jt IS NOT NULL
Oracle: http://sqlfiddle.com/#!4/78395/2 WORKED
MySql: http://sqlfiddle.com/#!2/78395/2
Postgres: http://sqlfiddle.com/#!15/78395/2
SQLServer: http://sqlfiddle.com/#!6/78395/2
The only way I could make it work on all databases was with this query:
SELECT
id,
max(case when colID = 1 then value else '' end) AS fn,
max(case when colID = 2 then value else '' end) AS ln,
max(case when colID = 3 then value else '' end) AS jt
FROM tbl
WHERE NOT EXISTS (SELECT * FROM tbl b WHERE tbl.id=b.id AND value IS NULL)
GROUP BY id
So I ask:
What is happening here that except for that specific case on Oracle all other DBs seem to ignore the IS NOT NULL filter?
To omit the row from the result if any of the source rows for the same id has value IS NULL, a solution in Postgres would be to use the aggregate function every() or (synonym for historical reasons) bool_and() in the HAVING clause:
SELECT id
, max(case when colID = 1 then value else '' end) AS fn
, max(case when colID = 2 then value else '' end) AS ln
, max(case when colID = 3 then value else '' end) AS jt
FROM tbl
GROUP BY id
HAVING every(value IS NOT NULL);
SQL Fiddle.
Explain
Your attempt with a WHERE clause would just eliminate one source row for id = 3 in your example (the one with colID = 1), leaving two more for the same id. So we still get a row for id = 3 in the result after aggregating.
But since we have no row with colID = 1, we get an empty string (note: not a NULL value!) for fn in the result for id = 3.
A faster solution in Postgres would be to use crosstab(). Details:
PostgreSQL Crosstab Query
Other RDBMS
While EVERY is defined in the SQL:2008 standard, many RDBMS do not support it, presumably because some of them have shady implementations of the boolean type. (Not dropping any names like "MySQL" or "Oracle" ...). You can probably substitute everywhere (including Postgres) with:
SELECT id
, max(case when colID = 1 then value else '' end) AS fn
, max(case when colID = 2 then value else '' end) AS ln
, max(case when colID = 3 then value else '' end) AS jt
FROM tbl
GROUP BY id
HAVING count(*) = count(value);
Because count() doesn't count NULL values. In MySQL there is also bit_and().
More under this related question:
Is there any equivalent to Postgresql EVERY aggregate function on other RDBMS?
It works in Oracle because Oracle handles NULL incorrectly in that NULL and '' are the same. The other databases don't do this because it is wrong. NULL is unknown, versus '' which is just a blank, empty string.
So if your where clause said something like WHERE (fn IS NOT NULL or fn <> '') you would probably get further.
I think this is a case where a HAVING clause will do what you need.
SELECT id, max ... (same stuff as before)
FROM tbl
GROUP by id
HAVING fn IS NOT NULL
AND ln IS NOT NULL
AND jt IS NOT NULL

Multiple count on same column and insert into another table

I have a student table that has three columns
1. Student Name
2. Class Name
3. Test result
A student takes more than one tests with different results. I am trying to get the data into another table that has
1. Stundent Name+CLass Name ( Concatenated )
2. Pass (No of tests Passed)
3. Fail (No of tests failed)
4. Absent (No of tests Absent)
I use
select count(*)
from Student
where Result in ('Passed')
group by StuName, ClassName;
to get the count of passed subject for each stu+class combination. Similarly for failed and absent tests.
How should I modify the code to make an insert into the Table two??
MySQL supports inline IF statement,
SELECT CONCAT(StudentName, ' ', ClassName) Student_Class,
SUM(test = 'PASSED') totalPass,
SUM(test = 'Failed') totalFailed,
SUM(test = 'Absent') totalAbsent
FROM student
GROUP BY CONCAT(StudentName, ' ', ClassName)
and when you want to insert the result from the query above, use INSERT INTO..SELECT statement,
INSERT INTO tableNAME(col1, totalPass, totalFailed, totalAbsent)
SELECT CONCAT(StudentName, ' ', ClassName) Student_Class,
SUM(test = 'PASSED') totalPass,
SUM(test = 'Failed') totalFailed,
SUM(test = 'Absent') totalAbsent
FROM student
GROUP BY CONCAT(StudentName, ' ', ClassName)
You can easily pivot the data using an aggregate function with a CASE:
select concat(StuName, ',', ClassName) StuNameClass,
sum(case when result = 'Passed' then 1 else 0 end) Passed,
sum(case when result = 'Fail' then 1 else 0 end) Fail,
sum(case when result = 'Absent' then 1 else 0 end) Absent
from Student
group by concat(StuName, ',', ClassName);
Then if you want to insert the data into your other table:
insert into Table2 (StudentClassName, Passed, Fail, Absent)
select concat(StuName, ',', ClassName) StuNameClass,
sum(case when result = 'Passed' then 1 else 0 end) Passed,
sum(case when result = 'Fail' then 1 else 0 end) Fail,
sum(case when result = 'Absent' then 1 else 0 end) Absent
from Student
group by concat(StuName, ',', ClassName);
INSERT INTO t (name_class, passed_count, failed_count, absent_count)
SELECT CONCAT(StuName, ' ', ClassName) AS name_class,
SUM(IF(Result='Passed', 1, 0)) AS passed_count,
SUM(IF(Result='Failed', 1, 0)) AS failed_count,
SUM(IF(Result='Absent', 1, 0)) AS absent_count
FROM Student
GROUP BY StuName, ClassName;

MySQL: List and like?

I want to compare the value of a column with several list and print something else in each case. Its a list of car brands, like Chevy, Ford and so on. My logic should be like this:
if(mycolumn like carlist1, 'this', '')
if(mycolumn like carlist2, 'that', '')
if(mycolumn like carlist3, 'foobar', '')
mycolumn has a value like Chevy-1234, so it must be a like since the numbers will change. How could I do that in MySQL?
Thanks!
Your best bet is to store the car list as a table, with data like:
Chevy
Ford
and so on.
Then you can do what you want with a join:
select t.*,
max(case when cl.brand = 'Chevy' then 'this' end) as Chevy,
max(case when cl.brand = 'Ford' then 'this' end) as Ford,
. . .
from t left outer join
carlist cl
on left(t.col, length(cl.brand)) = cl.brand
group by t.id
Otherwise, you have to deal with the number:
select (case when left(col, locate('-', col) - 1) = carlist1 then 'this' end),
(case when left(col, locate('-', col) - 1) = carlist2 then 'that' end),
(case when left(col, locate('-', col) - 1) = carlist3 then 'foobar' end)
Try it in mysql like this
SELECT
if(mycolumn like 'carlist1%', 'this', '') as 'Blah1',
if(mycolumn like 'carlist1%', 'that', '') as 'Blah2',
if(mycolumn like 'carlist1%', 'foobar', '') as 'Blah3'
FROM mytable
you may use
SELECT
WHEN mycolumn LIKE 'carlist1%' THEN 'this' END AS Blah1,
WHEN mycolumn LIKE 'carlist1%' THEN 'that' END AS Blah2,
WHEN mycolumn LIKE 'carlist1%' THEN 'foobar' END AS Blah3
FROM mytable