A Split Function to Extract the String 'go' - sql

I have a table-valued function called Split. The function takes 2 strings. It will split the 1st string into rows based on the value of the 2nd string.
I want to the function to split sql on the 'go' statements. The problem is that it will split the sql string when it finds the string 'go' anywhere in the sql. I need it to split on the string 'go' only when it is on a line all by itself. Any ideas? I am hoping not to have to re-write the function but to modify it in some (hopefully simple) way.
IF EXISTS (SELECT * FROM sys.objects WHERE
type = 'TF' AND name = 'Split')
BEGIN
DROP FUNCTION [dbo].[Split]
END
GO
CREATE FUNCTION dbo.Split
(
#RowData nvarchar(MAX),
#SplitOn nvarchar(50)
)
RETURNS #RtnValue table
(
Id int identity(1,1),
Data nvarchar(MAX)
)
AS
BEGIN
Declare #Cnt int
DECLARE #tst varchar(MAX)
Set #Cnt = 1
While (Charindex(#SplitOn,#RowData)>0)
Begin
Select
#tst = ltrim(rtrim(Substring(#RowData,1,Charindex(#SplitOn,#RowData)-1)));
Insert Into #RtnValue (data)
Select
Data = ltrim(rtrim(Substring(#RowData,1,Charindex(#SplitOn,#RowData)-1)))
Set #RowData = Substring(#RowData,Charindex(#SplitOn,#RowData)+2,len(#RowData))
Set #Cnt = #Cnt + 1
End
Insert Into #RtnValue (data)
Select Data = ltrim(rtrim(#RowData))
Return
END
GO
-- test out the function
SELECT data
FROM dbo.Split('
begin transaction;
go
alter table activity_log add
hcm_got_estimate_num char(16) default (NULL);
go
set ANSI_NULLS on;
go
commit;
go'
, 'go');

The idea is to add carriage return to the 'go' in "split-on" parameter. Unfortunately split doesn't work very well on such combination (some artifacts remain). So in order not to mess with the Split function you can prep the text by replacing carriage return + 'go' with some special character and then do the split on that character:
SELECT data
FROM dbo.Split(REPLACE('
begin transaction;
go
alter table activity_log add
hcm_got_estimate_num char(16) default (NULL);
go
set ANSI_NULLS on;
go
commit;
go',char(13) + char(10) + 'go', '¬')
, '¬');

Related

How to fetch the code comments from a stored procedure / function and populate to a table?

How to fetch the code comments from a stored procedure / function and populate to a table?
/*
Author : Test
Comment : Test
*/
I am working on a user defined function by passing either a stored procedure or function as input parameter to read the code history comments and store it in a table. Having the detail in a table to maintain the version notes for the input.
Check this, there are different ways to get the definition, I prefer sp_helptext because it's already splitted in lines
DECLARE #Objects TABLE(name varchar(100))
DECLARE #Lines TABLE(id int identity, line varchar(maX))
INSERT #Objects
SELECT name FROM sys.objects WHERE Type in ('FN', 'IF', 'P', 'TR', 'TF')
DECLARE #ObjectName VARCHAR(100)
WHILE EXISTS (SELECT 1 FROM #Objects)
BEGIN
SELECT TOP 1 #ObjectName = name FROM #Objects
DELETE #Lines
INSERT #Lines (line)
exec sp_helptext #ObjectName
DECLARE #Linestart INT, #LineEnd INT
WHILE EXISTS(SELECT 1 FROM #Lines WHERE charindex('/*', line) > 0)
BEGIN
SELECT TOP 1 #Linestart = id
FROM #Lines WHERE charindex('/*', line) > 0
ORDER BY id
SELECT TOP 1 #LineEnd = id
FROM #Lines WHERE charindex('*/', line) > 0
ORDER BY id
DECLARE #comment VARCHAR(MAX) = ''
SELECT #Coment = #coment + char(13) + char(10) + line
FROM #Lines
WHERE id between #LineStart and #lineEnd
INSERT INTO yourtable (#objectName, #Comment)
DELETE #Lines WHERE id between #LineStart and #lineEnd
END
DELETE #Objects WHERE name = #ObjectName
END
You can create a function/stored procedure to achieve this:
CREATE FUNCTION InsertCommentIntoTable
(
#Param1 VARCHAR(200)
)
RETURNS int
AS
BEGIN
-- Declare the return variable here
DECLARE #str VARCHAR(max)
SELECT #str = definition
FROM sys.sql_modules
WHERE object_id = (OBJECT_ID(N'dbo.CustOrderHist'));
--parse #str string value and do your stuffs: #str has the function and stored procedure codes.
RETURN 0;
END
GO

Incompatible object type when create and alter a table value function in SQL

I'm getting the below error for the given function.
Msg 2010, Level 16, State 1, Procedure GetTableFromDelimitedValues, Line 2
Cannot perform alter on 'dbo.GetTableFromDelimitedValues' because it is an incompatible object type.
IF NOT EXISTS(SELECT 1 FROM sys.objects
WHERE object_id = OBJECT_ID('[GetTableFromDelimitedValues]'))
BEGIN
EXECUTE('CREATE FUNCTION [dbo].[GetTableFromDelimitedValues](#input varchar(max),
#delimiter char(1) = ",")) RETURNS #Result TABLE (
Value nvarchar(4000)) AS BEGIN RETURN END')
END
GO
ALTER FUNCTION [dbo].[GetTableFromDelimitedValues](
#input varchar(max),
#delimiter char(1) = ',')
RETURNS #Result TABLE
(
Value nvarchar(4000)
)
AS
BEGIN
DECLARE #position int;
DECLARE #column nvarchar(4000);
WHILE LEN(#input) > 0
BEGIN
SET #position = CHARINDEX(#delimiter, #input);
IF (#position < 0) OR (#position IS NULL)
BEGIN
SET #position = 0;
END
IF #position > 0
BEGIN
SET #column = SUBSTRING(#input, 1, #position - 1);
SET #input = SUBSTRING(#input, #position + 1, LEN(#input) - #position)
END
ELSE
BEGIN
SET #column = #input;
SET #input = '';
END
INSERT #Result (Value)
SELECT #column;
END;
RETURN;
END
GO
Can someone please help me to get the compatible type by fixing the function?
You need to DROP and CREATE the function in this particular context
Since there is change in function return type, we must drop then recreate the function.
There are three types of functions,
Scalar
Inline table valued and
Multi Statement
ALTER cannot be used to change the function type.
IF EXISTS (SELECT [name] FROM sys.objects
WHERE object_id = OBJECT_ID('GetTableFromDelimitedValues'))
BEGIN
DROP FUNCTION [GetTableFromDelimitedValues];
END
GO
/* Now create function */
CREATE FUNCTION [dbo].[GetTableFromDelimitedValues](
#input varchar(max),
#delimiter char(1) = ',')
RETURNS #Result TABLE (
Value nvarchar(4000)
)
AS
BEGIN
..
..
..
RETURN;
END
in OBJECT_ID function you need to pass only function name not the schema. and why would create it 1st and then Alter it . Just check for existence 1st if it exists then drop function and create your function as I have shown above.
Also do not add Type in where clause when checking for existence, if there is another object not a function but any other object with the same name, it will not pick it up in your select statement and you will end up creating a function with a name an object already exists (this will throw an error).
IF you want to do it your way this is how you would go about it
IF NOT EXISTS(SELECT 1 FROM sys.objects
WHERE object_id = OBJECT_ID('[GetTableFromDelimitedValues]'))
BEGIN
EXECUTE('CREATE FUNCTION [dbo].[GetTableFromDelimitedValues]() RETURNS #Result TABLE (
Value nvarchar(4000)) AS BEGIN RETURN END')
END
GO
In my case, this happened when I have a table name exactly as proc name. so making a change to proc name or a table referred in the proc should also fix this error message.
I have something to notify on your error related to your code:
The error says Cannot perform alter on 'dbo.GetTableFromDelimitedValues' because it is an incompatible object type
Which means that you have to look on your lines after the ALTER....
And yes there t is:
#input varchar(max)
The SQL server 2008 r2 not accept objects varchar(MAX), but that is only if you run a stored procedure
Because if you create a table by hand then it is fully accept it.
If you want a large cell then type varchar(1024) or varchar(2048) both of them are accepted. I face this issue few days ago...
That is my humble opinion
ADDITIONAL CHANGES
Use this
IF NOT EXISTS(SELECT 1 FROM sys.objects
WHERE object_id = OBJECT_ID('[GetTableFromDelimitedValues]'))
BEGIN
execute('CREATE FUNCTION [dbo].[GetTableFromDelimitedValues]( #input varchar(max), #delimiter char(1)= ",") RETURNS #Result TABLE ( Value nvarchar(4000)) AS BEGIN RETURN END')
END GO
.... Pay attention to the change from ' to the "
** ADDITIONAL CHANGES **
I use the following which also works fine... with no any issue...
IF EXISTS (SELECT [name] FROM sys.objects
WHERE object_id = OBJECT_ID('GetTableFromDelimitedValues'))
BEGIN
DROP FUNCTION [GetTableFromDelimitedValues];
END
BEGIN
execute('CREATE FUNCTION [dbo].[GetTableFromDelimitedValues]()
RETURNS
#Result TABLE (
Value nvarchar(4000))
AS
BEGIN
RETURN
END')
execute('ALTER FUNCTION [dbo].[GetTableFromDelimitedValues](
#input varchar(max),
#delimiter char(1) = ",")
RETURNS #Result TABLE (
Value nvarchar(4000))
AS
BEGIN
RETURN
END')
END
GO
I confirm the below code works. Seems the issue was somehow a scalar value function created with the same name during my development and got error as script's multi part table value alter statement function is compatible with it.
IF NOT EXISTS(SELECT 1 FROM sys.objects
WHERE object_id = OBJECT_ID('[GetTableFromDelimitedValues]'))
BEGIN
EXEC sp_executesql
#statement = N'CREATE FUNCTION dbo.[GetTableFromDelimitedValues] () RETURNS #Result
TABLE(Value nvarchar(4000))
AS
BEGIN
RETURN
END' ;
END
GO
ALTER FUNCTION [dbo].[GetTableFromDelimitedValues](
#input varchar(max),
#delimiter char(1) = ',')
RETURNS #Result TABLE
(
Value nvarchar(4000)
)
AS
BEGIN
DECLARE #position int;
DECLARE #column nvarchar(4000);
WHILE LEN(#input) > 0
BEGIN
SET #position = CHARINDEX(#delimiter, #input);
IF (#position < 0) OR (#position IS NULL)
BEGIN
SET #position = 0;
END
IF #position > 0
BEGIN
SET #column = SUBSTRING(#input, 1, #position - 1);
SET #input = SUBSTRING(#input, #position + 1, LEN(#input) - #position)
END
ELSE
BEGIN
SET #column = #input;
SET #input = '';
END
INSERT #Result (Value)
SELECT #column;
END;
RETURN;
END
GO
bug does function created and data return is not field define, just change after the table(add field) returns.
solution fix bug:
deleted function just
edit key word "Alter" => "Create"
F5 is created function is success

Building dynamic T-SQL query from a string argument in a sproc

Let's say I have a table which contains a varchar field:
CREATE TABLE [MyTable] (
[MyId] varchar(3) NOT NULL,
.....
)
The [MyId] column contains sequential alphanum values like A1, A2... A99, B1, B2..B99, C1 and so on (up to Z99).
What I'd like to do is to extract rows from the table whose MyId field matches some specific prefixes... e.g. I'd like to fetch rows from the series A, C, P and X.
And I'd like to this with a sproc which will dynamically construct the query based on the prefix alphabets supplied in the argument.
I'm thinking about something like this...
CREATE PROCEDURE [dbo].[uspFilterMyTable]
#prefixArray varchar(max)
AS
... -- split individual characters from #prefixArray into an array
SELECT * FROM [MyTable]
WHERE
[MyId] LIKE ....
OR
[MyId] LIKE .... -- iterate all characters from #prefixArray
I think the main bulk of the stored procedure will resemble the following pseudo-code:
DECLARE #sql nvarchar(max)
-- iterate through all the characters
SET #sql = 'SELECT * FROM [MyTable] WHERE [MyId] LIKE ' + #charInTheArray + '%'
SET #sql = #sql + ' OR [MyId] LIKE ' + #nextCharInArray + '%'
EXEC (#sql)
The above proecedure will be called like this:
EXEC uspFilterMyTable("A,C,P,X")
... or perhaps like this (if it makes splitting the alphabets easier):
EXEC uspFilterMyTable("ACPX")
Any ideas? Pointers?
Update: OK, this is what I've come up with ([Split] function borrowed from Chhatrapati Sharma):
-- [MyTable] contains these rows: 'A7', 'A87', 'B16', 'C51', 'H99', 'X12'
-- the "input" parameter
DECLARE #prefixArray NVARCHAR(100)= 'H,A,C'
-- split the string into SQL wild-card patterns
DECLARE charCursor CURSOR FOR
select items + N'%' from dbo.Split(#prefixArray, ',')
OPEN charCursor;
DECLARE #pattern CHAR(2)
-- create temp table if necessary
IF NOT EXISTS(SELECT * FROM TEMPDB.SYS.TABLES WHERE NAME LIKE '#tmpTable%')
CREATE TABLE #tmpTable ([Id] VARCHAR(3) NOT NULL)
-- purge old data
DELETE FROM #tmpTable
FETCH NEXT FROM charCursor into #pattern
WHILE ##FETCH_STATUS = 0
BEGIN
--SELECT * INTO #tmpTable FROM [MyTable] WHERE [MyId] LIKE #pattern
Insert Into #tmpTable Select * FROM [MyTable] WHERE [MyId] LIKE #pattern
FETCH NEXT FROM charCursor into #pattern
END
CLOSE charCursor;
DEALLOCATE charCursor;
-- return the values
SELECT * FROM #tmpTable
It's ugly I know, but it works... any tips to improvise the code?
first you should create below function and then use this in query like this
SELECT * FROM [MyTable] WHERE [MyId] in (select items from dbo.split(#prefixArray,','))
CREATE FUNCTION [dbo].[Split](#String varchar(8000), #Delimiter char(1))
returns #temptable TABLE (items varchar(8000))
as
begin
declare #idx int
declare #slice varchar(8000)
select #idx = 1
if len(#String)<1 or #String is null return
while #idx!= 0
begin
set #idx = charindex(#Delimiter,#String)
if #idx!=0
set #slice = left(#String,#idx - 1)
else
set #slice = #String
if(len(#slice)>0)
insert into #temptable(Items) values(#slice)
set #String = right(#String,len(#String) - #idx)
if len(#String) = 0 break
end
return
end
Here you have a nice and fast split method based on XML:
DECLARE #str NVARCHAR(100)= 'A1,B3,C4,B12,K19', #separator VARCHAR(1)= ','
DECLARE #SplitedList TABLE (code NVARCHAR(30))
DECLARE #XMLList XML
SET #XMLList=CAST('<i>'+REPLACE(#str, #separator,'</i><i>')+'</i>' AS XML)
INSERT INTO #SplitedList
SELECT x.i.value('(./text())[1]','varchar(100)')
FROM #XMLList.nodes('i') x(i)
SELECT * FROM #SplitedList
Result will be a table with the splitted values:
code
A1
B3
C4
B12
K19
From here you can continue and use this table on your procedure and join with you original table using LIKE as you propossed.
I would have suggested you to use table valued parameters to call your stored procedure. I guess you call it from .net. But EF I think will not be able to handle it, though you might check it. If not, I think the best way is to first parse the string into a temporary table, or a table value and after that join with it.
With TVP:
CREATE PROCEDURE [dbo].[uspFilterMyTable]
#prefixArray tvp_idlist readonly
as
select
t.*
from MyTable t
join #prefixArray pa on pa.id = t.myid
With a split function (of your choosing, you find many examples on the net)
CREATE PROCEDURE [dbo].[uspFilterMyTable]
#prefixArray varchar(max)
as
create #prefixArray tvp_idlist
insert into #prefixArray (id)
select id from dbo.myCustomSplit(#prefixArray,',')
select
t.*
from MyTable t
join #prefixArray pa on pa.id = t.myid
Where for both cases #prefixArray is a table variable is Id = varchar(3)
As an edit, after a little digging, it seems that with a little work EF works fine with TVPs. Check this : Entity Framework Stored Procedure Table Value Parameter. So The best thing is to send directly a table to your stored procedure, then to send a string to parse.

stored proc - executing a query with NOT IN where clause

i have a stored procedure
Create PROCEDURE abc
#sRemovePreviouslySelectedWhereClause nvarchar(max)
AS
BEGIN
SELECT *
FROM table
WHERE nId NOT IN (#sRemovePreviouslySelectedWhereClause)
END;
The parameter #sRemovePreviouslySelectedWhereClause can have values like 0,1 . But this fails with error message:
Conversion failed when converting the nvarchar value ' 0,1 ' to data type int.
Is there any other way to achieve this other than dynamic queries?
First, create a split function which splits your delimited string into a table:
CREATE FUNCTION [dbo].[Split]
(
#String varchar(max)
,#Delimiter char
)
RETURNS #Results table
(
Ordinal int
,StringValue varchar(max)
)
as
begin
set #String = isnull(#String,'')
set #Delimiter = isnull(#Delimiter,'')
declare
#TempString varchar(max) = #String
,#Ordinal int = 0
,#CharIndex int = 0
set #CharIndex = charindex(#Delimiter, #TempString)
while #CharIndex != 0 begin
set #Ordinal += 1
insert #Results values
(
#Ordinal
,substring(#TempString, 0, #CharIndex)
)
set #TempString = substring(#TempString, #CharIndex + 1, len(#TempString) - #CharIndex)
set #CharIndex = charindex(#Delimiter, #TempString)
end
if #TempString != '' begin
set #Ordinal += 1
insert #Results values
(
#Ordinal
,#TempString
)
end
return
end
Then change your where clause as follows:
select
t.*
from [yourTable] t
where t.[ID] not in (select cast([StringValue] as int) from dbo.Split(#sRemovePreviouslySelectedWhereClause,','))
Create FUNCTION [dbo].[fn_Split] (
#List nvarchar(2000), #SplitOn nvarchar(5)
)
RETURNS #RtnValue table (
Value nvarchar(100) )
AS
BEGIN
While (Charindex(#SplitOn,#List)>0)
Begin
Insert Into #RtnValue (value)
Select Value = ltrim(rtrim(Substring(#List,1,Charindex(#SplitOn,#List)-1)))
Set #List = Substring(#List,Charindex(#SplitOn,#List)+len(#SplitOn),len(#List))
End
Insert Into #RtnValue (Value)
Select Value = ltrim(rtrim(#List))
Return
END
**********
Create PROCEDURE abc
#sRemovePreviouslySelectedWhereClause nvarchar(max)
AS
BEGIN
SELECT *
FROM Table
WHERE nId NOT IN (select * from dbo.fn_Split(#sRemovePreviouslySelectedWhereClause,','))
END;
You have to split the #sRemovePreviouslySelectedWhereClause parameter by ',' and place the resulting values in a temp table. Then your select would look like
select * from table where nId not in (select nId from #tempIds)
This approach you're trying doesn't work. But if you're on SQL Server 2008, you could make use of the new features called Table Valued Parameters.
Basically, you create a user-defined table type
CREATE TYPE dbo.nIdTable AS TABLE(nID INT)
and you can then pass in multiple values in that TVP from the outside (e.g. from ADO.NET or such):
CREATE PROCEDURE abc(#idValues dbo.nIdTable READONLY)
and use that table variable inside your stored proc:
SELECT *
FROM table
WHERE nId NOT IN (SELECT nID FROM #idValues)
You will need to use Dynamic sql for such kind of queries.
first construct the query and
SET #sql = 'select * from table
where nId not in (' + #sRemovePreviouslySelectedWhereClause+ ')'
then use EXEC(#sql) to run the query.

break and return #temp_catid if(true)

i have a following sql function which takes a string word as input it then checks whether the word is equal to a categoryName or not i want it to behave like when the if statement is true it breaks from the loop and return #temp_catid else it returns 0 or 1 how can i do that im new to sql scripting plz help... below is my function
USE [myDB]
GO
/****** Object: UserDefinedFunction [dbo].[udfisEqualToCategory] Script Date: 01/31/2011 10:57:56 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
Create FUNCTION [dbo].[udfisEqualToCategory](#word nvarchar(max))
RETURNS INT
AS
BEGIN
--declare a temp category table for data processing
declare #temptbl_category Table
(
indexx int identity(1,1),
categoryid int,
categoryname nvarchar(max)
)
--insert data from feedcrawler.category into temp category table
insert into #temptbl_category
select CategoryId,Name
from Category
--declare some variables to hold the data of current row of temp category table being processed while looping
declare #temp_catid int
declare #temp_catname nvarchar(max)
declare #rowcount int
set #rowcount=(select count(indexx)from #temptbl_category)
declare #I int
set #I=1
--print'given string-> '+ #FullName
--print'string length-> '+convert(nvarchar(max),#strlen)
while(#I <= #rowcount)
begin
select #temp_catname=categoryname,#temp_catid=categoryid from #temptbl_category where indexx=#I
set #temp_catname=lower(#temp_catname)
if(#word=#temp_catname)
begin
return #temp_catid
break
end--end if
set #I=#I+1
END--while loop ends
return 0
end-- function ends
GO
No need to loop, just search the table with WHERE
Create FUNCTION [dbo].[udfisEqualToCategory](#word nvarchar(max))
RETURNS INT
AS
BEGIN
declare #temp_catid int
select
#temp_catid = categoryid
from
Category
WHERE
LOWER(#word) = LOWER(categoryname);
--no match = #temp_catid IS NULL. So change to zero
RETURN ISNULL(#temp_catid, 0);
END
GO