Stored Procedure Parser - c#

I am trying to parse through hundreds of stored procedures to specifically grab their output variables "#FirstName", which tables they use, and which fields they pull from "MyTbl.FirstName". I am able to collect the variables pretty easily but I'm having trouble collecting the table names. Could anyone help?
So far I've been able to pull most of these fields by parsing through the SQL files using the StreamReader and collecting information line by line, for example if a line contains output, then I know the first text in the line is most likely the #Variable.
#Address1 varchar(45) output,
#Address2 varchar(45) output,
#City varchar(35) output,
#State varchar(2) output,
#Zip varchar(10) output
From there I can store the #Variable into a dictionary and if any line contains the #Variable and also contains a '=' then I know we have a match as to which field it corresponds to.
#Address1 = c.Address,
#Address2 = c.AddressSecondLine,
#City = c.City,
#State = c.State,
#Zip = c.ZipOrPostalCode
Now I'm just having issues gathering the table name. I can easily parse the table alias off the field name but I'm having issues matching the alias with a table name. Does anyone know of a good way to do this? Here's what I've been trying so far:
FROM Table.dbo.SalesStuff ss
LEFT OUTER JOIN Table.dbo.Customer c ON ss.CustNo = c.CustNo
Left JOIN Table.dbo.Vending v on #tmpVin = v.vin
Code:
keyColl = tables.Keys;
foreach (string var in keyColl)
{
if (line.Contains(" " + var + '\r') || line.Contains(" " + var + " ") || line.Contains(" " + var + ((char)13)) || line.Contains(" " + var + Environment.NewLine))
{
tables[var] = line.ToString();
break;
}
}
I thought that this would match the table alias since most aliases are a letter, followed by a line break, but so far I haven't been able to get any of the table names... Does anyone have an idea?

Quite frankly I don't think you're going to get very far with your parsing idea. You're making very brave assumptions about how code is going to be formatted in every single procedure. I'm very meticulous about formatting but there's no way I could guarantee the kind of consistency you're depending on across that many procedures, even if I did write them all myself.
With the caveat that deferred name resolution can bite you in the rear and that dependency tracking was certainly far from perfect in SQL Server 2005 (see the workarounds I posted for keeping it accurate even in SQL Server 2008), here are a couple of ideas (and they're not perfect either, but they'll definitely cause less gray hair):
You can get parameters in a much easier way than brute force parsing, by using the catalog view sys.parameters:
SELECT OBJECT_NAME([object_id]), p.name, t.name
FROM sys.parameters AS p
INNER JOIN sys.types AS t
ON p.system_type_id = t.system_type_id
WHERE p.is_output = 1;
If all of your procedures have been recompiled and you are not subject to deferred name resolution issues, you can get table names and column names from sys.sql_dependencies - however this will include columns that are referenced in where/join clauses even if they are not in the select list:
SELECT [procedure] = OBJECT_NAME(d.[object_id]),
[table] = OBJECT_NAME(d.referenced_major_id),
[column] = c.name
FROM sys.sql_dependencies AS d
INNER JOIN sys.columns AS c
ON c.[object_id] = d.referenced_major_id
AND c.column_id = d.referenced_minor_id;
There is a column here called is_selected but I have not found it to be accurate/reliable.
Note that anything that happens in dynamic SQL stays in dynamic SQL - so if your procedures use dynamic SQL it will be next to impossible to cull out table/column names.

you can use regular expressions. For example for string like
FROM Table.dbo.SalesStuff ss
you can use
string pattern = #"\s*FROM\s+Table\.dbo\.(\w+)\s+(\w+)";
string input = "line from stored proc body here";
MatchCollection matches = Regex.Matches(input, pattern);
foreach (Match match in matches)
{
Console.WriteLine("table name: {0}", match.Groups[1].Value);
Console.WriteLine("Alias: {0}", match.Groups[2].Value);
Console.WriteLine();
}
you must define pattern for each type of string containing table name and alias.

Related

SQL Server Show Results when Record not found

I am trying to get SQL to list not found when a machine name lookup is not in the database. I have the query working but I need to integrate it into C# where I have other queries. As it stands it is a lot more convoluted than I want it to be, I am guessing there is a much more intelligent and concise why of having the SQL achieve the same result.
DECLARE #myHostCount as INT
DECLARE #myHostName1 as Varchar(50);
DECLARE #myHostName2 as Varchar(50) = 'Machine220054';
DECLARE #myHostCount1 as INT
DECLARE #myHostName3 as Varchar(50);
DECLARE #myHostName4 as Varchar(50) = 'Machine22054';
SET #myHostCount1 = (SELECT COUNT(*) FROM db_owner.host WHERE name LIKE '%Machine22054%')
SET #myHostName3 = (SELECT Name FROM db_owner.host WHERE name LIKE '%Machine22054%')
SET #myHostCount = (SELECT COUNT(*) FROM db_owner.host WHERE name LIKE '%Machine220054%')
SET #myHostName1 = (SELECT Name FROM db_owner.host WHERE name LIKE '%Machine220054%')
SELECT
CASE
WHEN #myHostCount = 1 THEN 'Found'
WHEN #myHostCount = 0 THEN 'Not Found'
END AS 'Result',
#myHostName2 AS 'HostName'
UNION
SELECT
CASE
WHEN #myHostCount1 = 1 THEN 'Found'
WHEN #myHostCount1 = 0 THEN 'Not Found'
END AS 'Result',
#myHostName4 AS 'HostName'
Output:
Result Machine Name
--------------------------
Found Machine220054
Not Found Machine22054
You can try this:
DECLARE #myHostName2 as Varchar(50) = 'Machine220054';
DECLARE #myHostName4 as Varchar(50) = 'Machine22054';
DECLARE #hostName TABLE (
Name VARCHAR(255)
);
INSERT INTO #hostName (Name)
VALUES (#myHostName2), (#myHostName4)
SELECT HostName = HostName.name,
Result = IIF(COUNT(*) = 0, 'Not Found', 'Found')
FROM #hostName AS HostName
LEFT JOIN db_owner.host ON host.name LIKE '%' + HostName.Name + '%'
GROUP BY HostName.name;
First: Try to remove as much procedural logic in SQL as possible.
Second: Try to minimize the use of "Like" queries if at all possible. It will hinder indexing and cause bottlenecks eventually, or, at least limit it to ending with a "%" instead of starting it with one.
Third: you are assuming that there will only be zero or one hit in your code. What if (given either non-unique or like based queries) there are more than one?
I'd go for something like this:
declare #looking table (name varchar(50))
insert into #looking (name) values ('Machine220054'),('Machine22054')
select case when t.name is null then 'Not Found' else 'Found' end as [FoundOrNot], t.name as [FoundName],l.name as [SearchName]
from #looking as l
left outer join db_owner.host as t
on t.name like '%' + l.name + '%'
UPDATE: Tested against a random table, removed "group by" on account of either one or no hit expected.
Just to provide an update, I ended up taking the results from the DR reader and entering them into a datatable, since the dr reader can only be read once. Then I compared the selections from the listbox with the contents from the datatable then added the difference to another datatable then displayed the results in a datagrid, this produced the desired outcome of results found, being listed and the objects not found being listed as not found.

Making a WHERE function that only runs if value is not empty/null

So i'm currently making somewith with C# and SQL on visual studios 2013 and want to make a form where there will be several text boxes for entering in the search criteria for each field/column to help find records in a database faster.
The user will be able to enter in values into as many fields/boxes as they want. However, i'm unsure of how I would write the WHERE statement for it.
I know that if I do WHERE FirstName = '' it will show up nothing, meaning I must append the WHEREs on.
I assume it would not be a good idea to have something like
public void QueryName(string FN, string LN,etc,etc)
"SELECT * FROM Table WHERE "
IF(FirstName != "") THEN
+ "FirstName = 'FN'" + IF(LastName != "") THEN
+ "LastName = 'LN'"
as it would break (i assume) if it had a WHERE followed by nothing, and I can't start off each append part off as "WHERE" as the user decides what values it searches by.
So what i'm looking for is a way to make an SQL statement in C# using multiple values of the users choice, without it being longer and more complicated than it needs to be.
If i'm confusing anyone, I'll do my best to explain.
You can do something like this:
SELECT * FROM Table WHERE (FirstName = '' OR FirstName = #FirstName) AND (LastName = '' OR LastName = #LastName
This approach can cause some performance issues.
I prefer build the sql query using C# to identify the filters that the user wants to apply, like that:
var sql = "SELECT * FROM Table WHERE 1 = 1 /* Just to not care about if the where clause is needed */";
if (!String.IsNullOrWhiteSpace(FirstName))
{
sql += " AND FirstName = #FirstName ";
}
if (!String.IsNullOrWhiteSpace(LastName))
{
sql += " AND LastName= #LastName";
}
You can use a query like this:
select * from YourTable a
where (isnull(#FirstName, '') = '' or a.FirtName = #FirstName) and
(isnull(#LastName, '') = '' or a.LastName = #LastName);
This code isnull(#FirstName, '') <> '' will check if the value provided is null, then make it empty, otherwise leave it alone. Then checks if it is empty. So if this condition passes, then it will never try to compare against the FirstName column.

What should I use instead of SQL "OR" statements

I have a gridview that can be filtered by 6 drop down boxes, so when writing the sql the easiest thing would be to use an 'or' statement if dropdown has selection or null etc.
However I have read on here and other sites that using sql or statements are a bad idea, can anyone offer any other suggestions i could use rather than me writing variations on whether each ddl selection is null? Below is an example of the first query, with every ddl returning a value
#ruleID int = null,
#engagementStatusID int = null,
#areaOfWorkID int = null,
#registered bit = null,
#staffGroupID int = null,
#assignmentTypeID int = NULL
AS
SET NOCOUNT ON
IF (#ruleID IS NOT NULL and #engagementStatusID IS NOT NULL and #areaOfWorkID IS NOT NULL and
#registered IS NOT NULL and #staffGroupID IS NOT NULL and #assignmentTypeID IS NOT NULL)
BEGIN
SELECT r.dbRuleId AS RuleID,r.dbEngagementStatusId AS EngagementStatusID,
r.dbIsAllStaffGroups AS AllStaffGroups,r.dbIsAllAssignments AS AllAssignments,
r.dbIsAllRegistered AS AllRegistered,r.dbIsAllUnregistered AS AllUnregistered,
r.dbSoftDelete AS Softdelete, es.dbName AS EngagementName,
sgc.dbName AS StaffGroupName, aow.dbName AS AreaOfWorkName,
at.dbName AS AssignmentName, at.dbIsRegistered AS Registered,sgc.dbStaffGroupCodeId AS StaffGroupCodeID,
at.dbAssignmentTypeId AS AssignmentID, aow.dbAreaOfWorkId AS AreaOfWorkID
FROM dbo.tbRule r INNER JOIN
dbo.EngagementStatus es ON r.dbEngagementStatusId = es.dbEngagementStatusId INNER JOIN
dbo.RuleStaffGroup rsg ON r.dbRuleId = rsg.dbRuleId INNER JOIN
dbo.StaffGroupCode sgc ON rsg.dbStaffGroupId = sgc.dbStaffGroupCodeId INNER JOIN
dbo.RuleAssignmentCode rac ON r.dbRuleId = rac.dbRuleId INNER JOIN
dbo.AssignmentCode ac ON
rac.dbAssignmentCodeId = ac.dbAssignmentCodeId INNER JOIN
dbo.AssignmentType at ON ac.dbAssignmentId = at.dbAssignmentTypeId INNER JOIN
dbo.AreaOfWork aow ON ac.dbAreaOfWorkId = aow.dbAreaOfWorkId
WHERE ((r.dbRuleId = #ruleID) and (r.dbEngagementStatusId = #engagementStatusID) and (aow.dbAreaOfWorkId = #areaOfWorkID) and
(at.dbIsRegistered = #registered) and (sgc.dbStaffGroupCodeId = #staffGroupID) and (at.dbAssignmentTypeId = #assignmentTypeID))
Any advice on this would be great
Update
I feel i should clarify something about my code, when i say null, this is the value i have assigned to the "all" selection of a drop down list, so for example imn most cases i do something like this to get the value that needs to be passed to the DB
int? Type = (this.ddlType.SelectedValue.ToString() == "All") ? (int?)null : Convert.ToInt32(this.ddlType.SelectedValue.ToString());
so if the user has selected all the Db recieves 'null' which i can then use on the 'if #blah IS NOT NULL' etc. I realise this is probably not the best way to do this
It seems you are executing this stored procedure and then validating the user input at the database level. You should not call the database stored procedure if the drop down list values are null, you can handle this at the client side (or server side).
Client side (JavaScript) would be better for the users experience, you can then invoke the stored procedure if the user has selected all appropriate drop down list values.
The problem comes when you do things like:
WHERE (r.dbRuleId = #ruleID or #ruleID is null)
and (r.dbEngagementStatusId = #engagementStatusID
or #engagementStatusID is null)
-- ... lots more
which quickly degrades to really bad query plans. The trick, then, is to have TSQL that matches your exact set of query parameters.
The hard to maintain way to fix this is to write DML for every single possibility, and branch into the correct one - but this is really ugly, and confuses a lot of tools.
The easiest way to do this is to build the TSQL appropriately at the caller - but if your system demands that you use a stored procedure (the benefits for which, these days, are dubious at best - btw), then the simplest choice is dynamic SQL. Obviously, you need to be careful here - you still don't want to concatenate inputs (for both injection and query-plan reasons), but - you can do something like:
declare #sql nvarchar(4000) = N'...start of query...';
if(#ruleID is not null)
set #sql = #sql + N' and r.dbRuleId = #ruleID';
if(#engagementStatusID is not null)
set #sql = #sql + N' and r.dbEngagementStatusId = #engagementStatusID';
You then need to execute that with sp_executesql, declaring the parameters:
exec 'sp_executesql', #sql,
N'#ruleID int, #engagementStatusID int',
#ruleID, #engagementStatusID
I'm not sure I understood your question, but if you're looking at avoiding repetition of OR operators, consider using IN('x','y','z') - listing the possible values. This would be easier to read than [something] = 'x' OR [something] = 'y' OR [something] = 'z'.

Differentiating between 2 SQL column names with the same name in a C# SqlConnection

I have joined the same 2 tables twice using different aliases in a SQL query to enable me to select 2 (potentially but not always) different address ids which then link in to the address table.
SELECT C.court_id, C.court_name, CA.court_address, CA2.court_address...
FROM court C " +
JOIN court_addr CA ON C.court_addr_id = CA.court_addr_id " +
JOIN court_addr CA2 ON C.court_postal_addr_id = CA2.court_addr_id " + ...
Now when trying to output the results of this query using ASP.NET C# I'm unsure how to specify which of the two addresses to Response.Write. Putting the alias in front of the column name (as in the 4th string value below) doesn't work and brings up an error. Is there a way of differentiating between the two addresses in C# despite them both having the same column name in the database?
while (myDataReader.Read())
{
string court_id = myDataReader["court_id"].ToString();
string court_name = myDataReader["court_name"].ToString();
string court_address = myDataReader["court_address"].ToString();
string court_postal_address = myDataReader["CA2.court_address"].ToString();
etc.....
Thanking you muchly in advance
You should use an alias in your sql to distinguish them, then you will be able to return the correct value:
SELECT C.court_id,
C.court_name,
CA.court_address as CACourtAddress,
CA2.court_address as CA2CourtAddress
FROM court C " +
JOIN court_addr CA ON C.court_addr_id = CA.court_addr_id " +
JOIN court_addr CA2 ON C.court_postal_addr_id = CA2.court_addr_id " + ...
You should use alias name to distinguish two columns having same name like :
SELECT C.court_id, C.court_name, CA.court_address CourtAddress, CA2.court_address CourtPostalAddress FROM court C
JOIN court_addr CA ON C.court_addr_id = CA.court_addr_id
JOIN court_addr CA2 ON C.court_postal_addr_id = CA2.court_addr_id
And then in C# you can access them very easily :
string court_id = myDataReader["court_id"].ToString();
string court_name = myDataReader["court_name"].ToString();
string court_address = myDataReader["CourtAddress"].ToString();
string court_postal_address = myDataReader["CourtPostalAddress"].ToString();
I hope this helps solve your problem :)
you could access the columns via index if you cannot modify the query.
var foo = MyDataReader[0].ToString();
Or you could modify the query using the AS keyword in your sql statement.
SELECT foo AS bar FROM Baz
In addition to providing a column name alias as bluefeet suggests, another way would be to access the values by index instead of by name in C#.
string theValue = myReader[0] as string ?? String.Empty;
This, however, requires the order of the columns to never change and is thus to be used carefully. Also, this only works as you specifically select the columns by name in the given order in your SQL statement.
This may not work for a SELECT *, as the order of the returned columns is not fixed in a SELECT *.

SQL Server 2005: dynamically adding parameters to a stored procedure

Scenario
I have a stored procedure that takes a single parameter. I want to update this stored procedure to take a VARIABLE NUMBER OF PARAMETERS - a number that I will never know.
I currently use SQLConnections through a C# interface in order to pass in a single parameter to the stored procedure and return a result.
The SQL Part
Lets say that I have a stored procedure that returns a list of results based on a single input parameter "#ccy" - (Currency). Now lets say that I want to update this stored procedure to take a list of Currencies instead of a single one, but that this number will be variable depending on the situation.
The SQL Code
ALTER PROCEDURE [dbo].[SEL_BootStrapperInstRICs]
(
#ccy varchar(10)
)
AS
SELECT DISTINCT i.CCY, i.Instrument, i.Tenor, r.RIC, r.[Server], r.RIType
FROM MDR.dbo.tblBootStrapperInstruments as i, MDR.dbo.tblBootStrapperRICs as r
WHERE i.Instrument = r.MurexInstrument
AND
i.Tenor = r.Tenor
AND i.CCY = r.CCY
AND i.CCY = #ccy
AND r.RIType NOT LIKE '%forward%'
The C# Part
This particular stored procedure is called from a C# WinForms application that uses the "SqlCommand.Parameters.AddWithValue()" method. As mentioned earlier this method currently passes in a single Currency as the parameter to the stored procedure and returns the result as a DataSet.
public DataSet GetBootStrapperInstRICsDS(List<string> ccys)
{
DataSet ds;
SqlConnection dbConn = null;
SqlCommand dbCmd = new SqlCommand();
try
{
dbConn = GetSQLConnection();
dbCmd = GetSqlCommand();
dbCmd.CommandType = CommandType.StoredProcedure;
dbCmd.CommandText = Utils.Instance.GetSetting ("SELBootStrapInsRics", "default");
foreach(string ccy in ccys)
dbCmd.Parameters.AddWithValue("#ccy", ccy);
dbCmd.CommandTimeout = 600;
dbCmd.Connection = dbConn;
SqlDataAdapter adapter = new SqlDataAdapter(dbCmd);
ds = new DataSet();
adapter.Fill(ds, "tblBootStrapperInstRICs");
dbCmd.Connection.Open();
return ds;
}
catch (Exception ex)
{
ApplicationException aex = new ApplicationException ("GetBootStrapperInstRICsDS", ex);
aex.Source = "Dal.GetBootStrapperInstRICsDS " + ex.Message;
MainForm.job.Log(aex.Source, Job.MessageType.Error);
Job.incurredErrors = true;
throw aex;
}
finally
{
if (dbCmd != null)
dbCmd.Dispose();
if (dbConn != null)
{
dbConn.Close();
dbConn.Dispose();
}
}
}
The Question
On the C# side I think my best option is to use a "foreach/for loop" in order to iterate through a list of parameters and dynamically add a new one to the SPROC. (I have already made this update in the C# code above).
HOWEVER - Is there some way that I can do this in the SQL Stored Procedure too? My thoughts are split with two potential options - Either create 20 or more parameters in the SPROC (each with the same name but with an incrementing number at the end e.g. - #ccy1,#ccy2 etc.) and use "for(int i=0;i
for(int i=0;i<NumberOfCurrenciesToAdd;i++)
dbCmd.Parameters.AddWithValue("#ccy"+i, currencyArray[i]);
Or the other option is to do something completely different and less rubbish and hack-esque. Help greatly appreciated.
EDIT - SQL Server 2005
EDIT2 - Must Use SPROCS - Company Specification Requirement.
You never specified SQL Server version, but for 2008 there are Table-Valued Parameters, which may help you:
Table-valued parameters are a new parameter type in SQL Server 2008. Table-valued parameters are declared by using user-defined table types. You can use table-valued parameters to send multiple rows of data to a Transact-SQL statement or a routine, such as a stored procedure or function, without creating a temporary table or many parameters.
I worked for a company that had to do this. It is much easier to just pass an nvarchar that is really a list that is comma delimited and then parse it when you get into the stored proc and insert the values into a temp table. The other option would be to have an xml parameter in your proc. That should also work. This is all for SQL 2005. 2008 does give you the table variable and that would be your best option.
I would try to stay away from dynamically changing your stored proc because I think that would be hard to maintain. At any given time if you try to look at the proc it could be different. Also, what happens when 2 people are trying to use your site and hit that proc at the same moment? One person's session will be modifying the procedure and the others will try to do it. This could cause a lock on the stored proc or it could cause other issues. Regardless it would be pretty inefficient.
Here is another option - though I think Anton's answer is better. You can pass in a csv string as a single parameter. Use a user-defined function to convert the csv string into a table of values, which you can join in your query. There are several csv parsing functions listed on SO and other places (though, sorry, I can't come up with a link right now).
edit: here is another option. Pass in the same csv string, then generate the sql query as a string in the procedure, and execute the string. Use the csv in an 'in' clause :
where i.ccy in (1,2,3,4)
I would not try to change the stored procedure, but (since you are on SQL Server 2005 and don't have table variable parameters) just pass in a comma separated list of values and let the procedure split them apart. You can change your C# loop to just build a CSV string and once you create a SQL split procedure, use it like:
SELECT
*
FROM YourTable y
INNER JOIN dbo.yourSplitFunction(#Parameter) s ON y.ID=s.Value
I prefer the number table approach to split a string in TSQL
For this method to work, you need to do this one time table setup:
SELECT TOP 10000 IDENTITY(int,1,1) AS Number
INTO Numbers
FROM sys.objects s1
CROSS JOIN sys.objects s2
ALTER TABLE Numbers ADD CONSTRAINT PK_Numbers PRIMARY KEY CLUSTERED (Number)
Once the Numbers table is set up, create this split function:
CREATE FUNCTION [dbo].[FN_ListToTable]
(
#SplitOn char(1) --REQUIRED, the character to split the #List string on
,#List varchar(8000)--REQUIRED, the list to split apart
)
RETURNS TABLE
AS
RETURN
(
----------------
--SINGLE QUERY-- --this will not return empty rows
----------------
SELECT
ListValue
FROM (SELECT
LTRIM(RTRIM(SUBSTRING(List2, number+1, CHARINDEX(#SplitOn, List2, number+1)-number - 1))) AS ListValue
FROM (
SELECT #SplitOn + #List + #SplitOn AS List2
) AS dt
INNER JOIN Numbers n ON n.Number < LEN(dt.List2)
WHERE SUBSTRING(List2, number, 1) = #SplitOn
) dt2
WHERE ListValue IS NOT NULL AND ListValue!=''
);
GO
You can now easily split a CSV string into a table and join on it:
select * from dbo.FN_ListToTable(',','1,2,3,,,4,5,6777,,,')
OUTPUT:
ListValue
-----------------------
1
2
3
4
5
6777
(6 row(s) affected)
Your can pass in a CSV string into a procedure and process only rows for the given IDs:
SELECT
y.*
FROM YourTable y
INNER JOIN dbo.FN_ListToTable(',',#GivenCSV) s ON y.ID=s.ListValue
I use this function to split CSV text into a table of numbers, it has great performance due to various optimizations (like returning a table with a primary key which greatly influence the query optimizer to produce good query plans ever for extremely large data sets).
Also it's not limited to 4000 characters, so you can pass in very large strings.
CREATE Function [dbo].[TextSplitToInt](#list text,
#delim char(1) = N',')
RETURNS #T TABLE (ID_T int primary key)
BEGIN
DECLARE #slices TABLE (slice nvarchar(4000) NOT NULL)
DECLARE #slice nvarchar(4000),
#textpos int,
#maxlen int,
#stoppos int
SELECT #textpos = 1, #maxlen = 4000 - 2
WHILE datalength(#list) / 2 - (#textpos - 1) >= #maxlen
BEGIN
SELECT #slice = substring(#list, #textpos, #maxlen)
SELECT #stoppos = #maxlen - charindex(#delim, reverse(#slice))
INSERT #slices (slice) VALUES (#delim + left(#slice, #stoppos) + #delim)
SELECT #textpos = #textpos - 1 + #stoppos + 2 -- On the other side of the comma.
END
INSERT #slices (slice)
VALUES (#delim + substring(#list, #textpos, #maxlen) + #delim)
INSERT #T (ID_T)
SELECT distinct Cast(str as int)
FROM (SELECT str = ltrim(rtrim(substring(s.slice, N.Number + 1,
charindex(#delim, s.slice, N.Number + 1) - N.Number - 1)))
FROM Numbers N
JOIN #slices s ON N.Number <= len(s.slice) - 1
AND substring(s.slice, N.Number, 1) = #delim) AS x
RETURN
END

Categories

Resources