List [a,b,c] to a,b,c - sap-ase

I receive a proc with this on parameter:
exec do_something [a,b,c]
I need to make a query like:
select * from B where b not in ("a","b","c")
How can I make this transformation?

A quick/easy solution would be to use the str_replace() function to reformat the input parameter, then build a dynamic query using said modified parameter, eg:
declare #param varchar(100),
#query varchar(1000)
select #param = 'a,b,c'
select #param = '"' + str_replace(#param,',','","') + '"'
print "#param: %1!",#param
select #query = 'select * from B where b not in (' + #param + ')'
print "#query: %1!",#query
exec(#query)
go
#param: "a","b","c"
#query: select * from B where b not in ("a","b","c")
.... results of running query .....

Related

Condition WHERE string without/with space

I have table with some strings. I would like make select with condition string = eqauls something
I Dont have any other strings....
The select returns more rows when I Have:
What is wrong?
DECLARE #C VARCHAR(2) = 'A'+SPACE(1)
DECLARE #T TABLE (id INT NOT NULL, string VARCHAR(200) NOT NULL)
INSERT INTO #T
(
id,
string
)
VALUES
( 1, 'A'), (2,'A'+SPACE(1))
SELECT * FROM #T WHERE string = #C--With space only
Returns:
id string
1 A
2 A
I know hot to make select LIKE '%.... '.
I want to know why TSQL returns more rows.
SQL 2019, MSSQL version 18.9.2
SQL Server follows the ANSI standard when it comes to comparing strings with =. Read a longer description over here: https://dba.stackexchange.com/a/10511/7656
The bottom line is, you can't check for trailing spaces with =. Use LIKE without any % instead.
Given
CREATE TABLE T (id INT NOT NULL, string VARCHAR(200) NOT NULL)
INSERT INTO T VALUES (1, 'A')
INSERT INTO T VALUES (2, 'A ')
this
SELECT id, len(string) len, datalength(string) datalength FROM T
results in
id
len
datalength
1
1
1
2
1
2
and
SELECT id FROM T WHERE string LIKE 'A '
will give you 2. See http://sqlfiddle.com/#!18/2356c9/1
You can use one of the following solutions
-- Option 1: add to the filter the condition `DATALENGTH(#C) = DATALENGTH(string)` or 'DATALENGTH(#C) < DATALENGTH(string)'
SELECT * FROM #T WHERE string = #C and DATALENGTH(#C) <= DATALENGTH(string)
-- Option 2: Use `LIKE` and add the expresion '%'
SELECT * FROM #T WHERE string like #C + '%'
The = operator ignores trailing spaces just like LEN(). The LIKE operator does not
SELECT * FROM #T WHERE string LIKE #C
You can prove this with
SELECT CASE WHEN 'A' = 'A ' THEN 'True' ELSE 'False' END -- True
SELECT CASE WHEN 'A' = ' A' THEN 'True' ELSE 'False' END -- False because of leading space
SELECT CASE WHEN 'A' LIKE 'A ' THEN 'True' ELSE 'False' END -- False
SELECT LEN(string), FROM #T -- both return 1

How to convert postfix expression in the form of a string into infix in c++ without using parenthesis?

There is a postfix expression in the form of string for e.g: ab+cd+* , so the output should be a * c + a * d + b * c + b * d (as we cannot write (a+b)*(c+d) because parenthesis are not allowed.)

How to extract specific text between the second and the third "_" with CHARINDEX and SUBSTRING?

How to write SQL script to get "Cabforce" out of "Email_Transport_Cabforce_NEB_Fallback_LB"?
I found solution below to get all words between first _ and last _ but failed to transform it to get a string between the second _ and the third _.
DECLARE #c varchar(100)
SET #c = 'Email_Transport_Cabforce_NEB_Fallback_LB'
SELECT SUBSTRING(
#c,
CHARINDEX('_', #c) + 1,
LEN(#c) - CHARINDEX('_', #c) - CHARINDEX('_', REVERSE(#c))
)
It's a bit long winded but you could do it this way. It finds the location of the first underscore then uses that as the start for the next CHARINDEX calculation. It then does this again for the third CHARINDEX. The results of the final two calculations are used as the workings for the SUBSTRING.
DECLARE #c VARCHAR(100); SET #c = 'Email_Transport_Cabforce_NEB_Fallback_LB'
SELECT
#C Variable
,SUBSTRING(#c,CHARINDEX('_',#c,CHARINDEX('_',#c)+1)+1,(CHARINDEX('_',#c,CHARINDEX('_',#c,CHARINDEX('_',#c)+1)+1)-CHARINDEX('_',#c,CHARINDEX('_',#c)+1))-1) Result
Output
Variable Result
Email_Transport_Cabforce_NEB_Fallback_LB Cabforce
If your output could contain no Underscores then use this one;
DECLARE #c VARCHAR(100); SET #c = 'Email'
IF NOT EXISTS (SELECT 1 WHERE CHARINDEX('_',#c) < 2)
BEGIN
SELECT
#C Variable
,SUBSTRING(#c,CHARINDEX('_',#c,CHARINDEX('_',#c)+1)+1,(CHARINDEX('_',#c,CHARINDEX('_',#c,CHARINDEX('_',#c)+1)+1)-CHARINDEX('_',#c,CHARINDEX('_',#c)+1))-1) Result
END
ELSE
SELECT #c Result
Output
Result
Email
This shows how you can build it: I've broken it down into pieces; you can see the logic and put it all back into one expression:
DECLARE #c varchar(100) SET #c = 'Email_Transport_Cabforce_NEB_Fallback_LB'
DECLARE #firstIndex int = CHARINDEX('_', #c) + 1
DECLARE #secondIndex int = CHARINDEX('_', #c, #firstIndex) + 1
DECLARE #thirdIndex int = CHARINDEX('_', #c, #secondIndex) + 1
SELECT SUBSTRING( #c, #secondIndex, #thirdIndex - #secondIndex - 1)

T-SQL Split Word into characters

I have searched everywhere and I cannot find this implementation anywhere.
Let's say I have the word: QWERTY
I want to obtain this table:
Q
W
E
R
T
Y
Or for QWERTY AnotherWord I want to obtain
Q
W
E
R
T
Y
[space character here]
A
n
o
t
h
e
r
W
o
r
d
Do it like this:
select substring(a.b, v.number+1, 1)
from (select 'QWERTY AnotherWord' b) a
join master..spt_values v on v.number < len(a.b)
where v.type = 'P'
Declare #word nvarchar(max)
Select #word = 'Hello This is the test';
with cte (Number)as
(Select 1
union all
select Number +1 From cte where number <len(#word)
)
select * from Cte Cross apply (Select SUBSTRING(#word,number,1 ) ) as J(Letter)
Here you have it:
create table #words (
character varchar(1)
)
declare #test varchar(10)
select #test = 'QWERTY'
declare #count int, #total int
select #total = len(#test), #count = 0
while #count <= #total
begin
insert into #words select substring(#test, #count, 1)
select #count = #count + 1
end
select * from #words
drop table #words
Here is a table-valued function (derived from aF's temp table implementation). It differs slightly from aF's implementation in that it starts with #count=1; this excludes an extraneous leading space.
CREATE FUNCTION [dbo].[Chars] (#string VARCHAR(max))
RETURNS #chars TABLE (character CHAR)
AS
BEGIN
DECLARE #count INT,
#total INT
SELECT #total = Len(#string),
#count = 1
WHILE #count <= #total
BEGIN
INSERT INTO #chars
SELECT Substring(#string, #count, 1)
SELECT #count = #count + 1
END
RETURN
END
Usage:
SELECT * FROM dbo.chars('QWERTY AnotherWord')
Please, PLEASE avoid referencing systems tables, specifically system tables in system databases. In fact, the selected answer above probably won't compile in a Visual Studio 2013 Database Project
Table variables are fine, but recursion with a CTE is the answer:
DECLARE #str VARCHAR(max)
SET #str = 'QWERTY AnotherWord'
WITH Split(stpos,endpos)
AS(
SELECT 1 AS stpos, 2 AS endpos
UNION ALL
SELECT endpos, endpos+1
FROM Split
WHERE endpos <= LEN(#str)
)
SELECT
'character' = SUBSTRING(#str,stpos,COALESCE(NULLIF(endpos,0),LEN(#str)+1)-stpos)
,'charindex' = stpos
FROM Split
That said, the use for the code above is to get a table full of letters representing different permissions for a user. That is not the way to do this. Make a table with an ID, a permission code and a description then make a linking table between the users table and the new permissions table. this gives you the same abilities and doesn't make you solve dumb problems like this.
I wanted to contribute my own solution to this problem.
Convert into table valued function as desired (and handle nulls however you wish)
DECLARE #str nvarchar(100) = 'QWERTY AnotherWord'
DECLARE #len int = LEN(#str)-1;
--create a string of len(#str)-1 commas
--because STRING_SPLIT will return n rows for n-1 commas
--split string to return a table of len(#str) rows
--provide an index column named [index]
WITH [rows] AS (
SELECT
ROW_NUMBER() OVER (ORDER BY [value]) [index]
FROM STRING_SPLIT(REPLICATE(',', #len), ',')
),
--for each row, take the index number
--and extract the character from that index
[split] AS (
SELECT
[index],
SUBSTRING(#str,[index],1) [char]
FROM [rows]
)
--maintain the same order
--and return just the extracted characters
SELECT
--[index],
[char]
FROM [split]
ORDER BY [index] ASC
output:
char
----
Q
W
E
R
T
Y
A
n
o
t
h
e
r
W
o
r
d
I like the use of REPLICATE() and substring in the answer by #drrollergator. I find value in the answer below, in accounting for:
The truncation to 8000 characters mentioned by Microsoft learn/docs. Explicitly casting to a larger datatype will avoid this.
the unordered ROW_NUMBER as mentioned in [https://stackoverflow.com/questions/44105691/row-number-without-order-by].
Sample SQL:
DECLARE #str NVARCHAR(MAX) = N'QWERTY AnotherWord'
SELECT
ss.[value]
FROM
( SELECT TOP(LEN(#str))
SUBSTRING(#str,n.[i],1) [value]
,n.[i]
FROM ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT '.')) [i] FROM STRING_SPLIT(REPLICATE(CAST('.' AS VARCHAR(MAX)),LEN(#str) - 1),'.') ) n([i])
/* [A.] Generate numbers equal to character count in #expression */
ORDER BY n.[i]
/* [B.] Return 1-Char-Substring for each number/position */
) ss

Split sql string into words

I want to split string into words like below, the output of all the string should be same:
INPUT:
1. This is a string
2. This is a string
3. This is a string
4. This is a string
OUTPUT:
This is a
Means, that I want first three words from the sentence, irrespective of the spaces.
Try this:
declare #s1 varchar(3000) ;
declare #xml xml,#str varchar(100),#delimiter varchar(10), #out varchar(max);;
select #delimiter =' '
select #s1 = 'This is a string';
select #s1 = 'This is a string ';
select #s1 = 'This is a string ';
select #s1 = 'This is a string';
select #xml = cast(('<X>'+replace(#s1,#delimiter ,'</X><X>')+'</X>') as xml)
select top 3 #out =
COALESCE(#out + ' ', '') + C.value('.', 'varchar(100)')
from #xml.nodes('X') as X(C)
where LEN(C.value('.', 'varchar(10)')) > 0
select #out
Now your case contains two steps:
1. Removing additional spaces and converting them to single space. You can use REPLACE() method to this.
SELECT REPLACE(REPLACE(REPLACE("This is a string",' ','<>'),'><',''),'<>',' ')
Process:
The innermost REPLACE changes all blanks to a less-than greater-than pair.
If there are three spaces between This and is, the innermost REPLACE returns This<><><>is.
The middle REPLACE changes all greater-than less-than pairs to the empty string, which removes them.
The<><><>is becomes The<>is.
The outer REPLACE changes all less-than greater-than pairs to a single blank. The<>is becomes
The is.
Now all the sentences are normalized with one space.
2. Split the words and get the three words.
There are lot of Stackoverflow question which discusses them. I liked the Common Table Expression to split the string : How do I split a string so I can access item x?
Let me know if you require any help in the splitting the words.
Create a Tally Table:
SELECT TOP 11000
IDENTITY( INT,1,1 ) AS Num
INTO dbo.Tally
FROM Master.dbo.SysColumns sc1,
Master.dbo.SysColumns sc2
GO
Create a Table Valued Function:
CREATE FUNCTION dbo.[fnSetSplit]
(
#String VARCHAR(8000),
#Delimiter CHAR(1)
)
RETURNS TABLE
AS
RETURN
( SELECT Num,
SUBSTRING(#String, CASE Num
WHEN 1 THEN 1
ELSE Num + 1
END,
CASE CHARINDEX(#Delimiter, #String,
Num + 1)
WHEN 0
THEN LEN(#String) - Num + 1
ELSE CHARINDEX(#Delimiter,
#String, Num + 1)
- Num
- CASE WHEN Num > 1 THEN 1
ELSE 0
END
END) AS String
FROM dbo.Tally
WHERE Num <= LEN(#String)
AND ( SUBSTRING(#String, Num, 1) = #Delimiter
OR Num = 1 )
)
Query function:
SELECT TOP 3
fss.String
FROM dbo.fnSetSplit('This is a string', ' ') fss
WHERE NOT ( fss.String = '' )
If you need to reconcatenate, look at string concatenation using FOR XML (PATH)
SQL Server 2016 (compatibility level 130) allows to use STRING_SPLIT function:
DECLARE #delimiter varchar(10) = ' '
SELECT STRING_AGG(value, #delimiter)
FROM (SELECT TOP 3 value FROM STRING_SPLIT('This is a string', #delimiter) WHERE LEN(value)>0) inq
SELECT STRING_AGG(value, #delimiter)
FROM (SELECT TOP 3 value FROM STRING_SPLIT('This is a string ', #delimiter) WHERE LEN(value)>0) inq
SELECT STRING_AGG(value, #delimiter)
FROM (SELECT TOP 3 value FROM STRING_SPLIT('This is a string', #delimiter) WHERE LEN(value)>0) inq
SELECT STRING_AGG(value, #delimiter)
FROM (SELECT TOP 3 value FROM STRING_SPLIT('This is a string', #delimiter) WHERE LEN(value)>0) inq
Result:
This is a
This is a
This is a
This is a

Resources