Using a SQL User Defined Function with an UPDATE - sql

I have a Function that removes any non alpha numerical characters, and a stored procedure that does several updates to the EMIR table. My question is, how do I write an update statement to call the function RemoveNonAlphaNumericCharacters and update the field 'Underlying_Security_Name'
USE EMIR
GO
ALTER FUNCTION [dbo].[RemoveNonAlphaNumericCharacters]
(
-- Add the parameters for the function here
#String NVARCHAR(100)
)
RETURNS nvarchar(100)
AS
BEGIN
DECLARE #Keep_Value AS NVARCHAR(50)
SET #Keep_Value = '%[^a-z0-9 ]%'
WHILE PATINDEX(#Keep_Value, #String) > 0
BEGIN
SET #String = STUFF(#String, PATINDEX(#Keep_Value, #String), 1, '')
END
RETURN #String
END
GO

You could do something like this.
UPDATE EMIR.table_name
SET Underlying_Security_Name = dbo.RemoveNonAlphaNumericCharacters(Underlying_Security_Name​);

Related

Indexing a computed column in sql states it is non-deterministic and fails

I have created the following computed column
ALTER TABLE
[users] ADD [LastNameCleaned] AS ([dbo].[ConvertAccents]([lastname]))
I want to add an index to LastNameCleaned but I am getting an error that the column is non-deterministic.
The [ConvertAccents] function strips diacritics from the string and returns the cleaned string.
I noticed that WITH SCHEMEBINDING is recommended to be added to the function under the RETURN and that should make it deterministic but it is not working for me.
I am trying to work out what I am missing.
I would also like to PERSIST the computed column but again because the call back from the [ConvertAccents] function it states it is non-deterministic and will not allow me to do it.
Code for ConvertAccents
CREATE FUNCTION dbo.ConvertAccents (#Text nvarchar(200))
RETURNS nvarchar(200)
AS
BEGIN
declare #TempString nvarchar(200)
--set #TempString = REPLACE(#TempString,'øß', 'oss')
set #TempString = (CONVERT(varchar(200), #Text) COLLATE Cyrillic_General_CI_AI)
set #TempString = REPLACE(#TempString,'o?', 'oss')
set #TempString = LOWER(#TempString)
return #TempString
END
GO
In this case, the problem is the usage of Convert - if that is removed, the function becomes deterministic and the computed column can be marked as persisted and indexed. You also need to add WITH SCHEMABINDING to the function:
CREATE FUNCTION dbo.ConvertAccents (#Text nvarchar(200))
RETURNS nvarchar(200)
WITH SCHEMABINDING
AS
BEGIN
declare #TempString nvarchar(200)
set #TempString = #Text COLLATE Cyrillic_General_CI_AI
set #TempString = REPLACE(#TempString,'øß', 'oss')
set #TempString = LOWER(#TempString)
return #TempString
END
Having said that, for just this replace you can do that all with a single line, without a user-defined function altogether:
create table users (
LastName nvarchar(200),
LastNameCleaned AS (LOWER(REPLACE(lastname COLLATE Cyrillic_General_CI_AI,'øß', 'oss'))) persisted
)

Creating multiple UDFs in one batch - SQL Server

I'm asking this question for SQL Server 2008 R2
I'd like to know if there is a way to create multiple functions in a single batch statement.
I've made the following code as an example; suppose I want to take a character string and rearrange its letters in alphabetical order. So, 'Hello' would become 'eHllo'
CREATE FUNCTION char_split (#string varchar(max))
RETURNS #characters TABLE
(
chars varchar(2)
)
AS
BEGIN
DECLARE #length int,
#K int
SET #length = len(#string)
SET #K = 1
WHILE #K < #length+1
BEGIN
INSERT INTO #characters
SELECT SUBSTRING(#string,#K,1)
SET #K = #K+1
END
RETURN
END
CREATE FUNCTION rearrange (#string varchar(max))
RETURNS varchar(max)
AS
BEGIN
DECLARE #SplitData TABLE (
chars varchar(2)
)
INSERT INTO #SplitData SELECT * FROM char_split(#string)
DECLARE #Output varchar(max)
SELECT #Output = coalesce(#Output,' ') + cast(chars as varchar(10))
from #SplitData
order by chars asc
RETURN #Output
END
declare #string varchar(max)
set #string = 'Hello'
select dbo.rearrange(#string)
When I try running this code, I get this error:
'CREATE FUNCTION' must be the first statement in a query batch.
I tried enclosing each function in a BEGIN END block, but no luck. Any advice?
Just use a GO statement between the definition of the UDFs
Not doable. SImple like that.
YOu can make it is one statement using a GO between them.
But as the GO is a batch delimiter.... this means you send multiple batches, which is explicitly NOT Wanted in your question.
So, no - it is not possible to do that in one batch as the error clearly indicates.

declaring T-Sql parameter for comma delimited list of integers

I have a table that contains a list of performances. These performances are grouped by production number. What I am trying to do is create a stored procedure that will return the last performance for each production entered. I would like to be able to input the production ids as a list of ids. Below is my procedure so far. Difficulty is I'm not sure how best to declare the #prod_no parameter to be used in the IN statement.
CREATE PROCEDURE IP_MAX_PERF_DATE
-- Add the parameters for the stored procedure here
#prod_no
AS
BEGIN
-- SET NOCOUNT ON added to prevent extra result sets from
-- interfering with SELECT statements.
SET NOCOUNT ON;
-- Insert statements for procedure here
SELECT [prod_season_no], MAX([perf_dt]) As max_dt FROM [T_PERF] WHERE [prod_season_no] IN (#prod)
GROUP By [prod_season_no];
END
GO
Any ideas
Try the sp_executesql
CREATE PROCEDURE IP_MAX_PERF_DATE
#prod_no nvarchar(500)
AS
BEGIN
SET NOCOUNT ON;
declare #statement nvarchar(1000)
set #statement = N'SELECT [prod_season_no], MAX([perf_dt]) As max_dt FROM [T_PERF] WHERE [prod_season_no] IN (' + #prod_no + ') GROUP By [prod_season_no]'
EXEC sp_executesql
#stmt = #statement
END
GO
generally there are three ways to pass in a list of Ids:
Option 1: use comma separated list and split it in the stored procedure. this requires you to have a split function, or use dynamic sql (not preferred most of the time due to performance problem - at least hard to see the execution plan and you lose the point of using stored procedure to optimize your query)
Option 2: use xml, and again, you need to query the xml to find out the Ids
Option 3: use table valued parameter, this requires you to have a user defined table type
a detailed comparison could be found here:
http://www.adathedev.co.uk/2010/02/sql-server-2008-table-valued-parameters.html
This is what I've always done for passing in comma sepearted Integer IDs.
ALTER FUNCTION [dbo].[SplitArray]
(
#List varchar(500)
)
RETURNS
#ArrayValues table
(
ListID int
)
AS
BEGIN
DECLARE #ListID varchar(10), #Pos int
SET #List = LTRIM(RTRIM(#List))+ ','
SET #Pos = CHARINDEX(',', #List, 1)
IF REPLACE(#List, ',', '') <> ''
BEGIN
WHILE #Pos > 0
BEGIN
SET #ListID = LTRIM(RTRIM(LEFT(#List, #Pos - 1)))
IF #ListID <> ''
BEGIN
INSERT INTO #ArrayValues (ListID)
VALUES (CAST(#ListID AS int)) --Use Appropriate conversion
END
SET #List = RIGHT(#List, LEN(#List) - #Pos)
SET #Pos = CHARINDEX(',', #List, 1)
END
END
RETURN
END
To use it, simply join it on your query like so:
Select a.* From Apples a Inner Join dbo.SplitArray(#IDList) array on a.AppleID = array.ListID

Sql string manipulation within stored procedure

I have a field which contains article titles. I need to create friendly or pretty url's out of the article titles.
I need help manipulating the string with SQL. This has to be done within a stored procedure or function.
The requirements:
The only characters allowed are lowercase letters and numbers (a-z and 0-9)
All spaces need to be replaced with dashes.
Thanks!
(updated) I am using Sql Server 2008
I found the answer over here. Thank you all!
How to strip all non-alphabetic characters from string in SQL Server?
CREATE Function [dbo].[create_pretty_url](#Temp VarChar(1000))
Returns VarChar(1000)
AS
Begin
While PatIndex('%[^A-za-z0-9]%', #Temp) > 0
Set #Temp = LOWER(Stuff(#Temp, PatIndex('%[^A-za-z0-9]%', #Temp), 1, ''))
Return #Temp
End
To check for lowercase letters, you can use a binary collation like Latin1_General_BIN.
This SQL Server procedure checks if a string contains only spaces, digits or lowercase letters. If so, it returns 1 and replaces spaces with underscores. Else it returns -1.
if OBJECT_ID('TestProc') is null
exec ('create procedure TestProc as select 1')
go
alter procedure TestProc(
#str varchar(256),
#result varchar(256) output)
as
begin
set #result = null
set #str = REPLACE(#str,' ','_')
if #str like '%[^0-9a-z_]%' collate Latin1_General_BIN
return -1
set #result = #str
return 1
end
go
Test data:
declare #rc int
declare #result varchar(256)
exec #rc = TestProc '11 aa', #result out
select #rc, #result
exec #rc = TestProc 'NO CAPS', #result out
select #rc, #result
exec #rc = TestProc '%#$#$', #result out
select #rc, #result
-->
1 11_aa
-1 NULL
-1 NULL
You did not state which database, or version for that matter, but lets go with:
I you were to be using Sql Server 2005, 2008, have a look at using CLR functions
Adding Regular Expressions (Regex) to SQL Server 2005

T-SQL: Concept similar to C# params

Does T-SQL allow a variable number of arguments to a stored procedure like params in C#?
EDIT: I'm using SQL Server 2005. That 2008 answer makes me wish we were using it...
In SQL 2008 there's Table-Valued Parameters (TVPs)
Your stored proc can accept lists of parameters..
Finally we're able to do a IN clause without relying on XML!
Mike
No, not for things like UDFs or stored procedures. That's what tables are for. Put the values in a table somewhere (with a common key) and pass the correct key to your procedure.
Typically
CREATE PROCEDURE dbo.sptest
( #xml TEXT )
AS
BEGIN
DECLARE #flag1 INT
DECLARE #flag2 VARCHAR(50)
DECLARE #flag3 DATETIME
DECLARE #idoc INT
exec sp_xml_preparedocument #idoc OUTPUT, #xml
SELECT #flag1 = firstparam, flag2 = secondparam, flag3 = thirdparam
FROM OPENXML(#idoc, '/root', 2) WITH
( firstparam INT, secondparam VARCHAR(50), thirdparam DATETIME) as x
END
exec sptest '<root><firstparam>5</firstparam><secondparam>Joes Bar</secondparam><thirdparam>12/30/2010</thirdparam></root>'
Extend as necessary
Another approach I've seen to passing in params or arrays is to pass in an XML string, dump that to a temporary table/table variable and work with it from that point. Not the easiest when you want to manually run a stored procedure, but it works as a work around to the lack of array/dynamic param support.
I've used a little function to separate a CSV string into a table
That way I could go
SELECT col1, col2
FROM myTable
WHERE myTable.ID IN (SELECT ID FROM dbo.SplitIDs('1,2,3,4,5...'))
My function is below:
CREATE FUNCTION [dbo].[SplitIDs]
(
#IDList varchar(500)
)
RETURNS
#ParsedList table
(
ID int
)
AS
BEGIN
DECLARE #ID varchar(10), #Pos int
SET #IDList = LTRIM(RTRIM(#IDList))+ ','
SET #Pos = CHARINDEX(',', #IDList, 1)
IF REPLACE(#IDList, ',', '') <> ''
BEGIN
WHILE #Pos > 0
BEGIN
SET #ID = LTRIM(RTRIM(LEFT(#IDList, #Pos - 1)))
IF #ID <> ''
BEGIN
INSERT INTO #ParsedList (ID)
VALUES (CAST(#ID AS int)) --Use Appropriate conversion
END
SET #IDList = RIGHT(#IDList, LEN(#IDList) - #Pos)
SET #Pos = CHARINDEX(',', #IDList, 1)
END
END
RETURN
END
I'm sure there are better ways to implement this, this is one way I found online and it works well for what I'm doing. If there are some improvement that can be made please comment.