Trouble using COALESCE function in sql - sql

I am attempting to do the following:
SELECT forename,
forename2,
surname,
(SELECT (COALESCE(LEFT(forename, 2)),"") +
(COALESCE(LEFT(forename2, 1)),"") +
(COALESCE(LEFT(surname, 1)), "") as Mnemonic)
from Persons
trying to get, first 2 letters of forename, first letter of forename2(if NOT null), and first letter of surname if not null, with "" empty strings where a null is present.
any help would be much appreciated,
Regards, Bserk

Your question doesn't say what's wrong with your current code, but I'm going to guess you are getting an error because you're using double-quotes instead of single quotes, and your brackets don't match.
You can also simplify this query somewhat by removing the inner select.
Try this:
SELECT forename, forename2, surname,
COALESCE(LEFT(forename, 2),'') +
COALESCE(LEFT(forename2, 1),'') +
COALESCE(LEFT(surname, 1),'') as Mnemonic
from Persons

Use single quotes instead of double for string literals and count your braces:
SELECT
forename,
forename2,
surname,
COALESCE(LEFT(forename, 2),'') + COALESCE(LEFT(forename2, 1),'') + COALESCE(LEFT(surname, 1), '') as Mnemonic
from Persons

Looks like "trouble" you've mentioned is because using of wrong parenthesis and double quotes.
Following query returns 1123
SELECT COALESCE(LEFT(null, 2), '')
+ COALESCE(LEFT('1111', 2), '')
+ COALESCE(LEFT('2222', 1),'')
+ COALESCE(LEFT('3333', 1), '')

The statement will not even compile as it is.
You don't need the second SELECT statement as you are not doing a subquery.
You need to use single braces. Double braces are used for object names (ie tables, procedures etc).
Too many parentheses
Try:
SELECT forename, forename2, surname,
COALESCE(LEFT(forename, 2),'') +
COALESCE(LEFT(forename2, 1),'') +
COALESCE(LEFT(surname, 1), '') as Mnemonic
from Persons

Related

Pulling a section of a string between two characters in SQL, and the section of the string around the extracted section

I have a table that includes names and allows for a "nickname" for each name in parenthesis.
PersonName
John (Johnny) Hendricks
Zekeraya (Zeke) Smith
Ajamain Sterling (Aljo)
Beth ()) Jackson
I need to extract the Nickname, and return a column of nicknames and a column of full names (Full string without the nickname portion in parenthesis). I also need a condition for the nickname to be null if no nickname exists, and so that the nickname only returns letters. So far I have been able to figure out how to get the nickname out using Substring, but I can't figure out how to create a separate column for just the name.
Select SUBSTRING(PersonName, CHARINDEX('(', PersonName) +1,(((LEN(PersonName))-CHARINDEX(')',REVERSE(PersonName)))-CHARINDEX('(',PersonName)))
as NickName
from dbo.Person
Any help would be appreciated. I'm using MS SQL Server 2019. I'm pretty new at this, as you can tell.
Using your existing substring, one simple way is to use apply.
Assuming your last row is an example of a nickname that should be NULL, you can use an inline if to check its length - presumably a nickname must be longer than 1 character? Adjust this logic as required.
select PersonName, Iif(Len(nn)<2,null,nn) NickName, Trim(Replace(Replace(personName, Concat('(',nn,')') ,''),' ','')) FullName
from Person
cross apply (values(SUBSTRING(PersonName, CHARINDEX('(', PersonName) +1,(((LEN(PersonName))-CHARINDEX(')',REVERSE(PersonName)))-CHARINDEX('(',PersonName))) ))c(nn)
The following code will deal correctly with missing parenthesis or empty strings.
Note how the first CROSS APPLY feeds into the next
SELECT
PersonName,
NULLIF(NickName, ''),
FullName = ISNULL(REPLACE(personName, ' (' + NickName + ')', ''), PersonName)
FROM t
CROSS APPLY (VALUES(
NULLIF(CHARINDEX('(', PersonName), 0))
) v1(opening)
CROSS APPLY (VALUES(
SUBSTRING(
PersonName,
v1.opening + 1,
NULLIF(CHARINDEX(')', PersonName, v1.opening), 0) - v1.opening - 1
)
)) v2(NickName);
db<>fiddle

SQL Concat and merge values

How to make it correct, whats wrong with this?
SELECT Name, Surname, CONCAT(NAME + ' '+ SURNAME) 'name and surname'
FROM Worker
WHERE Born = '1988';
or
SELECT Name, Surname, 'NAME' + ' '+ 'SURNAME' AS 'name and surname'
FROM Worker
WHERE Born = '1988';
The standard SQL operator for concatenation is ||, so you want:
SELECT Name, Surname, (NAME || ' ' || SURNAME) as full_name
FROM Worker
WHERE Born = '1988';
Many databases also support a CONCAT() function, although logically you want at least two arguments. SQL Server uses + for string concatenation, but that is not widespread.
Your syntax is wrong in the two queries:
SELECT Name, Surname, CONCAT(NAME, ' ', SURNAME) "name and surname"
FROM Worker
WHERE Born = '1988';
or
SELECT Name, Surname, NAME + ' '+ SURNAME AS "name and surname"
FROM Worker
WHERE Born = '1988';
Suposing you are using Microsoft SQL Server, keep in mind there are actually few differences between regular + operator and concat() function.
concat() will implicitly converts any parameter to a string while + will throw an error if you mix strings and other types
concat() will always return a not null string even if all parameters are null. + will return null as soon as any column is null.

Using string methods in a SELECT query to select up to the second space?

In an MS-Access database I'm working with, one of the tables has a field called "Name". The format of this field will generally be along the lines of "firstname surname integer", but sometimes may just be "firstname surname".
I need to select just the first name and the surname from the name field.
I've looked at using the Left function
SELECT DISTINCT LEFT([Name], x)
However since names are different lengths, this isn't going to work since there is no constant integer to use as the second parameter. Nor can it be used with
SELECT DISTINCT LEFT(InStr([Name], " "), x)
for the above reason, but also because because that would split the field at the first space.
Is there a way using LEFT, TRIM, SPLIT or any other string manipulation that I can create a query to select just the first two parts of the name? I need the space included.
You can try this.
SELECT DISTINCT IIf( ( InStr( InStr([Name],' ') + 1 , [Name], ' ') > 0 ), Left( [Name], InStr(InStr([Name],' ') + 1 , [Name], ' ') ), [Name])
FROM MyTable;

Running SQL query to remove all trailing and beginning double quotes only affects first record in result set

I'm having a problem when running the below query - it seems to ONLY affect the very first record. The query removes all trailing and beginning double quotes. The first query is the one that does this; the second query is just to demonstrate that there are multiple records that have beginning double quotes that I need removed.
QUESTION: As you can see the first record resulting from the top query is fine - it has its double quotes removed from the beginning. But all subsequent queries appear to be untouched. Why?
If quotes are always assumed to exist at both the beginning and the end, adjust your CASE statement to look for instances where both cases exist:
CASE
WHEN ([Message] LIKE '"%' AND [Message] LIKE '%"') THEN LEFT(RIGHT([Message], LEN([Message])-1),LEN([Message]-2)
ELSE [Message]
EDIT
If assumption is not valid, combine above syntax with your existing CASE logic:
CASE
WHEN ([Message] LIKE '"%' AND [Message] LIKE '%"') THEN LEFT(RIGHT([Message],LEN([Message])-1),LEN([Message]-2)
WHEN ([Message] LIKE '"%') THEN RIGHT([Message],LEN([Message]-1)
WHEN ([Message] LIKE '%"') THEN LEFT([Message],LEN([Message]-1)
ELSE [Message]
Because your CASE statement is only evaluating the first condition met, it will only ever remove one of the statements.
Try something like the following:
SELECT REPLACE(SUBSTRING(Message, 1, 1), '"', '') + SUBSTRING(Message, 2, LEN(Message) - 2) + REPLACE(SUBSTRING(Message, LEN(Message), 1), '"', '')
EDIT: As Martin Smith pointed out, my original code wouldn't work if a string was under two characters, so ...
CREATE TABLE #Message (Message VARCHAR(20))
INSERT INTO #Message (Message)
SELECT '"SomeText"'
UNION
SELECT '"SomeText'
UNION
SELECT 'SomeText"'
UNION
SELECT 'S'
SELECT
CASE
WHEN LEN(Message) >=2
THEN REPLACE(SUBSTRING(Message, 1, 1), '"', '') + SUBSTRING(Message, 2, LEN(Message) - 2) + REPLACE(SUBSTRING(Message, LEN(Message), 1), '"', '')
ELSE Message
END AS Message
FROM #Message
DROP TABLE #Message
Try this:
SELECT REPLACE([Message], '"', '') AS [Message] FROM SomeTable

Get index of two consecutive upper case characters

I am trying to separate a city/state/zip field into the city, state, and zip. Normally I would do this with charindex of ',' to get the city and state, and isnumeric and right() for the zip.
This will work fine for the zip, but most of the rows in the data I am working with now have no commas City ST Zip. Is there a way to identify the index of two upper case characters?
If not, does anybody have a better idea than just a case statement checking for each state individually?
EDIT: I found the PATINDEX/COLLATE option to work fairly intermittently. See my answer below.
PATINDEX should work for you:
PATINDEX('% [A-Z][A-Z] %', A COLLATE Latin1_general_cs_as)
So your full extract would be something like:
WITH CTE AS
( SELECT i = PATINDEX('% [A-Z][A-Z] %', A COLLATE Latin1_general_cs_as) + 1,
A
FROM (VALUES
('City ST Zip'),
('Another City ST Zip'),
('City, with comma ST Zip')
) t (A)
)
SELECT City = LEFT(A, i - 2),
State = SUBSTRING(A, i, 2),
Zip = SUBSTRING(A, i + 3, LEN(A))
FROM CTE;
Example on SQL Fiddle
The reason why PATINDEX appears to work intermittently is that you cannot use a character range (i.e. A-Z) to accomplish a case-sensitive search, even if using a case-sensitive collation. The issue is that character ranges work like sorting, and case-sensitive sorting groups the upper-case letters with their lower-case equivalents, just like it would be ordered in a dictionary. Range sorting is really: a,A,b,B,c,C,d,D,etc. Or, depending on the collation, it might be: A,a,B,b,C,c,D,d,etc (there are 31 Collations that sort upper-case first). When doing this in a case-sensitive collation, that merely groups all A entries together, separate from the a entries, whereas in a case-insensitive sort they would be intermixed.
But if you specify each of the letters individually (hence not using a range), then it will work as expected:
PATINDEX(N'%[ABCDEFGHIJKLMNOPQRSTUVWXYZ][ABCDEFGHIJKLMNOPQRSTUVWXYZ]%',
[CityStZip] COLLATE Latin1_General_100_CS_AS)
The reason that PATINDEX and LIKE (both of which allow for a single character class of [A-Z]) work this way is that the [start-end] syntax is not a Regular Expression. Many people claim that PATINDEX and LIKE support "limited" RegEx due to supporting this syntax, but that is not true. It is merely a very similar (and a confusingly similar) syntax to RegEx where [A-Z] would normally not include any lower-case matches.
Of course, if you are guaranteed to only be searching on the US-English letters of A-Z, then a binary collation (i.e. one ending in _BIN2; don't use ones ending in _BIN as they have been deprecated since SQL Server 2005 was introduced, I believe) should work.
PATINDEX(N'%[A-Z][A-Z]%', [CityStZip] COLLATE Latin1_General_100_BIN2)
For more details about case-sensitive matching, especially in regards to including Unicode / NVARCHAR data, please see my related answer on DBA.StackExchange:
How to find values with multiple consecutive upper case characters
If you have zip code and state at the end of the string, then this might work:
select right(address, 5) as zip,
left(right(address, 8), 2) as state,
left(address, len(address) - 9) as city
You can start by removing the commas and double spaces from the address.
If you have a table of states(which you should) with a column of the abbreviations you can do things like this:
SELECT a.* FROM Addresses a
INNER JOIN States s ON
a.CityStateZip Like '% ' + s.UpperCaseAbbreviation + ' %' --space on either side of abbreviation
You can make it work for both commas and spaces:
SELECT a.* FROM Addresses a
INNER JOIN States s ON
Replace(a.CityStateZip, ',' , ' ') Like '% ' + s.UpperCaseAbbreviation + ' %'
I found the PATINDEX/COLLATE option to work fairly intermittently. Here is what I ended up doing:
--get rid of the sparsely used commas
--get rid of the duplicate spaces
update MyTable set
CityStZip=
replace(
replace(
replace(CityStZip,' ',' '),
' ',' '),
',','')
select
--check if state and zip are there and then grab the city
case when isNumeric(right(CityStZip,1))=1
then left(CityStZip,len(CityStZip)-charindex(' ',reverse(CityStZip),
charindex(' ',reverse(CityStZip))+1)+1)
--no zip. check for state
when left(right(CityStZip,3),1) = ' '
then left(CityStZip,len(CityStZip)-charIndex(' ',reverse(CityStZip)))
else CityStZip
end as City,
--check if zip is there and then grab the city
case when isNumeric(right(CityStZip,1))=1
then substring(CityStZip,
len(CityStZip)-charindex(' ',reverse(CityStZip),
charindex(' ',reverse(CityStZip))+1)+2,
2)
--no zip. check if 3rd to last char is a space and grab the last two chars
when left(right(CityStZip,3),1) = ' '
then right(CityStZip,2)
end as [State],
--grab everything after the last space if the last character is numeric
case when isNumeric(right(CityStZip,1))=1
then substring(CityStZip,
len(CityStZip)-charindex(' ',reverse(CityStZip))+1,
charindex(' ',reverse(CityStZip)))
end as Zip
from MyTable