SQL Server Parse Key Value from text - sql

i am a novice programmer in sql.
I want to grab some key value pairs from the text in sql column
e.g.
some text[Key1=Val1]some text[Key2=Val2][Key3=Val3]some text
i want a function which takes Keyname as input & returns the value related with it if found.
GetValueFmKey('Key1')
should return Val1 and like on.
Note: the specified values are in text it may have pre or post text exists.

Assumptions:
The square brackets [] are included as part of the text
You can use a stored procedure (not sure if you mean SQL Function or a subroutine, i.e. stored proc.
Place the following code in a stored procedure.
Have it's parameter use my #Param and remove the #Param declaration and SET value
DECLARE #Param VARCHAR(255) --Remove this
SET #Param = 'Key1' --Remove this
DECLARE #KeyStart SMALLINT
DECLARE #ValueStart SMALLINT
DECLARE #ValueEnd SMALLINT
SELECT #KeyStart = CHARINDEX(#Param, Col2)
FROM Table1
WHERE Col1 = 1;
SET #ValueStart = #KeyStart + LEN(#Param) + 1
SELECT #ValueEnd = CHARINDEX(']', Col2, #KeyStart)
FROM Table1
WHERE Col1 = 1;
SELECT SUBSTRING(Col2, #ValueStart, #ValueEnd - #ValueStart)
FROM Table1
WHERE Col1 = 1

Use following function:
ALTER FUNCTION dbo.GetValueFmKey(#text NVARCHAR(1000), #Key NVARCHAR(100))
RETURNS NVARCHAR(100)
AS BEGIN
SET #key = '['+#key + '='
IF (CHARINDEX(#Key,#text)=0) RETURN ''
SET #text = SUBSTRING(#text,CHARINDEX(#Key,#text)+LEN(#Key), LEN(#text))
RETURN LEFT(#text,CHARINDEX(']',#text)-1)
END
GO
For Example:
SELECT dbo.GetValueFmKey('text[Key2=Val2][Key3=Val3]','Key2')

Related

Procedure using Variables and Dynamic SQL

The seed of this project is that our SSRS reporting team submits their reporting requests with a query and slicers declared as nvarchar(4000). However the table that it is hitting uses varchar(4000). This is causing various issues and I have been charged with creating a procedure that takes the passed variables, converts any nvarchar variable to varchar and then returns the results of the query to reporting. Here is an example of the reporting queries as they currently exist.
SELECT officeid AS Value1, officename AS Value2, claimcode AS Value3 FROM reporting.vStatus
WHERE (SubmitDate BETWEEN #DateParameter1 AND #DateParameter2)
AND (officeid IN (SELECT value FROM string_split(#TextParameter1, ''^'')))
--Variables Being Passed
#DetailQueryTextParameter1 nvarchar(4000)
#DetailQueryTextParameter1=N'1o1o1o1o-1o1o10-1p1p1p6-4r5t5y-q2w3er5^5d4f6t21-5f2sde65rf47-f5df6ffd5-d5e8r7'
#DateParameter1='2022-11-20 00:00:00',
#DateParameter2='2022-11-20 00:00:00'
Let's focus on the #TextParameter1 variable as this is a string of alphanumeric IDs designated as nvarchar datatype and delimited by ^. My procedure takes this variable and splits the elements based on the ^ and inserts into a temp table, #slicer, casted as varchar instead of nvarchar. I then use a while statement to add all of theses ids to a new empty string,#var2, delimited by ^. This much seems to work. Here is a short example
#TextParameter1 nvarchar(4000)=NULL,
#DateParameter1 datetime = ' ',
#DateParameter2 datetime = ' ',
#DetailQuery nvarchar(max) = ' '
if #DetailQueryTextParameter1 is not null
SELECT identity(Int,1,1) as rowkey, value into #slicer FROM STRING_SPLIT(cast(#DetailQueryTextParameter1 as varchar(4000)), '^')
declare #var2 varchar(4000) = ' ', #rownum int = 1, #setnum int = (select count(*) from #slicer)
while #rownum <=#setnum
Begin
declare #slice varchar(4000) = (Select value +'^' from #slicer where rowkey = #rownum)
set #var2 = #var2 +#slice
set #rownum = #rownum+1
End
set #var2 = (left(#var2,LEN(#var2)-1))
In order for the procedure to handle running the query with the newly converted slices I used the following input
declare #slice_w nvarchar(max) = ' WHERE (SubmitDate BETWEEN '+cast(#DetailQueryDateParameter1 as datetime)+' AND ' +cast(#DetailQueryDateParameter2 as datetime)+') AND (officeid IN (SELECT value FROM string_split('+#var2+', ''^'')))'
set #DetailQuery=#DetailQuery+#slice_w
print(#DetailQuery)
print(#var2)
execute sp_executesql #DetailQuery
and then I call the procedure using the the following variables
EXEC Procedure
#TextParameter1 = N'1o1o1o1o-1o1o10-1p1p1p6-4r5t5y-q2w3er5^5d4f6t21-5f2sde65rf47-f5df6ffd5-d5e8r7'
#DateParameter1='2022-01-01',
#DateParameter2='2022-01-02',
#DetailQuery='SELECT officeid AS Value1, officename AS Value2, claimcode AS Value3 FROM reporting.vStatus '
Thank you for your patience up to this point. The problem I run into is when I call this procedure it will return an error in regards to how the #DetailQuery handles variables #var2,#DateParameter1,and #DateParameter2. SELECT value FROM string_split('+#var2+', ''^'') will return a delimited list of ids outside of a string making it so that the query cant read it properly. The date variables return a date/time conversion error, but if I cast them as Varchar or nVarchar they return Jun 1 2022 instead of the expected '2022-01-01'
What do I need to do in order to get the dynamic sql to handle these variables properly? Do I need to encapsulate #var2 in '' ? Do I need to specify a datetime format for the date variables? Do I need to consider adding the variable names to the #DetailQuery when I call the procedure?

How to append a part to query based of if else conditions within a stored procedure

What I have to achieve is something like appending string to existing variable for e.g. :
string abc = "good";
abc += "afternoon";
so that the final output will be "good afternoon"
I have written the following stored procedure :
CREATE OR ALTER PROCEDURE [dbo].[sp_getPropertyContactUsDetails]
(#isDeleted CHAR(1),
#cName VARCHAR(MAX))
AS
BEGIN
SET NOCOUNT ON;
--DECLARE #mainQ varchar(max)
SELECT
pcd.col1, pcd.col2,.....
CASE
WHEN pcd.col1 = 'abc'
THEN (SELECT pname FROM tbl_pd1 WHERE id = pcd.id)
WHEN pcd.col2 = 'def'
THEN (SELECT pname FROM tbl_pd2 WHERE id = pcd.id)
END AS 'pname',
FROM
tbl_pcd pcd
WHERE
pcd.isDeleted = #isDeleted
Now I want to append a part to the query based on if else condition as following:
IF #cName IS NOT NULL
THEN
append following part to the select query :
ORDER BY ID DESC
IF #cName IS NULL then don't append the part!
I have tried with declaring a parameter as :
DECLARE #mainQ VARCHAR(MAX)
Then I am doing something like :
SET #mainQ = 'The select statement as specified above in the stored procedure'
T
hen for append part :
IF #cName IS NOT NULL
SET #mainQ = #mainQ + ' ORDER BY ID DESC'
END (END statement of stored procedure)
Can anyone help me out with the following situations, how to append a part to original query based on if else condition??
Note that stored procedure may contain multiple if statements!
If you just want add to order by clause then you can use something like -
ORDER BY
CASE WHEN #cName IS NOT NULL
THEN ID
END DESC

Compare if textfield value is greater then x

How can I compare a numeric value in a textfield against any given value x?
Iam writing the code in TSQL.
See code below:
DECLARE #TA_SM varchar(5000)
DECLARE #TA_MSD varchar(5000)
DECLARE #T_SUM int(5)
SET #TA_SM = '[field name='Toelichting_advies_ISM']'
SET #TA_MSD = '[field name='Toelichting_advies_MSD']'
SET #T_SUM = '[field name='Offerte_totaalsom']'
IF ((#TA_SM = '') OR (#TA_MSD = '')) AND (#T_SUM >25.000)
SELECT 1
ELSE
SELECT 2
I get a error message saying:
Must declare the table value #T_SUM
The statement before the AND operator is working just fine.
Change the DECLARE #T_SUM int(5) to DECLARE #T_SUM int.
If you only want to have 5 digits use NUMERIC instead of INT.
EDIT 1 - Full code.
This should run through in TSQL without error but I am not sure if this is the result you are looking for. I have added extra quote marks to your variables definitions.
BEGIN
DECLARE #TA_SM varchar(5000)
DECLARE #TA_MSD varchar(5000)
DECLARE #T_SUM int
SET #TA_SM = '[field name=''Toelichting_advies_ISM'']'
SET #TA_MSD = '[field name=''Toelichting_advies_MSD'']'
SET #T_SUM = '[field name=''Offerte_totaalsom'']'
IF ((#TA_SM = '') OR (#TA_MSD = '')) AND (#T_SUM >25.000)
SELECT 1
ELSE
SELECT 2
END

Split/explode comma delimited string with Sybase SQL Anywhere

UPDATE:
Someone marked this question as duplicate of
How do I split a string so I can access item x.
But it's different, my question is about Sybase SQL Anywhere, the other is about MS SQL Server. These are two different SQL engines, even if they have the same origin, they have different syntax. So it's not duplicate. I wrote in the first place in description and tags that it's all about Sybase SQL Anywhere.
I have field id_list='1234,23,56,576,1231,567,122,87876,57553,1216'
and I want to use it to search IN this field:
SELECT *
FROM table1
WHERE id IN (id_list)
id is integer
id_list is varchar/text
But in this way this doesn't work, so I need in some way to split id_list into select query.
What solution should I use here? I'm using the T-SQL Sybase ASA 9 database (SQL Anywhere).
Way I see this, is to create own function with while loop through,
and each element extract based on split by delimiter position search,
then insert elements into temp table which function will return as result.
This can be done without using dynamic SQL but you will need to create a couple of supporting objects. The fist object is a table valued function that will parse your string and return a table of integers. The second object is a stored procedure that will have a parameter where you can pass the string (id_list), parse it to a table, and then finally join it to your query.
First, create the function to parse the string:
CREATE FUNCTION [dbo].[String_To_Int_Table]
(
#list NVARCHAR(1024)
, #delimiter NCHAR(1) = ',' --Defaults to CSV
)
RETURNS
#tableList TABLE(
value INT
)
AS
BEGIN
DECLARE #value NVARCHAR(11)
DECLARE #position INT
SET #list = LTRIM(RTRIM(#list))+ ','
SET #position = CHARINDEX(#delimiter, #list, 1)
IF REPLACE(#list, #delimiter, '') <> ''
BEGIN
WHILE #position > 0
BEGIN
SET #value = LTRIM(RTRIM(LEFT(#list, #position - 1)));
INSERT INTO #tableList (value)
VALUES (cast(#value as int));
SET #list = RIGHT(#list, LEN(#list) - #position);
SET #position = CHARINDEX(#delimiter, #list, 1);
END
END
RETURN
END
Now create your stored procedure:
CREATE PROCEDURE ParseListExample
#id_list as nvarchar(1024)
AS
BEGIN
SET NOCOUNT ON;
--create a temp table to hold the list of ids
CREATE TABLE #idTable (ID INT);
-- use the table valued function to parse the ids into a table.
INSERT INTO #idTable(ID)
SELECT Value FROM dbo.String_to_int_table(#id_list, ',');
-- join the temp table of ids to the table you want to query...
SELECT T1.*
FROM table1 T1
JOIN #idTable T2
on T1.ID = T2.ID
Execution Example:
exec ParseListExample #id_list='1234,23,56,576,1231,567,122,87876,57553,1216'
I hope this helps...
Like Mikael Eriksson said, there is answer at dba.stackexchange.com with two very good solutions, first with use of sa_split_list system procedure, and second slower with CAST statement.
For the Sybase SQL Anywhere 9 sa_split_list system procedure not exist, so I have made sa_split_list system procedure replacement (I used parts of the code from bsivel answer):
CREATE PROCEDURE str_split_list
(in str long varchar, in delim char(10) default ',')
RESULT(
line_num integer,
row_value long varchar)
BEGIN
DECLARE str2 long varchar;
DECLARE position integer;
CREATE TABLE #str_split_list (
line_num integer DEFAULT AUTOINCREMENT,
row_value long varchar null,
primary key(line_num));
SET str = TRIM(str) || delim;
SET position = CHARINDEX(delim, str);
separaterows:
WHILE position > 0 loop
SET str2 = TRIM(LEFT(str, position - 1));
INSERT INTO #str_split_list (row_value)
VALUES (str2);
SET str = RIGHT(str, LENGTH(str) - position);
SET position = CHARINDEX(delim, str);
end loop separaterows;
select * from #str_split_list order by line_num asc;
END
Execute the same way as sa_split_list with default delimiter ,:
select * from str_split_list('1234,23,56,576,1231,567,122,87876,57553,1216')
or with specified delimiter which can be changed:
select * from str_split_list('1234,23,56,576,1231,567,122,87876,57553,1216', ',')
You use text in your query and this is not going to work.
Use dynamic query.
Good contribution from bsivel answer, but to generalise it (for other separators than a comma), then the line
SET #list = LTRIM(RTRIM(#list))+ ','
must become
SET #list = LTRIM(RTRIM(#list))+ #delimiter
The first version will only work for comma-separated lists.
The dynamic query approach would look like this:
create procedure ShowData #IdList VarChar(255)
as
exec ('use yourDatabase; select * from MyTable where Id in ('+#IdList+')')

Passing SQL stored procedure entirety of WHERE clause

I have a SQL stored procedure of the form
SELECT [fields] FROM [table] WHERE #whereSql
I want to pass the procedure an argument (#whereSql) which specifies the entire WHERE clause, but the following error is returned:
An expression of non-boolean type specified in a context where a condition is expected
Can this be done?
The short answer is that you can't do it like this -- SQL Server looks at the contents of a variable as a VALUE. It doesn't dynamically build up the string to execute (which is why this is the correct way to avoid SQL injection attacks).
You should make every effort to avoid a dynamic WHERE as you're trying to do, largely for this reason, but also for the sake of efficiency. Instead, try to build up the WHERE clause so that it short-circuits pieces with lots of ORs, depending on the situation.
If there's no way around it, you can still build a string of your own assembled from the pieces of the command, and then EXEC it.
So you could do this:
DECLARE #mywhere VARCHAR(500)
DECLARE #mystmt VARCHAR(1000)
SET #mywhere = ' WHERE MfgPartNumber LIKE ''a%'' '
SELECT #mystmt = 'SELECT TOP 100 * FROM Products.Product AS p ' + #mywhere + ';'
EXEC( #mystmt )
But I recommend instead that you do this:
SELECT TOP 100 *
FROM Products.Product AS p
WHERE
( MfgPartNumber LIKE 'a%' AND ModeMfrPartNumStartsWith=1)
OR ( CategoryID = 123 AND ModeCategory=1 )
I believe this can be done using Dynamic SQL. See below:
CREATE PROCEDURE [dbo].[myProc]
#whereSql nvarchar(256)
AS
EXEC('SELECT [fields] FROM [table] WHERE ' + #whereSql)
GO
That said, you should do some serious research on dynamic SQL before you actually use it.
Here are a few links that I came across after a quick search:
http://www.sommarskog.se/dynamic_sql.html
http://msdn.microsoft.com/en-us/library/aa224806%28SQL.80%29.aspx
http://www.itjungle.com/fhg/fhg100505-story02.html
Make sure you read this fully
www.sommarskog.se/dynamic_sql.html
Dynamic SQL listed in some of the Answers is definitely a solution. However, if Dynamic SQL needs to be avoided, one of the solutions that I prefer is to make use of table variables (or temp tables) to store the parameter value that is used for comparison in WHERE clause.
Here is an example Stored Procedure implementation.
CREATE PROCEDURE [dbo].[myStoredProc]
#parameter1 varchar(50)
AS
declare #myTempTableVar Table(param1 varchar(50))
insert into #myTempTableVar values(#parameter1)
select * from MyTable where MyColumn in (select param1 from #myTempTableVar)
GO
In case you want to pass in multiple values, then the comma separated values can be stored as rows in the table variable and used in the same way for comparison.
CREATE PROCEDURE [dbo].[myStoredProc]
#parameter1 varchar(50)
AS
--Code Block to Convert Comma Seperated Parameter into Values of a Temporary Table Variable
declare #myTempTableVar Table(param1 varchar(50))
declare #index int =0, #tempString varchar(10)
if charindex(',',#parameter1) > 0
begin
set #index = charindex(',',#parameter1)
while #index > 0
begin
set #tempString = SubString(#parameter1,1,#index-1)
insert into #myTempTableVar values (#tempString)
set #parameter1 = SubString(#parameter1,#index+1,len(#parameter1)-#index)
set #index = charindex(',',#parameter1)
end
set #tempString = #parameter1
insert into #myTempTableVar values (#tempString)
end
else
insert into #myTempTableVar values (#parameter1)
select * from MyTable where MyColumn in (select param1 from #myTempTableVar)
GO
http://sqlmag.com/t-sql/passing-multivalued-variables-stored-procedure
try this it works!!
CHARINDEX (',' + ColumnName + ',', ',' +
REPLACE(#Parameter, ' ', '') + ',') > 0
execute syntax set #Parameter= 'nc1,nc2'