Substring in Informix - substring

I need to split a field(Name) into Two (First Name and Last Name) based on a comma in Informix.
Example: "Mark, Wheeler J" has to be split as Mark and Wheeler. I have tried using SQL syntax but it gives me a syntax error in Informix. Please help me.
SELECT
SUBSTRING(name, 1, CHARINDEX(',', name ) - 1) AS FirstName,
SUBSTRING(name, CHARINDEX(',', name) + 1, 8000) AS LastName
FROM
employee

At first if you need to split such values and if you need to do it often then it would be easier to change your db schema:
rename column name into last_name
add column first_name
It has some advantages. You probably want to search employees by last name, and it is easy when you simply have such column. If last name is a part of name column then you must search using LIKE which is slower and worse.
Now you will have to change some data. If you have comma in last_name then in such column there is first and last name and you must split it.
If you have charindex() function you can do it with:
UPDATE employees SET last_name=substring(last_name FROM charindex(',', last_name)+1), first_name=substring(last_name FROM 1 FOR charindex(',', last_name)-1) WHERE charindex(',', last_name) > 0;
(you can also use TRIM() to remove spaces before/after comma which will be copied)
From comments I see that your version of Informix do not have CHARINDEX() function so you must upgrade db engine or use technique other than clean SQL.
If you can use programming language like Java or Python (for this example I use Jython: it is Python that work in Java environment and can use JDBC driver) you can:
db = DriverManager.getConnection(db_url, usr, passwd)
# prepare UPDATE:
pu = db.prepareStatement("UPDATE employee SET last_name=?, first_name=? WHERE id=?")
# search for names that must be changed:
pstm = prepareStatement("SELECT id, last_name FROM employee WHERE last_name LIKE '%,%')
# for each record found remember its `id`, split `first_name` and update it:
rs = pstm.executeQuery()
while (rs.next()):
id = rs.getInt(1)
name = rs.getString(2)
first_name, last_name = name.split(',')
pu.setString(1, last_name.strip())
pu.setString(2, first_name.strip())
pu.setInt(3, id)
rc = pu.executeUpdate()

I have faced a similar problem so I have developed a below function "char_cut" for informix 11.50 (haven't tried on different versions) Be warn that this is not the most efficient way to do that, but it works fine.
Usage:
SELECT
char_cut(name, ',', 1) AS FirstName,
char_cut(name, ',', 2) AS LastName
FROM
employee
Procedure:
create procedure char_cut( str_in varchar(255), separator_in char(1), field_in int )
returning varchar(255) ;
define res varchar(255);
define str_len int;
define pos_curr int;
define substr_start int;
define substr_length int;
define pos_char char(1);
IF field_in <= 0 THEN return ''; END IF;
LET res = '';
LET substr_start = 0;
LET substr_length = 0;
LET str_len = length(str_in);
FOR pos_curr = 1 TO str_len
LET pos_char = substr(str_in,pos_curr,1);
IF pos_char = separator_in THEN
LET field_in = field_in - 1;
END IF;
IF field_in = 1 and substr_start = 0 THEN
LET substr_start = pos_curr + DECODE(pos_char,separator_in,1,0);
END IF;
IF field_in <= 0 THEN
LET substr_length = pos_curr;
EXIT FOR; --KONIEC
END IF;
END FOR;
IF substr_length = 0 THEN
LET substr_length = str_len+1;
END IF;
IF substr_start = 0 THEN
LET substr_start = str_len+1;
END IF;
IF substr_length < substr_start THEN
LET substr_length = 0;
ELSE
LET substr_length = substr_length - substr_start;
END IF;
RETURN NVL(substring ( str_in from substr_start for substr_length ),'');
end procedure;

Related

Access pass-through query passing comma separated list as a parameter to SQL stored procedure

The SQL server is 2008. I have an Access 2016 front-end for reporting purposes. One report requires that one or more Product Classes from a list be chosen to report on. I have the VBA that creates the pass-through query with the appropriate single line:
exec dbo.uspINVDAYS 'A3,A4,A6,AA,AB'
I have this SQL code that should take the list as hard-coded here:
DECLARE #parProductClasses NVARCHAR(200) = 'A3,A4,A6,AA,AB';
DECLARE #ProductClasses NVARCHAR(200),#delimiter NVARCHAR(1) = ',';
SET #ProductClasses = #parProductClasses;
DECLARE #DAYS INT,#numDAYS int;
SET #DAYS = 395;
SET #numDAYS = #DAYS;
SELECT UPINVENTORY.StockCode, UPINVENTORY.[Description], UPINVENTORY.Supplier, UPINVENTORY.ProductClass
, UPINVENTORY.WarehouseToUse
, CAST(UPINVENTORY.Ebq AS INT)Ebq
, cast(UPINVENTORY.QtyOnHand AS INT)QtyOnHand
, cast(UPINVENTORY.PrevYearQtySold AS INT)PrevYearQtySold
, cast(UPINVENTORY.YtdQtyIssued AS INT)YtdQtyIssued
,#numDAYS as numDAYS
,CAST(ROUND((PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS,0) AS INT)TOTAL
,CASE WHEN (PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS
= 0 THEN 0
ELSE CAST(ROUND(QTYONHAND/((PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS),0)AS INT)
END FINAL
,CASE WHEN (PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS
= 0 THEN 0
ELSE CAST(ROUND(QTYONHAND/((PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS),0)AS INT)
END FINAL1
FROM
TablesCoE.dbo.vwRPUpInventory UPINVENTORY
WHERE UPINVENTORY.ProductClass IN (Select val From TablesCoE.dbo.split(#ProductClasses,','));
When I run this I get:
Msg 468, Level 16, State 9, Line 9
Cannot resolve the collation conflict between "SQL_Latin1_General_CP1_CI_AS" and "Latin1_General_BIN" in the equal to operation.
I cannot determine where
COLLATE SQL_Latin1_General_CP1_CI_AS
should go. Where am I equating or comparing? The SQL IN clause cannot handle the comma-separated list since it is not a strict SQL table.
Here's the code used to create the dbo.split() function:
CREATE FUNCTION dbo.split(
#delimited NVARCHAR(MAX),
#delimiter NVARCHAR(100)
) RETURNS #t TABLE (id INT IDENTITY(1,1), val NVARCHAR(MAX))
AS
BEGIN
DECLARE #xml XML
SET #xml = N'<t>' + REPLACE(#delimited,#delimiter,'</t><t>') + '</t>'
INSERT INTO #t(val)
SELECT r.value('.','varchar(MAX)') as item
FROM #xml.nodes('/t') as records(r)
RETURN
END
Thanks to Sandeep Mittal and I am sure others have very similar split functions. Run separately this function does operate as expected and provides a table of the comma-separated list objects.
DECLARE #parProductClasses NVARCHAR(200) = 'A3,A4,A6,AA,AB';
DECLARE #ProductClasses NVARCHAR(200),#delimiter NVARCHAR(1) = ',';
SET #ProductClasses = #parProductClasses;
Select val From TablesCoE.dbo.split(#ProductClasses,',')
Returns
val
A3
A4
A6
AA
AB
try this.
WHERE concat(',',#ProductClasses,',') like concat('%',UPINVENTORY.ProductClass,'%')
it's a silly way of checking if your productClass is within the #productClasses list.
After attempting to use a prefabricated table-valued variable versus on the fly in the WHERE clause, neither worked, I then started to try different placements of the COLLATE statement. I was complacent in applying COLLATE to the right-side with the collation listed on the left in the SQL error message. I tried the collation listed on the right of the SQL error message to the left side of the WHERE clause and the SQL code works to spec now. Here it is:
DECLARE #parProductClasses NVARCHAR(200) = 'A3,A4,A6,AA,AB';
DECLARE #ProductClasses NVARCHAR(200),#delimiter NVARCHAR(1) = ',';
SET #ProductClasses = #parProductClasses;
DECLARE #DAYS INT,#numDAYS int;
SET #DAYS = 395;
SET #numDAYS = #DAYS;
SELECT UPINVENTORY.StockCode, UPINVENTORY.[Description], UPINVENTORY.Supplier, UPINVENTORY.ProductClass
, UPINVENTORY.WarehouseToUse
, CAST(UPINVENTORY.Ebq AS INT)Ebq
, cast(UPINVENTORY.QtyOnHand AS INT)QtyOnHand
, cast(UPINVENTORY.PrevYearQtySold AS INT)PrevYearQtySold
, cast(UPINVENTORY.YtdQtyIssued AS INT)YtdQtyIssued
,#numDAYS as numDAYS
,CAST(ROUND((PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS,0) AS INT)TOTAL
,CASE WHEN (PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS
= 0 THEN 0
ELSE CAST(ROUND(QTYONHAND/((PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS),0)AS INT)
END FINAL
,CASE WHEN (PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS
= 0 THEN 0
ELSE CAST(ROUND(QTYONHAND/((PREVYEARQTYSOLD + YTDQTYISSUED)/#DAYS),0)AS INT)
END FINAL1
FROM
TablesCoE.dbo.vwRPUpInventory UPINVENTORY
WHERE UPINVENTORY.ProductClass COLLATE Latin1_General_BIN IN (SELECT val FROM TablesCoE.dbo.split(#ProductClasses,','));
Thanks for your suggestions #Krish and #Isaac.
Tim

get rank of players' height in plpgsql

We are going to have the rank of height, but I got 0 for all players
I convert the feet and inches into cm first, and use the sample code teacher gave us.
Here is my code:
CREATE OR REPLACE FUNCTION player_height_rank (firstname VARCHAR, lastname VARCHAR) RETURNS int AS $$
DECLARE
rank INTEGER:= 0;
offset INTEGER:= 0;
tempValue FLOAT:= NULL;
r record;
BEGIN
FOR r IN SELECT ((p.h_feet * 30.48) + (p.h_inches * 2.54)) AS height, p.firstname, p.lastname
FROM players p
ORDER BY ((p.h_feet * 30.48) + (p.h_inches * 2.54)) DESC, p.firstname, p.lastname
LOOP
IF r.height = tempValue then
offset := offset + 1;
ELSE
rank := rank + offset + 1;
offset := 0;
tempValue := r.height;
END IF;
IF r.firstname = $1 AND r.lastname = $2 THEN
RETURN rank;
END IF;
END LOOP;
-- not in DB
RETURN 0;
END;
$$ LANGUAGE plpgsql;
--select * from player_height_rank('Ming', 'Yao');
Your function works fine for me if I correct two bugs:
One of your commas is not really a comma, but a “fullwidth comma”, UNICODE code point FF0C, which causes a syntax error.
You have a variable name offset, which causes SQL syntax errors because it is a reserved key word in SQL. If you really need to use that name, you have to enclose it in double quotes (") throughout, but it is better to choose a different name.
The reason this causes a problem is that an assignment like offset := offset + 1; in PL/pgSQL is translated into an SQL statement like SELECT offset + 1 INTO offset;.
You can do the whole thing in a single SQL query, which is more efficient:
SELECT rank
FROM (SELECT firstname,
lastname,
rank() OVER (ORDER BY h_feet + 12 * h_inches)
FROM players
) dummy
WHERE firstname = 'Ming'
AND lastname = 'Yao';

Add comma every nth character in value

my problem is pretty simple. I get a value from a sql select which looks like this:
ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG
and I need it like this:
AR,AM,AU,BE,BA,BR,BG,CN,DK,DE,EE,FO,FI,FR,GE,GR,IE,IS,IT,JP,YU,CA,KZ,KG
The length is different in each dataset.
I tried it with format(), stuff() and so on but nothing brought me the result I need.
Thanks in advance
With a little help of a numbers table and for xml path.
-- Sample table
declare #T table
(
Value nvarchar(100)
)
-- Sample data
insert into #T values
('ARAMAU'),
('ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG')
declare #Len int
set #Len = 2;
select stuff(T2.X.value('.', 'nvarchar(max)'), 1, 1, '')
from #T as T1
cross apply (select ','+substring(T1.Value, 1+Number*#Len, #Len)
from Numbers
where Number >= 0 and
Number < len(T1.Value) / #Len
order by Number
for xml path(''), type) as T2(X)
Try on SE-Data
Time to update your resume.
create function DontDoThis (
#string varchar(max),
#count int
)
returns varchar(max)
as
begin
declare #result varchar(max) = ''
declare #token varchar(max) = ''
while DATALENGTH(#string) > 0
begin
select #token = left(#string, #count)
select #string = REPLACE(#string, #token, '')
select #result += #token + case when DATALENGTH(#string) = 0 then '' else ',' end
end
return #result
end
Call:
declare #test varchar(max) = 'ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG'
select dbo.DontDoThis(#test, 2)
gbn's comment is exactly right, if not very diplomatic :) TSQL is a poor language for string manipulation, but if you write a CLR function to do this then you will have the best of both worlds: .NET string functions called from pure TSQL.
I believe this is what QQping is looking for.
-- select .dbo.DelineateEachNth('ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG',2,',')
create function DelineateEachNth
(
#str varchar(max), -- Incoming String to parse
#length int, -- Length of desired segment
#delimiter varchar(100) -- Segment delimiter (comma, tab, line-feed, etc)
)
returns varchar(max)
AS
begin
declare #resultString varchar(max) = ''
-- only set delimiter(s) when lenght of string is longer than desired segment
if LEN(#str) > #length
begin
-- continue as long as there is a remaining string to parse
while len(#str) > 0
begin
-- as long as know we still need to create a segment...
if LEN(#str) > #length
begin
-- build result string from leftmost segment length
set #resultString = #resultString + left(#str, #length) + #delimiter
-- continually shorten result string by current segment
set #str = right(#str, len(#str) - #length)
end
-- as soon as the remaining string is segment length or less,
-- just use the remainder and empty the string to close the loop
else
begin
set #resultString = #resultString + #str
set #str = ''
end
end
end
-- if string is less than segment length, just pass it through
else
begin
set #resultString = #str
end
return #resultString
end
With a little help from Regex
select Wow=
(select case when MatchIndex %2 = 0 and MatchIndex!=0 then ',' + match else match end
from dbo.RegExMatches('[^\n]','ARAMAUBEBABRBGCNDKDEEEFOFIFRGEGRIEISITJPYUCAKZKG',1)
for xml path(''))

Invalid Object Error on Insert after read of same table

I am trying to insert data into a table based off what is already in the table. I read the table to get the number of records inserted for a certian month and then insert information based off if it is more or less than ten. After I read the table it throws an invalid object name when I try to do that insert. It's not an invalid object as it just read the table. If this is a permissions error how do I correct it? My code is below. Thanks,
Jason
declare #email VARCHAR(75),
#seminarNumber INT,
#isValidEmail BIT,
#monthlyTotal INT,
#statusCode INT
set #email = 'email#domain.com'
set #seminarNumber = '12345'
set #isValidEmail = dbo.RegexMatch('^[_a-zA-Z0-9-]+(\.[_a-zA-Z0-9-]+)*#[a-zA-Z0-9-]+(\.[a-zA-Z0-9-]+)*\.(([0-9]{1,3})|([a-zA-Z]{2,3})|(aero|coop|info|museum|name))$',#email)
if #isValidEmail = 1
begin
SELECT #monthlyTotal = count(mailid)
from Email_Tracking
where emailaddress = #email
and year(datesent) = year(getdate())
and month(datesent) = month(getdate())
if #monthlyTotal > 10
begin
set #statusCode = 1
end
else
begin
set #statusCode = 2
end
end
else
begin
set #statusCode = 3
end
if #statusCode = 1
begin
insert Email_Tracking ('seminarNumber','email','reasonNotSent')
values(#seminarNumber,#email,'Maximum surveys for the month have already been sent')
end
else if #statusCode = 2
begin
insert Email_Tracking ('seminarNumber','email','datesent')
values(#seminarNumber,#email,getdate())
end
else if #statusCode = 3
begin
insertEmail_Tracking ('seminarNumber','email','reasonNotSent')
values(#seminarNumber,#email,'Email address missing or invalid')
end
print #statusCode
try removing quotes from column names. so for eg:
insert Email_Tracking (seminarNumber,email,reasonNotSent)
values(#seminarNumber,#email,'Email address missing or invalid')

T-SQL function for generating slugs?

Quick check to see if anyone has or knows of a T-SQL function capable of generating slugs from a given nvarchar input. i.e;
"Hello World" > "hello-world"
"This is a test" > "this-is-a-test"
I have a C# function that I normally use for these purposes, but in this case I have a large amount of data to parse and turn into slugs, so it makes more sense to do it on the SQL Server rather than have to transfer data over the wire.
As an aside, I don't have Remote Desktop access to the box so I can't run code (.net, Powershell etc) against it
Thanks in advance.
EDIT:
As per request, here's the function I generally use to generate slugs:
public static string GenerateSlug(string n, int maxLength)
{
string s = n.ToLower();
s = Regex.Replace(s, #"[^a-z0-9s-]", "");
s = Regex.Replace(s, #"[s-]+", " ").Trim();
s = s.Substring(0, s.Length <= maxLength ? s.Length : maxLength).Trim();
s = Regex.Replace(s, #"s", "-");
return s;
}
You can use LOWER and REPLACE to do this:
SELECT REPLACE(LOWER(origString), ' ', '-')
FROM myTable
For wholesale update of the column (the code sets the slug column according to the value of the origString column:
UPDATE myTable
SET slug = REPLACE(LOWER(origString), ' ', '-')
This is what I've come up with as a solution. Feel free to fix / modify where needed.
I should mention that the database I'm currently developing against is case insensitive hence the LOWER(#str).
CREATE FUNCTION [dbo].[UDF_GenerateSlug]
(
#str VARCHAR(100)
)
RETURNS VARCHAR(100)
AS
BEGIN
DECLARE #IncorrectCharLoc SMALLINT
SET #str = LOWER(#str)
SET #IncorrectCharLoc = PATINDEX('%[^0-9a-z ]%',#str)
WHILE #IncorrectCharLoc > 0
BEGIN
SET #str = STUFF(#str,#incorrectCharLoc,1,'')
SET #IncorrectCharLoc = PATINDEX('%[^0-9a-z ]%',#str)
END
SET #str = REPLACE(#str,' ','-')
RETURN #str
END
Mention to: http://blog.sqlauthority.com/2007/05/13/sql-server-udf-function-to-parse-alphanumeric-characters-from-string/ for the original code.
I know this is an old thread, but for future generation, I found one function that deals even with accents here:
CREATE function [dbo].[slugify](#string varchar(4000))
RETURNS varchar(4000) AS BEGIN
declare #out varchar(4000)
--convert to ASCII
set #out = lower(#string COLLATE SQL_Latin1_General_CP1251_CS_AS)
declare #pi int
--I'm sorry T-SQL have no regex. Thanks for patindex, MS .. :-)
set #pi = patindex('%[^a-z0-9 -]%',#out)
while #pi>0 begin
set #out = replace(#out, substring(#out,#pi,1), '')
--set #out = left(#out,#pi-1) + substring(#out,#pi+1,8000)
set #pi = patindex('%[^a-z0-9 -]%',#out)
end
set #out = ltrim(rtrim(#out))
-- replace space to hyphen
set #out = replace(#out, ' ', '-')
-- remove double hyphen
while CHARINDEX('--', #out) > 0 set #out = replace(#out, '--', '-')
return (#out)
END
Here's a variation of Jeremy's response. This might not technically be slugifying since I'm doing a couple of custom things like replacing "." with "-dot-", and stripping out apostrophes. Main improvement is this one also strips out all consecutive spaces, and doesn't strip out preexisting dashes.
create function dbo.Slugify(#str nvarchar(max)) returns nvarchar(max)
as
begin
declare #IncorrectCharLoc int
set #str = replace(replace(lower(#str),'.',' dot '),'''','')
-- remove non alphanumerics:
set #IncorrectCharLoc = patindex('%[^0-9a-z -]%',#str)
while #IncorrectCharLoc > 0
begin
set #str = stuff(#str,#incorrectCharLoc,1,' ')
set #IncorrectCharLoc = patindex('%[^0-9a-z -]%',#str)
end
-- remove consecutive spaces:
while charindex(' ',#str) > 0
begin
set #str = replace(#str, ' ', ' ')
end
set #str = replace(#str,' ','-')
return #str
end
I took Jeremy's response a couple steps further by removing all consecutive dashes even after spaces are replaced, and removed leading and trailing dashes.
create function dbo.Slugify(#str nvarchar(max)) returns nvarchar(max) as
begin
declare #IncorrectCharLoc int
set #str = replace(replace(lower(#str),'.','-'),'''','')
-- remove non alphanumerics:
set #IncorrectCharLoc = patindex('%[^0-9a-z -]%',#str)
while #IncorrectCharLoc > 0
begin
set #str = stuff(#str,#incorrectCharLoc,1,' ')
set #IncorrectCharLoc = patindex('%[^0-9a-z -]%',#str)
end
-- replace all spaces with dashes
set #str = replace(#str,' ','-')
-- remove consecutive dashes:
while charindex('--',#str) > 0
begin
set #str = replace(#str, '--', '-')
end
-- remove leading dashes
while charindex('-', #str) = 1
begin
set #str = RIGHT(#str, len(#str) - 1)
end
-- remove trailing dashes
while len(#str) > 0 AND substring(#str, len(#str), 1) = '-'
begin
set #str = LEFT(#str, len(#str) - 1)
end
return #str
end
-- Converts a title such as "This is a Test" to an all lower case string such
-- as "this-is-a-test" for use as the slug in a URL. All runs of separators
-- (whitespace, underscore, or hyphen) are converted to a single hyphen.
-- This is implemented as a state machine having the following four states:
--
-- 0 - initial state
-- 1 - in a sequence consisting of valid characters (a-z, A-Z, or 0-9)
-- 2 - in a sequence of separators (whitespace, underscore, or hyphen)
-- 3 - encountered a character that is neither valid nor a separator
--
-- Once the next state has been determined, the return value string is
-- built based on the transitions from the current state to the next state.
--
-- State 0 skips any initial whitespace. State 1 includes all valid slug
-- characters. State 2 converts multiple separators into a single hyphen
-- and skips trailing whitespace. State 3 skips any punctuation between
-- between characters and, if no additional whitespace is encountered,
-- then the punctuation is not treated as a word separator.
--
CREATE FUNCTION ToSlug(#title AS NVARCHAR(MAX))
RETURNS VARCHAR(MAX)
AS
BEGIN
DECLARE #retval AS VARCHAR(MAX) = ''; -- return value
DECLARE #i AS INT = 1; -- title index
DECLARE #c AS CHAR(1); -- current character
DECLARE #state AS INT = 0; -- current state
DECLARE #nextState AS INT; -- next state
DECLARE #tab AS CHAR(1) = CHAR(9); -- tab
DECLARE #lf AS CHAR(1) = CHAR(10); -- line feed
DECLARE #cr AS CHAR(1) = CHAR(13); -- carriage return
DECLARE #separators AS CHAR(8) = '[' + #tab + #lf + #cr + ' _-]';
DECLARE #validchars AS CHAR(11) = '[a-zA-Z0-9]';
WHILE (#i <= LEN(#title))
BEGIN
SELECT #c = SUBSTRING(#title, #i, 1),
#nextState = CASE
WHEN #c LIKE #validchars THEN 1
WHEN #state = 0 THEN 0
WHEN #state = 1 THEN CASE
WHEN #c LIKE #separators THEN 2
ELSE 3 -- unknown character
END
WHEN #state = 2 THEN 2
WHEN #state = 3 THEN CASE
WHEN #c LIKE #separators THEN 2
ELSE 3 -- stay in state 3
END
END,
#retval = #retval + CASE
WHEN #nextState != 1 THEN ''
WHEN #state = 0 THEN LOWER(#c)
WHEN #state = 1 THEN LOWER(#c)
WHEN #state = 2 THEN '-' + LOWER(#c)
WHEN #state = 3 THEN LOWER(#c)
END,
#state = #nextState,
#i = #i + 1
END
RETURN #retval;
END
To slug with Vietnamese unicode
CREATE function [dbo].[toslug](#string nvarchar(4000))
RETURNS varchar(4000) AS BEGIN
declare #out nvarchar(4000)
declare #from nvarchar(255)
declare #to varchar(255)
--convert to ASCII dbo.slugify
set #string = lower(#string)
set #out = #string
set #from = N'ýỳỷỹỵáàảãạâấầẩẫậăắằẳẵặéèẻẽẹêếềểễệúùủũụưứừửữựíìỉĩịóòỏõọơớờởỡợôốồổỗộđ·/_,:;'
set #to = 'yyyyyaaaaaaaaaaaaaaaaaeeeeeeeeeeeuuuuuuuuuuuiiiiioooooooooooooooood------'
declare #pi int
set #pi = 1
--I'm sorry T-SQL have no regex. Thanks for patindex, MS .. :-)
while #pi<=len(#from) begin
set #out = replace(#out, substring(#from,#pi,1), substring(#to,#pi,1))
set #pi = #pi + 1
end
set #out = ltrim(rtrim(#out))
-- replace space to hyphen
set #out = replace(#out, ' ', '-')
-- remove double hyphen
while CHARINDEX('--', #out) > 0 set #out = replace(#out, '--', '-')
return (#out)
END