Mysql combine columns if not null - mysql

I know that I can combine multiple columns in mysql by doing:
SELECT CONCAT(zipcode, ' - ', city, ', ', state) FROM Table;
However, if one of the fields is NULL then the entire value will be NULL. So to prevent this from happening I am doing:
SELECT CONCAT(zipcode, ' - ', COALESE(city,''), ', ', COALESCE(state,'')) FROM Table;
However, there still can be situation where the result will look something like this:
zipcode-, ,
Is there a way in MySQL to only have to comma and the hyphen if the next columns are not NULL?

There is actually a native function that will do this called Concat with Separator (concat_ws)!
Specifically, it seems that what you would need is:
SELECT CONCAT_WS(' - ',zipcode, NULLIF(CONCAT_WS(', ',city,state),'')) FROM TABLE;
This should account for all of the null cases you allude to.
However, it is important to note that a blank string ('') is different than a NULL. If you want to address this in the state/city logic you would add a second NULLIF check inside the second CONCAT_ws for the case where a city or a state would be blank strings. This will depend on the database's regard for the blank field and whether you are entering true NULLS into your database or checking the integrity of the blank data before you use it. Something like the following might be slightly more robust:
SELECT CONCAT_WS(' - ', zipcode, NULLIF(CONCAT_WS(', ', NULLIF(city, ''), NULLIF(state, '')), '')) FROM TABLE;
For more, check out the native documentation on concat_ws() here:
https://dev.mysql.com/doc/refman/8.0/en/string-functions.html#function_concat-ws

I think you need something like this
Set #zipcode := '12345';
Set #city := NULL;
Set #state := NULL;
SELECT CONCAT(#zipcode, ' - ', COALESCE(#city,''), ', ', COALESCE(#state,''));
Result: 12345 - ,
SELECT CONCAT(#zipcode, IF(#city is NULL,'', CONCAT(' - ', #city)), IF(#state is NULL,'', CONCAT(', ',#state)))
Result 12345

You need to make the output of the separators conditional on the following values being non-NULL. For example:
SELECT CONCAT(zipcode,
CASE WHEN city IS NOT NULL OR state IS NOT NULL THEN ' - '
ELSE ''
END,
COALESCE(city, ''),
CASE WHEN city IS NOT NULL AND state IS NOT NULL THEN ', '
ELSE ''
END,
COALESCE(state, '')
) AS address
FROM `Table``
Output (for my demo)
address
12345 - San Francisco, CA
67890 - Los Angeles
34567 - AL
87654
Demo on dbfiddle

Related

Issue with MySQL Concat and like command

I want to validate the data in MYSQL table. Table has 4 fields :
Firstname
Middlename
Lastname
Fullname
I want to compare if CONCAT(firstname, ' ', LASTNAME), matches Fullname
Here is the command I am using :
select * from user_info where CONCAT(firstname, ' ', lastname)
like CONCAT('%', fullname, '%')
However, this is not working. But the following command works :
select * from user_info where CONCAT(firstname, ' ', lastname)
like '%JOHN DOE%'
What could be the issue with the MySQL command ?
Your newly attached data confirms what I suspected, namely that the full name is not necessarily composes simply of the first and last name, but include the middle name, or might even be missing any of the three components. One option here would be to assert that each component present does appear somewhere inside the full name, in the correct order, e.g.
SELECT *
FROM yourTable
WHERE
fullname REGEXP
CONCAT(
CASE WHEN firstname IS NOT NULL
THEN CONCAT('[[:<:]]', COALESCE(firstname, ''), '[[:>:]]')
ELSE '' END,
'.*',
CASE WHEN middlename IS NOT NULL
THEN CONCAT('[[:<:]]', COALESCE(middlename, ''), '[[:>:]]')
ELSE '' END,
'.*',
CASE WHEN lastname IS NOT NULL
THEN CONCAT('[[:<:]]', COALESCE(lastname, ''), '[[:>:]]')
ELSE '' END);
Demo
You need to change order of condition in your code :-
select * from table1 where fullname
like CONCAT('%', trim(CONCAT(firstname,' ',middlename,' ',lastname)) , '%')
SQL Fiddle

Splitting a single column (name) into two (forename, surname) in SQL

Currently I'm working on a database redesign project. A large bulk of this project is pulling data from the old database and importing it into the new one.
One of the columns in a table from the old database is called 'name'. It contains a forename and a surname all in one field (ugh). The new table has two columns; forenames and surname. I need to come up with a clean, efficient way to split this single column into two.
For now I'd like to do everything in the same table and then I can easily transfer it across.
3 columns:
Name (the forename and surname)
Forename (currently empty, first half of name should go here)
Surname (currently empty, second half of name should go here)
What I need to do: Split name in half and place into forename and surname
If anyone could shed some light on how to do this kind of thing I would really appreciate it as I haven't done anything like this in SQL before.
Database engine: MySQL
Storage engine: InnoDB
A quick solution is to use SUBSTRING_INDEX to get everything at the left of the first space, and everything past the first space:
UPDATE tablename
SET
Forename = SUBSTRING_INDEX(Name, ' ', 1),
Surname = SUBSTRING_INDEX(Name, ' ', -1)
Please see fiddle here. It is not perfect, as a name could have multiple spaces, but it can be a good query to start with and it works for most names.
Try this:
insert into new_table (forename, lastName, ...)
select
substring_index(name, ' ', 1),
substring(name from instr(name, ' ') + 1),
...
from old_table
This assumes the first word is the forename, and the rest the is lastname, which correctly handles multi-word last names like "John De Lacey"
For the people who wants to handle fullname: John -> firstname: John, lastname: null
SELECT
if( INSTR(`name`, ' ')=0,
TRIM(SUBSTRING(`name`, INSTR(`name`, ' ')+1)),
TRIM(SUBSTRING(`name`, 1, INSTR(`name`, ' ')-1)) ) first_name,
if( INSTR(`name`, ' ')=0,
null,
TRIM(SUBSTRING(`name`, INSTR(`name`, ' ')+1)) ) last_name
It works fine with John Doe. However if user just fill in John with no last name, SUBSTRING(name, INSTR(name, ' ')+1)) as lastname will return John instead of null and firstname will be null with SUBSTRING(name, 1, INSTR(name, ' ')-1).
In my case I added if condition check to correctly determine lastname and trim to prevent multiple spaces between them.
This improves upon the answer given, consider entry like this "Jack Smith Smithson", if you need just first and last name, and you want first name to be "Jack Smith" and last name "Smithson", then you need query like this:
-- MySQL
SELECT
SUBSTR(full_name, 1, length(full_name) - length(SUBSTRING_INDEX(full_name, ' ', -1)) - 1) as first_name,
SUBSTRING_INDEX(full_name, ' ', -1) as last_name
FROM yourtable
Just wanted to share my solution. It also works with middle names. The middle name will be added to the first name.
SELECT
TRIM(SUBSTRING(name,1, LENGTH(name)- LENGTH(SUBSTRING_INDEX(name, ' ', -1)))) AS firstname,
SUBSTRING_INDEX(name, ' ', -1) AS lastname
I had a similar problem but with Names containing multiple names, eg. "FirstName MiddleNames LastName" and it should be "MiddleNames" and not "MiddleName".
So I used a combo of substring() and reverse() to solve my problem:
select
SystemUser.Email,
SystemUser.Name,
Substring(SystemUser.Name, 1, instr(SystemUser.Name, ' ')) as 'First Name',
reverse(Substring(reverse(SystemUser.Name), 1, instr(reverse(SystemUser.Name), ' '))) as 'Last Name',
I do not need the "MiddleNames" part and maybe this is not the most efficient way to solve it, but it works for me.
Got here from google, and came up with a slightly different solution that does handle names with more than two parts (up to 5 name parts, as would be created by space character). This sets the last_name column to everything to the right of the 'first name' (first space), it also sets full_name to the first name part. Perhaps backup your DB before running this :-) but here it is it worked for me:
UPDATE users SET
name_last =
CASE
WHEN LENGTH(SUBSTRING_INDEX(full_name, ' ', 1)) = LENGTH(full_name) THEN ''
WHEN LENGTH(SUBSTRING_INDEX(full_name, ' ', 2)) = LENGTH(full_name) THEN SUBSTRING_INDEX(del_name, ' ', -1)
WHEN LENGTH(SUBSTRING_INDEX(full_name, ' ', 3)) = LENGTH(full_name) THEN SUBSTRING_INDEX(del_name, ' ', -2)
WHEN LENGTH(SUBSTRING_INDEX(full_name, ' ', 4)) = LENGTH(full_name) THEN SUBSTRING_INDEX(del_name, ' ', -3)
WHEN LENGTH(SUBSTRING_INDEX(full_name, ' ', 5)) = LENGTH(full_name) THEN SUBSTRING_INDEX(del_name, ' ', -4)
WHEN LENGTH(SUBSTRING_INDEX(full_name, ' ', 6)) = LENGTH(full_name) THEN SUBSTRING_INDEX(del_name, ' ', -5)
ELSE ''
END,
full_name = SUBSTRING_INDEX(full_name, ' ', 1)
WHERE LENGTH(name_last) = 0 or LENGTH(name_last) is null or name_last = ''
SUBSTRING_INDEX didn't work for me in SQL 2018, so I used this:
declare #fullName varchar(50) = 'First Last1 Last2'
declare #first varchar(50)
declare #last varchar(50)
select #last = right(#fullName, len(#fullName)-charindex(' ',#fullName, 1)), #first = left(#fullName, (charindex(' ', #fullName, 1))-1);
Yields #first = 'First', #last = 'Last1 Last2'

Invalid length parameter passed to the LEFT or SUBSTRING function

I've seen a few of these questions asked but haven't spotted one that's helped!! I'm trying to select the first part of a postcode only, essentially ignoring anything after the space. the code I am using is
SUBSTRING(PostCode, 1 , CHARINDEX(' ', PostCode ) -1)
However, I am getting:
Invalid length parameter passed to the LEFT or SUBSTRING function
There's no nulls or blanks but there are some the only have the first part. Is this what causing the error and if so what's the work around?
That would only happen if PostCode is missing a space.
You could add conditionality such that all of PostCode is retrieved should a space not be found as follows
select SUBSTRING(PostCode, 1 ,
case when CHARINDEX(' ', PostCode ) = 0 then LEN(PostCode)
else CHARINDEX(' ', PostCode) -1 end)
CHARINDEX will return 0 if no spaces are in the string and then you look for a substring of -1 length.
You can tack a trailing space on to the end of the string to ensure there is always at least one space and avoid this problem.
SELECT SUBSTRING(PostCode, 1 , CHARINDEX(' ', PostCode + ' ' ) -1)
This is because the CHARINDEX-1 is returning a -ive value if the look-up for " " (space) is 0. The simplest solution would be to avoid '-ve' by adding
ABS(CHARINDEX(' ', PostCode ) -1))
which will return only +ive values for your length even if CHARINDEX(' ', PostCode ) -1) is a -ve value. Correct me if I'm wrong!
One of the selected column is null or empty.
Something else you can use is isnull:
isnull( SUBSTRING(PostCode, 1 , CHARINDEX(' ', PostCode ) -1), PostCode)

Getting empty result if there is 'null' value in CONCAT

I expected to get full name without middle initial if there is no middle name, but if column "M_NAME" is Null, the select statement returns empty. How can i solve this?
SELECT CONCAT(`Employee`.`F_NAME`, ' ', LEFT(`Employee`.`M_NAME`, 1), '. ', `Employee`.`L_NAME`) FROM `ccms`.`Employee` WHERE HR_ID = '223';
CONCAT returns NULL if any argument is null. You can solve this by making sure that no null argument is null by wrapping any nullable column in either IFNULL or COALESCE (the latter can take more than two arguments).
SELECT
CONCAT(
IFNULL(F_NAME, ''),
' ',
IFNULL(CONCAT(LEFT(M_NAME, 1), '. '), ''),
IFNULL(L_NAME, '')
)
FROM
ccms.Employee
WHERE
HR_ID = '223';
What this does is replace NULL column values with an empty string, which is probably your intent. Note that I updated the selection of M_NAME so that the period is only added if the value is not null by using this very behavior.
EDIT: You can use backticks and qualify column names if you want, but it's not necessary for this exact query.
Use COALESCE()
Returns the first non-NULL value in the list, or NULL if there are no non-NULL values.
SELECT CONCAT(`Employee`.`F_NAME`, ' ', COALESCE(LEFT(`Employee`.`M_NAME`, 1), ''), '. ', `Employee`.`L_NAME`) FROM `ccms`.`Employee` WHERE HR_ID = '223';
Use IFNULL().
Any place you have a possibly NULL field, wrap it with IFNULL(whatever, '') and then you'll get an empty string instead of a result-killing NULL.
another simple solution is use CONCAT_WS. It will work defenitely.
SELECT
CONCAT_WS(''
F_NAME,
'',
LEFT(M_NAME, 1),
L_NAME) FROM
ccms.Employee WHERE HR_ID = '223';

Conditional CONCAT with potentially NULL or empty values

In the below piece of code, I am creating an Address field by concatenating various parts of an address.
However, if for instance address2 was empty, the trailing , will still be concatenated into Address.
This means if all fields were empty, I end up with a result of ,,,,.
If address1 is "House Number" and everything else is empty, I end up with House Number,,,,.
CONCAT( COALESCE(address1,'') , ', ' ,
COALESCE(address2,'') , ', ' ,
COALESCE(address3,'') , ', ' ,
COALESCE(city,'') , ', ' ,
COALESCE(zip, '')
) AS Address,
Is there some way of conditionally placing the commas between address parts only if the content of an address part is not empty.
Such as something along the lines of (pseudo-code) IF(address1) is NULL use '' ELSE use ','
Thank you.
CONCAT_WS(', ',
IF(LENGTH(`address1`),`address1`,NULL),
IF(LENGTH(`address2`),`address2`,NULL),
IF(LENGTH(`address3`),`address3`,NULL),
IF(LENGTH(`city`),`city`,NULL),
IF(LENGTH(`zip`),`zip`,NULL)
)
Using CONCAT_WS as Mat says is a very good idea, but I thought I'd do it a different way, with messy IF() statements:
CONCAT( COALESCE(address1,''), IF(LENGTH(address1), ', ', ''),
COALESCE(address2,''), IF(LENGTH(address2), ', ', ''),
COALESCE(address3,''), IF(LENGTH(address3), ', ', ''),
COALESCE(city,''), IF(LENGTH(city), ', ', ''),
COALESCE(zip,''), IF(LENGTH(address1), ', ', ''),
) AS Address,
The IF()s check if the field has a length and if so returns a comma. Otherwise, it returns an empty string.
try with MAKE_SET
SELECT MAKE_SET(11111,`address1`,`address2`,`address3`,`city`,`zip`) AS Address
It will returns a string with all NOT NULL value separated by ,
CONCAT_WS(', ',
NULLIF(`address1`,''),
NULLIF(`address2`,''),
NULLIF(`address3`,''),
NULLIF(`city`,''),
NULLIF(`zip`,'')
)
CONCAT_WS combines non-NULL strings.
NULLIF writes NULL if left and right side are equals. In this case if values are equals an empty sting ''.