SQL Proper Case Function Query Refinement - sql

I have a table of names that I am trying to convert from UPPERCASE to Proper Case. And the below code does almost exactly what I am. When I was testing it out I noticed that I had folks who had Roman Numerals in their name, Mc* and O'* in the table. Naturally the query converted any multiple Roman Numeral into Iv like it was supposed to and any MCDONALDS or O'DANIEL were converted into Mcdonalds and O'daniel. I was trying to figure out how to make a clean change to this function so I can run my update query but I'm still peacemilling my SQL knowledge together in off hours. Any help/suggestions would be much appreciated. I did a google search and found several examples but the ones I tried didn't work. The amount of corrections I would have to do is relatively minor (17 corrections out of 1000 row table), but I'd like to try and tidy it up to limit as many human errors as possible.
Thank you in advance for your help.
CREATE FUNCTION [dbo].[f_ProperCase]
(#Text as varchar(80))
RETURNS varchar(80) as
BEGIN
DECLARE #Reset bit
DECLARE #Ret varchar(80)
DECLARE #i int
DECLARE #c char(1)
SELECT #Reset = 1, #i=1, #Ret = ''
WHILE #i <= LEN(#Text)
SELECT #c= SUBSTRING(#Text,#i,1),
#Ret = #Ret + CASE WHEN #Reset=1 THEN UPPER(#c) ELSE LOWER(#c) END,
#Reset= CASE WHEN
CASE WHEN SUBSTRING(#Text,#i-4,5) like '_[a-z] [DOL]''' THEN 1
WHEN SUBSTRING(#Text,#i-4,5) like '_[a-z] [D][I]' THEN 1
WHEN SUBSTRING(#Text,#i-4,5) like '_[a-z] [M][C]' THEN 1
WHEN SUBSTRING(#Text,#i-4,5) like '_[a-z] [M][c][*]' THEN 1 --Convert MCDONALD to McDonald
WHEN SUBSTRING(#Text,#I-4,5) like '_[a-z] [O][''][*]' THEN 1 --Convert O'DONNEL to O'Donnel
ELSE 0
END = 1
THEN 1
ELSE CASE WHEN #c like '[a-zA-Z]' or #c in ('''') THEN 0
ELSE 1
END
END,
#i = #i +1
RETURN #Ret
end

I would do it differently:
CREATE FUNCTION [dbo].[f_ProperCase]
(#Text as varchar(80))
RETURNS varchar(80) as
BEGIN
DECLARE #Reset bit
DECLARE #Ret varchar(80)
DECLARE #i int
DECLARE #c char(1)
DECLARE #Text1 varchar(81)
SELECT #Reset = 1, #i=1, #Ret = '', #Text1 = ' ' + #Text
WHILE #i <= LEN(#Text1)
SELECT #c= SUBSTRING(#Text1,#i,1),
#Ret = #Ret + CASE WHEN #Reset=1 THEN UPPER(#c) ELSE LOWER(#c) END,
#Reset= CASE WHEN
CASE WHEN SUBSTRING(#Text1,#i-2,3) like ' [DdOoLl]''' THEN 1
WHEN SUBSTRING(#Text1,#i-2,4) like ' [Mm][cC][a-zA-Z]' THEN 1
WHEN SUBSTRING(#Text1,#i-3,5) like ' [Mm][Aa][cC][a-zA-Z]' THEN 1
ELSE 0
END = 1
THEN 1
ELSE CASE WHEN #c like '[a-zA-Z]' or #c in ('''') THEN 0
ELSE 1
END
END,
#i = #i +1
RETURN stuff(#Ret, 1, 1, '')
end
This function supports O', L', D', as well as Mc, and Mac. The function is also converts from any case (not only the upper case) to the proper case
SQL Fiddle
select dbo.f_ProperCase('CORMACK')
,dbo.f_ProperCase('Mcdonald ronald')
,dbo.f_ProperCase('o''hara')
| | | |
|---------|-----------------|--------|
| Cormack | McDonald Ronald | O'Hara |

I tweaked some to handle suffixes being included in the name. This handled almost anything I threw at it. We sometimes had hyphenated names as well as periods after some suffixes. I know handling from I-IX was overkill, but was easy enough to add the X to the V check.
ALTER FUNCTION dbo.udf_ProperCase
(#Text as varchar(1024))
RETURNS varchar(1024)
AS
/*
Created to ProperCase most common LastName (Mc/Mac/O'/D'/L') and handle I(first)-IX(nineteenth) suffixes
Original Code: https://stackoverflow.com/questions/22923616/sql-proper-case-function-query-refinement
SELECT dbo.udf_ProperCase('iitest demetrii mcdonald o''neil victor second 2nd ii iii iv v vi vii viii ix x xi test')
*/
BEGIN
DECLARE #Reset bit
DECLARE #Ret varchar(1024)
DECLARE #i int
DECLARE #c char(1)
DECLARE #Text1 varchar(1025)
SELECT #Reset = 1, #i=1, #Ret = '', #Text1 = ' ' + #Text + ' ' --Ensure one space before to make first char upper and after to handle suffixes
--Loop through each character, comparing prior/next to determine if need to handle
WHILE #i <= LEN(#Text1)
BEGIN
SELECT
#c= SUBSTRING(#Text1,#i,1)
,#Ret = #Ret + CASE WHEN #Reset=1 THEN UPPER(#c) ELSE LOWER(#c) END
,#Reset= CASE
WHEN
CASE
WHEN SUBSTRING(#Text1,#i-2,3) like '[ -][DdOoLl][''`]' THEN 1 --Names (including hyphenated) beginning with D/O/L
WHEN SUBSTRING(#Text1,#i-2,4) like ' [Mm][cC][a-zA-Z]' THEN 1 --Names with Mc
WHEN SUBSTRING(#Text1,#i-3,5) like ' [Mm][Aa][cC][a-zA-Z]' THEN 1 --Names with Mac
WHEN SUBSTRING(#Text1,#i-1,4) like ' [Ii][Ii][ .]' THEN 1 --Handle II (include ending with Space or period)
WHEN SUBSTRING(#Text1,#i-1,5) like ' [Ii][Ii][Ii][ .]' THEN 1 --Handle III
WHEN SUBSTRING(#Text1,#i-2,4) like ' [Ii][Ii][ .]' THEN 1 --Handle II
WHEN SUBSTRING(#Text1,#i-2,5) like ' [Ii][Ii][Ii][ .]' THEN 1 --Handle III
WHEN SUBSTRING(#Text1,#i-3,4) like ' [Ii][Ii][ .]' THEN 1 --Handle II
WHEN SUBSTRING(#Text1,#i-3,5) like ' [Ii][Ii][Ii][ .]' THEN 1 --Handle III
WHEN SUBSTRING(#Text1,#i-1,4) like ' [Ii][VvXx][ .]' THEN 1 --Handle IV
WHEN SUBSTRING(#Text1,#i-1,4) like ' [VvXx][Ii][ .]' THEN 1 --Handle VI
WHEN SUBSTRING(#Text1,#i-1,5) like ' [VvXx][Ii][Ii][ .]' THEN 1 --Handle VII
WHEN SUBSTRING(#Text1,#i-1,6) like ' [VvXx][Ii][Ii][Ii][ .]' THEN 1 --Handle VIII
WHEN SUBSTRING(#Text1,#i-2,4) like ' [VvXx][Ii][ .]' THEN 1 --Handle VI
WHEN SUBSTRING(#Text1,#i-2,5) like ' [VvXx][Ii][Ii][ .]' THEN 1 --Handle VII
WHEN SUBSTRING(#Text1,#i-2,6) like ' [VvXx][Ii][Ii][Ii][ .]' THEN 1 --Handle VIII
WHEN SUBSTRING(#Text1,#i-3,4) like ' [VvXx][Ii][ .]' THEN 1 --Handle VI
WHEN SUBSTRING(#Text1,#i-3,5) like ' [VvXx][Ii][Ii][ .]' THEN 1 --Handle VII
WHEN SUBSTRING(#Text1,#i-3,6) like ' [VvXx][Ii][Ii][Ii][ .]' THEN 1 --Handle VIII
ELSE 0
END = 1 THEN 1
ELSE
CASE
WHEN #c like '[a-zA-Z`]' or #c in ('''') or #c like '[0-9]' THEN 0 --If any letter, single-quote or number, then keep next lowercase
ELSE 1 --Anything else (e.g. Space dash, And(&), etc. make next Upper-Case)
END
END
,#i = #i +1
END
RETURN stuff(#Ret, 1, 1, '')
END
GO

Related

Calculate word length with SQL

Is it possible to calculate for each word its length with SQL only? For example for the following sentence:
I have a length of x.
1 4 1 6 2 1
Can try a custom method to create:
following is a sample code that can help:
Declare #products varchar(200) = 'I have a length of x.'
Declare #individual varchar(20) = null
Declare #lengthOfProducts varchar(20) = null
WHILE LEN(#products) > 0
BEGIN
IF PATINDEX('% %', #products) > 0
BEGIN
SET #individual = SUBSTRING(#products,
0,
PATINDEX('% %', #products))
SELECT #individual
SET #lengthOfProducts = #lengthOfProducts + ' ' + LEN(#individual)
SET #products = SUBSTRING(#products,
LEN(#individual + ' ') + 1,
LEN(#products))
END
ELSE
BEGIN
SET #individual = #products
SET #products = NULL
SELECT #lengthOfProducts
END
END
There is the LEN() function. You have just to use this in SQL
SELECT LEN(column_name) FROM table_name;
and this in Oracle
SELECT LENGTH(column_name) FROM table_name;
Credits : SQL LEN() Function
In sql server, use LEN(column_name).
For oracle use LENGTH(column_name)
e.g SELECT CustomerName,LEN(Address) as LengthOfAddress
FROM Customers;

I have a column with datatype nvarchar and I want to sort it in ascending order. How do I achieve it in SSRS?

This is what I'm getting
abc 1
abc 12
abc 15
abc 2
abc 3
And this is how I want
abc 1
abc 2
abc 3
abc 12
abc 15
Query that I use:
select *
from view_abc
order by col1
Use a function to strip out the non numeric characters and leave just the value. Use another function to strip out all the numeric data. You can then sort on the two returned values.
It seems like a bit of work at first but once the functions are in you can re-use them in the future. Here's two functions I use regularly when we get data in from external sources and it's not very normalised.
They may not be the most efficient functions in the world but they work for my purposes
1st a function to just leave the numeric portion.
CREATE FUNCTION [fn].[StripToAlpha]
(
#inputString nvarchar(4000)
)
RETURNS varchar(4000)
AS
BEGIN
DECLARE #Counter as int
DECLARE #strReturnVal varchar(4000)
DECLARE #Len as int
DECLARE #ASCII as int
SET #Counter=0
SET #Len=LEN(#inputString)
SET #strReturnVal = ''
WHILE #Counter<=#Len
BEGIN
SET #Counter = #Counter +1
SET #ascii= ASCII(SUBSTRING(#inputString,#counter,1))
IF(#ascii BETWEEN 65 AND 90) OR (#ascii BETWEEN 97 AND 122)
BEGIN
SET #strReturnVal = #strReturnVal + (SUBSTRING(#inputString,#counter,1))
END
END
RETURN #strReturnVal
END
2nd a function to extract the value from a text field, this also handle percentages (e.g. abc 23% comes out as 0.23) but this is not required in your case.
You'll need to CREATE an 'fn' schema of change the schema name first...
CREATE FUNCTION [fn].[ConvertToValue]
(
#inputString nvarchar(4000)
)
RETURNS Float
AS
BEGIN
DECLARE #Counter as int
DECLARE #strReturnVal varchar(4000)
DECLARE #ReturnVal Float
DECLARE #Len as int
DECLARE #ASCII as int
SET #Counter=0
SET #Len=LEN(#inputString)
SET #strReturnVal = ''
IF #inputString IS NULL
BEGIN
Return NULL
END
IF #Len = 0 OR LEN(LTRIM(RTRIM(#inputString))) = 0
BEGIN
SET #ReturnVal=0
END
ELSE
BEGIN
WHILE #Counter<=#Len
BEGIN
SET #Counter = #Counter +1
SET #ascii= ASCII(SUBSTRING(#inputString,#counter,1))
IF(#ascii BETWEEN 48 AND 57) OR (#ascii IN (46,37))
BEGIN
SET #strReturnVal = #strReturnVal + (SUBSTRING(#inputString,#counter,1))
END
END
if RIGHT(#strReturnVal,1)='%'
BEGIN
SET #strReturnVal = LEFT(#strReturnVal,len(#strReturnVal)-1)
SET #strReturnVal = CAST((CAST(#strReturnVal AS FLOAT)/100) AS nvarchar(4000))
END
SET #ReturnVal = ISNULL(#strReturnVal,0)
END
RETURN #ReturnVal
END
Now we have the two functions created you can simply do
SELECT *
FROM view_abc
ORDER BY fn.StripToAlpha(Col1), fn.ConvertToValue(Col1)
Try this
Edited :
SELECT CAST(SUBSTRING(ColumnNameToOrder, CHARINDEX(' ', ColumnNameToOrder, 0), LEN (ColumnNameToOrder)) AS INT) AS IntColumn, SUBSTRING(ColumnNameToOrder,0, CHARINDEX(' ', ColumnNameToOrder, 0)) AS CharColumn, * FROM view_abc ORDER BY Charcolumn, Intcolumn
Instead of ColumnNameToOrder, you can put your column name which contains the data like 'abc 123'...
Tell me if it works please.
This is what I have come up with. Maybe it can help you, or at least point you in the right direction.
I tested the values. When the values have a zero, the order is like you would like the order to be. Like this:
abc 01
abc 02
abc 03
abc 12
abc 15
So you can run this query to update the existing values, to add the zero.
UPDATE abc
SET col1 = 'abc 0' + SUBSTRING(col1, 5, 1)
WHERE LEN(col1) = 5
Or you can do the above query like this if the first three characters can vary:
UPDATE abc
SET col1 = (SUBSTRING(col1, 1, 3) + ' 0' + SUBSTRING(col1, 5, 1))
WHERE col1 LIKE 'abc__'
This will override the existing value in the col1 column, only when the length of the current String is of length 5.
Then you can run the following query to get the results:
SELECT col1
FROM abc
ORDER BY col1 ASC
=cint(right(Fields!Col1.Value, instrrev(Fields!Col1.Value, " ")-1))
This will work in SSRS and sort correctly, but will only work if Col1 always contains a space, and that the characters after the space can be converted to an integer.

Change Case to Upper & Lower in SQL Query SELECT

I have a SQL Query to pull a customer name from database, create a customer folder with that name. In the query I am killing slashes and periods, and i am also changing any result containing MCDONALD to SIMPLY "MCDONALDS". So MCDONALDS #123 comes out as simply MCDONALDS. Here is my query.
SELECT
CASE WHEN CHARINDEX('MCDONALD', cust_name) = 0
THEN REPLACE(REPLACE([cust_name],'.',''),'--','-')
ELSE 'MCDONALDS'
END cust_name
FROM job,dbo.cust cust_name
WHERE job.cust_id_bill_to = cust_name.cust_code AND
job.job_id = '44321' AND
job.sub_job_id = '2'
So, results now are:
MCDONALDS,
BRISTOL-MYERS,
TRUMP-CASINO
Desired results is upper and lower case in every word.. like this:
Mcdonalds,
Bristol-Myers,
Trump-Casino
I know its easy but for me its how to have it work WITH the rest of my query, what I am trying just not working so far.
Check Nimit Dudani Answer Here
DELIMITER $$
DROP FUNCTION IF EXISTS `test`.`initcap`$$
CREATE FUNCTION `initcap`(x char(30)) RETURNS char(30) CHARSET utf8
BEGIN
SET #str='';
SET #l_str='';
WHILE x REGEXP ' ' DO
SELECT SUBSTRING_INDEX(x, ' ', 1) INTO #l_str;
SELECT SUBSTRING(x, LOCATE(' ', x)+1) INTO x;
SELECT CONCAT(#str, ' ', CONCAT(UPPER(SUBSTRING(#l_str,1,1)),LOWER(SUBSTRING(#l_str,2)))) INTO #str;
END WHILE;
RETURN LTRIM(CONCAT(#str, ' ', CONCAT(UPPER(SUBSTRING(x,1,1)),LOWER(SUBSTRING(x,2)))));
END$$
DELIMITER ;
Usage:
select initcap('This is test string');
For Sql Server you can use this
INITCAP User defined Function code
IF OBJECT_ID('dbo.InitCap') IS NOT NULL
DROP FUNCTION dbo.InitCap;
GO
CREATE FUNCTION dbo.InitCap (#inStr VARCHAR(8000))
RETURNS VARCHAR(8000)
AS
BEGIN
DECLARE #outStr VARCHAR(8000) = LOWER(#inStr),
#char CHAR(1),
#alphanum BIT = 0,
#len INT = LEN(#inStr),
#pos INT = 1;
-- Iterate through all characters in the input string
WHILE #pos <= #len BEGIN
-- Get the next character
SET #char = SUBSTRING(#inStr, #pos, 1);
-- If the position is first, or the previous characater is not alphanumeric
-- convert the current character to upper case
IF #pos = 1 OR #alphanum = 0
SET #outStr = STUFF(#outStr, #pos, 1, UPPER(#char));
SET #pos = #pos + 1;
-- Define if the current character is non-alphanumeric
IF ASCII(#char) <= 47 OR (ASCII(#char) BETWEEN 58 AND 64) OR
(ASCII(#char) BETWEEN 91 AND 96) OR (ASCII(#char) BETWEEN 123 AND 126)
SET #alphanum = 0;
ELSE
SET #alphanum = 1;
END
RETURN #outStr;
END
GO
Testing :
SELECT dbo.InitCap('new york');
Result: New York
For your query
SELECT
dbo.InitCap(CASE WHEN CHARINDEX('MCDONALD', cust_name) = 0
THEN REPLACE(REPLACE([cust_name],'.',''),'--','-')
THEN 'MCDONALDS'
END) cust_name
FROM job,dbo.cust cust_name
WHERE job.cust_id_bill_to = cust_name.cust_code AND
job.job_id = '44321' AND
job.sub_job_id = '2'

isnumeric function not providing expected results

The dsply_val is a varchar(2000) field. When i run the query below i am getting 0 for the isnumeric columns even though these values appear to be numeric. I am trying to perform a simple calculation on this column but I end up getting conversion error when I try to convert to decimal or float. As you can see by the 15.2 value some actually do come back as numeric.
I exported the dsply_val to excel and verified that there are no letters or anything out of the ordinary.
What else could i attempt with this or what am I not doing correctly?
SELECT obsv_cd_name,
dsply_val,
ISNUMERIC(dsply_val),
ISNUMERIC(dsply_val + 'e0'),
ISNUMERIC(rtrim(Ltrim(dsply_val)))
FROM smsmir.mir_sr_obsv
where obsv_cd_name = 'WBC'
and sort_dtime >= '2015-01-25'
and orgz_cd = 'CFVMC'
and dsply_val NOT like ('%SEE%')
WBC 8.6 0 0 0
WBC 7.8 0 0 0
WBC 13.4 0 0 0
WBC 5.9 0 0 0
WBC 8.2 0 0 0
WBC 5.9 0 0 0
WBC 7.5 0 0 0
WBC 15.2 1 1 1
WBC 15.2 0 0 0
I think you have line feed or carriage return or tab char in your column.
Actually I can reproduce your problem:
SET NOCOUNT ON
GO
Set string to 6 + Carriage Return + Line Feed + Tab
DECLARE #s NVARCHAR(MAX) = '6' + CHAR(13) + CHAR(10) + CHAR(9)
You see just 6
SELECT #s
But the length of string is 4:
SELECT LEN(#s)
Here are results for ISNUMERIC function:
SELECT ISNUMERIC(#s)
Even TRIM doesn't help:
SELECT ISNUMERIC(LTRIM(RTRIM(#s)))
You should replace those chars:
SELECT ISNUMERIC(REPLACE(REPLACE(REPLACE(#s, CHAR(10), ''), CHAR(13), ''), CHAR(9), ''))
You can print out all chars in your string, so you can then replace non-digit chars in string with blank '':
DECLARE #i INT = 1
WHILE #i <= len(#s)
BEGIN
PRINT ASCII(SUBSTRING(#s, #i, 1))
SET #i = #i + 1
END
Use this function instead of ISNUMERIC. Trim functionality has been added in the function.
CREATE FUNCTION dbo.isReallyNumeric
(
#num VARCHAR(64)
)
RETURNS BIT
BEGIN
SET #num = LTRIM(RTRIM(#num))
IF LEFT(#num, 1) = '-'
SET #num = SUBSTRING(#num, 2, LEN(#num))
DECLARE #pos TINYINT
SET #pos = 1 + LEN(#num) - CHARINDEX('.', REVERSE(#num))
RETURN CASE
WHEN PATINDEX('%[^0-9.-]%', #num) = 0
AND #num NOT IN ('.', '-', '+', '^')
AND LEN(#num)>0
AND #num NOT LIKE '%-%'
AND
(
((#pos = LEN(#num)+1)
OR #pos = CHARINDEX('.', #num))
)
THEN
1
ELSE
0
END
END
GO
And use the query
SELECT ColumnName
FROM Yourtable
WHERE DBO.isReallyNumeric(ColumnName)=1

Incrementing a combination of nvarchar and numbers [duplicate]

This question already has answers here:
Formatting Numbers by padding with leading zeros in SQL Server
(14 answers)
Closed 9 years ago.
following scenario:
I need to increment a code like this one DS0000001
So next Item would be DS0000002, following DS0000003 and so on.
So for generating this code in a big INSERT Statement I wrote the following function:
CREATE FUNCTION dbo.eufn_e5_importCRM_getNextClientCode(
#partnerCode AS NVARCHAR(10),
#number AS INT
)
RETURNS NVARCHAR(20)
AS
BEGIN
DECLARE #next AS INT
DECLARE #zeroes AS NVARCHAR(10)
DECLARE #nextStr AS NVARCHAR(10)
DECLARE #return AS NVARCHAR(20)
DECLARE #partnerPrefix AS NVARCHAR(10)
SELECT #next = RIGHT(c.clientCode,7) FROM Clients c
SELECT #partnerPrefix = partners.partnerPrefix
FROM partners WHERE partnerCode = #partnerCode
SET #next = ISNULL(#next,0) + #number
SET #nextStr = CONVERT(NVARCHAR(10),#next)
SELECT #zeroes = CASE WHEN #next > 9 THEN '00000'
WHEN #next > 99 THEN '0000'
WHEN #next > 999 THEN '000'
WHEN #next > 9999 THEN '00'
WHEN #next > 99999 THEN '0'
WHEN #next > 999999 THEN ''
ELSE '000000'
END
IF ISNULL(#next,'') = ''
BEGIN
SET #return = #partnerPrefix + #zeroes + '1'
END
ELSE SET #return = #partnerPrefix + #zeroes + #nextStr
RETURN #return
END
#number gets the row number I added in the insert statement manually with the ROW_NUMBER()-function.
This already works fine - it inserts the right code into the table.
What I don't like about the code is this part here:
SELECT #zeroes = CASE WHEN #next > 9 THEN '00000'
WHEN #next > 99 THEN '0000'
WHEN #next > 999 THEN '000'
WHEN #next > 9999 THEN '00'
WHEN #next > 99999 THEN '0'
WHEN #next > 999999 THEN ''
ELSE '000000'
END
I already searched a while on stackoverflow and also googled, but I could not find a function like convert that leaves the "0" standing where they are.
Any ideas how to make this function more flexible and if possible: reusable?
Thanks in advance for any help!
Here's an you can do it like this
Declare #a int
Set #a =8756
Select 'DS' + REPLACE(STR(#a, 7), SPACE(1), '0')
Demo: http://sqlfiddle.com/#!3/d41d8