#strA and #strB are two variables
#strA = "055367911126753316"
#strB = "00055367"
how to find the common part "055367" and remove it from string A, using SQL server query without looping?
the result should be "911126753316"
be noted that always string A begin by the end part of string B
You can use replace in sql server as below:
declare #strA varchar(50) = '055367911126753316'
declare #strB varchar(50) = '00055367'
select replace(#strA,right(#strB,len(#strB)-2),'')
If it is in different columns in a table you can use as below:
create table #yourcolumns ( cola varchar(50), colb varchar(50))
insert into #yourcolumns (cola, colb) values
('055367911126753316', '00055367')
select replace(cola,right(colb,len(colb)-2),'') from #yourcolumns
I think we need to go for substring in your case as you are looking for startswith
select SUBSTRING(cola,CHARINDEX(LEFT(REVERSE(colb),1),cola)+1,len(cola)) from #yourcolumns
Try This, works dynamically.
declare #StrA Nvarchar(250)='055367911126753316',#StrB Nvarchar(250)='0055367'
select right(#StrA,len(#strA)-PATINDEX('%[^'+#StrB+']%',#strA)+1)
It YES with looping, But it work....
create function MyFn (#A nvarchar(max), #B nvarchar(max))
returns nvarchar(max)
as
begin
declare #I int, #L int, #SUB nvarchar(max)
select #I = 1, #L = len(#B)
while #I<#L begin
set #SUB = substring(#B,#I,#L-#I+1)
if charindex(#SUB,#A,1) > 0 begin
set #A = replace(#A, #SUB, '')
break
end
set #I = #I + 1
end
return #A
end
If each string A starts with 000 and each string b just contains one 0 this should retrieve the correct result
WITH CTE AS (SELECT [StringA], [StringB],REPLACE([StringB], '000', '0') [tbd]
FROM YourTable)
SELECT REPLACE(YourTable.[StringA], CTE.[tbd], ''), YourTable.[StringA]
FROM YourTable
JOIN CTE ON YourTable.[StringA]= CTE.[StringA]
DECLARE #strA varchar(50) = '055367911126753316'
DECLARE #strB varchar(50) = '00055367'
SET #strA=(SELECT REVERSE(SUBSTRING(#strA, PATINDEX('%[^0 ]%', #strA + ' '), LEN(#strA))))
SET #strB=(SELECT REVERSE(SUBSTRING(#strB, PATINDEX('%[^0 ]%', #strB + ' '), LEN(#strB))))
SELECT #strA String ,#strB StringtoSearch,REVERSE(SUBSTRING(#strA,0,CHARINDEX(RIGHT(#strA,LEN(#strB)),#strA)))ExpectedOutput
Output
ExpectedOutput
---------------
911126753316
Related
Thanks to advise me for the below issue:
I am using below query to fetch the value of a column:
Select OptionList = case when isnull(AS_CIS_Code,'') <> '' then AS_CIS_Code
else ''
end
from added_services
AS_CIS_Code column is of varchar(10) in added_services table. It contains values like 'AB', 'ABC', 'GHKIK', 'UYTIOPJ' and so on which represents different codes.
Now I have to select these codes after modifying the above query so that '_' is appended after each character.
Like it should be fetched as 'A_B_', 'A_B_C_', 'G_H_K_I_K_', 'U_Y_T_I_O_P_J_'.
How should I implement it? Using a temp table will down the performance for one column only, so should I use while loop or please suggest me better alternatives.
Try this:
DECLARE #Input VARCHAR(100) = 'TESTING'
DECLARE #Pos INT = LEN(#Input)
WHILE #Pos > 1
BEGIN
SET #Input = STUFF(#Input,#Pos,0,'_')
SET #Pos = #Pos - 1
END
SELECT #Input
Output
T_E_S_T_I_N_G
UDF
CREATE FUNCTION PadStr(#Data VARCHAR(100)) RETURNS VARCHAR(200)
AS
BEGIN
DECLARE #Input VARCHAR(200) = #Data
DECLARE #Pos INT = LEN(#Input)
WHILE #Pos > 1
BEGIN
SET #Input = STUFF(#Input,#Pos,0,'_')
SET #Pos = #Pos - 1
END
RETURN #Input + '_'
END
Output
SELECT dbo.PadStr('TESTING') -- T_E_S_T_I_N_G_
You can split the string using a numbers table and the rebuild it using for xml path().
select isnull(C.Value, '') as AS_CIS_Code
from added_services as A
cross apply (
select substring(A.AS_CIS_Code, T.N, 1)+'_'
from (values(1),(2),(3),(4),(5),(6),(7),(8),(9),(10)) as T(N)
where T.N <= len(A.AS_CIS_Code)
order by T.N
for xml path('')
) as C(Value)
SQL Fiddle
Try this
Create Function Code_Pad
(
#code varchar(max)
)
returns varchar(max)
as
begin
Declare #coding varchar(max) =''
Declare #i int = 1
WHILE(LEN(#Coding)<=2*len(#code)-1)
begin
Select #coding = #coding+SUBSTRING(#code,#i,1)+'_'
set #i=#i+1
end
return #coding
end
End of Function
Select OptionList = case when isnull(AS_CIS_Code,'') <> '' then dbo.Code_Pad(AS_CIS_Code) else ''
end
from added_services
May not be best solution, but works in one query using recursion
;WITH valCTE(Replaced,ToBeReplaced,Position)
AS
(
SELECT CAST(LEFT(AS_CIS_Code,1) + '_' AS VARCHAR(20))
,SUBSTRING(AS_CIS_Code,2,LEN(AS_CIS_Code)-1)
,1
FROM added_services
UNION ALL
SELECT CAST(Replaced + LEFT(ToBeReplaced,1) + '_' AS VARCHAR(20))
,SUBSTRING(ToBeReplaced,2,LEN(ToBeReplaced)-1)
,Position+1
FROM valCTE
WHERE LEN(ToBeReplaced)>0
)
SELECT TOP 1 LEFT(Replaced,LEN(Replaced)-1) -- remove last _
FROM valCTE
ORDER BY Position DESC
This is the scenario:
My app will have the following:
A listbox (The checkbox property enabled) that will display a list of Something.
The user will select from the listbox (multiselect) by using the checkbox.
I will loop into All the checked items and store the ID's into an array. I will store the ID's into something like this separating the ID with a comma (1,2,3,4) and then I will use length -1 to delete the last comma.
How can I convert the string 1,2,3,4 into an integer type of data if my stored procedure is like this?
Select * from tblSomething Where ID in (1,2,3,4)
You can use the following SQL function.
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
CREATE FUNCTION [dbo].[CommaSeparatedToString]
(
#psCSString VARCHAR(8000)
)
RETURNS #otTemp TABLE(sID VARCHAR(20))
AS
BEGIN
DECLARE #sTemp VARCHAR(50)
WHILE LEN(#psCSString) > 0
BEGIN
SET #sTemp = LEFT(#psCSString, ISNULL(NULLIF(CHARINDEX(',', #psCSString) - 1, -1),
LEN(#psCSString)))
SET #psCSString = SUBSTRING(#psCSString,ISNULL(NULLIF(CHARINDEX(',', #psCSString), 0),
LEN(#psCSString)) + 1, LEN(#psCSString))
INSERT INTO #otTemp VALUES (#sTemp)
END
RETURN
END
And call in your stored procedure like
Select * from tblSomething
Where ID in (SELECT * FROM CommaSeparatedToString('1,2,3,4'))
You can use the
SELECT CAST(MyVarcharCol AS INT) FROM Table
SELECT CONVERT(INT, MyVarcharCol) FROM Table
refer this link
http://msdn.microsoft.com/en-us/library/ms187928.aspx
You need to create dynamic query for this
e.g you are getting list of values in #values paramter so prepare and run the dynamic query like this
DECLARE #query NVARCHAR(500)
DECLARE #values VARCHAR(200)
SET #values='1,2'
SET #query =N'Select * from tblSomething Where ID in ( ' + #values + ')'
SELECT #query
EXEC #Query
Use this function to split the value:
CREATE FUNCTION [dbo].[udfSplitCSV]
(
#String varchar (max),
#Delimiter varchar (10) = ','
)
RETURNS #ValueTable TABLE ([Row] int IDENTITY(1,1), [Value] varchar(max), [Length] int, [Duplicate] int NULL)
BEGIN
DECLARE #NextString varchar(max)
DECLARE #Pos int
DECLARE #NextPos int
IF #String IS NULL RETURN
--Initialize
SET #NextString = ''
SET #String = #String + #Delimiter
--Get position of first Comma
SET #Pos = charindex(#Delimiter,#String)
SET #NextPos = 1
--Loop while there is still a comma in the String
WHILE (#Pos <> 0)
BEGIN
SET #NextString = RTrim(LTrim(SubString(#String,1,#Pos - 1)))
INSERT INTO #ValueTable ([Value], [Length]) VALUES (#NextString, Len(#NextString))
SET #String = SubString(#String,#Pos+1,Len(#String))
SET #NextPos = #Pos
SET #Pos = CharIndex(#Delimiter,#String)
END
UPDATE #ValueTable
SET [Duplicate] = X.Duplicate
FROM #ValueTable VT
INNER JOIN (Select [Row], [Value], Row_Number() OVER (Partition By [Value] ORDER BY [Value], [Row]) as Duplicate FROM #ValueTable) X
ON X.[Row] = VT.[Row]
RETURN
END
-- Select * from dbo.udfSplitCSV('a , c b,c, a', ',')
When you are storing a bunch of IDs into the array, store with single quote.
so it will be ('1','2','3').
Then you no need to covert IDs into integer.
My table has one column that contain strings like: ” HRM_APPLICATION_DELAY_IN”
I want to perform bellow operations on each row on this column
convert to lower case
remove underscore “_”
change case (convert to upper case) of the character after the underscore like: ” hrm_Application_Delay_In”
Need help for conversion. Thanks for advance
Here is a function to achieve it:
create function f_test
(
#a varchar(max)
)
returns varchar(max)
as
begin
set #a = lower(#a)
while #a LIKE '%\_%' ESCAPE '\'
begin
select #a = stuff(#a, v, 2, upper(substring(#a, v+1,1)))
from (select charindex('_', #a) v) a
end
return #a
end
Example:
select dbo.f_test( HRM_APPLICATION_DELAY_IN')
Result:
hrmApplicationDelayIn
To update your table here is an example how to write the syntax with the function:
UPDATE <yourtable>
SET <yourcolumn> = dbo.f_test(col)
WHERE <yourcolumn> LIKE '%\_%' ESCAPE '\'
For a variable this is overkill, but I'm using this to demonstrate a pattern
declare #str varchar(100) = 'HRM_APPLICATION_DELAY_IN';
;with c(one,last,rest) as (
select cast(lower(left(#str,1)) as varchar(max)),
left(#str,1), stuff(lower(#str),1,1,'')
union all
select one+case when last='_'
then upper(left(rest,1))
else left(rest,1) end,
left(rest,1), stuff(rest,1,1,'')
from c
where rest > ''
)
select max(one)
from c;
That can be extended to a column in a table
-- Sample table
declare #tbl table (
id int identity not null primary key clustered,
str varchar(100)
);
insert #tbl values
('HRM_APPLICATION_DELAY_IN'),
('HRM_APPLICATION_DELAY_OUT'),
('_HRM_APPLICATION_DELAY_OUT'),
(''),
(null),
('abc<de_fg>hi');
-- the query
;with c(id,one,last,rest) as (
select id,cast(lower(left(str,1)) as varchar(max)),
left(str,1), stuff(lower(str),1,1,'')
from #tbl
union all
select id,one+case when last='_'
then upper(left(rest,1))
else left(rest,1) end,
left(rest,1), stuff(rest,1,1,'')
from c
where rest > ''
)
select id,max(one)
from c
group by id
option (maxrecursion 0);
-- result
ID COLUMN_1
1 hrm_Application_Delay_In
2 hrm_Application_Delay_Out
3 _Hrm_Application_Delay_Out
4
5 (null)
6 abc<de_Fg>hi
select
replace(replace(replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(replace(replace(
replace(replace(replace(replace(replace(replace(lower('HRM_APPLICATION_DELAY_IN'),'_a','A'),'_b','B'),'_c','C'),'_d','D'),'_e','E'),'_f','F'),
'_g','G'),'_h','H'),'_i','I'),'_j','J'),'_k','K'),'_l','L'),
'_m','M'),'_n','N'),'_o','O'),'_p','P'),'_q','Q'),'_r','R'),
'_s','S'),'_t','T'),'_u','U'),'_v','V'),'_w','W'),'_x','X'),
'_y','Y'),'_z','Z'),'_','')
Bellow two steps can solve problem,as example i use sys.table.user can use any one
declare #Ret varchar(8000), #RetVal varchar(8000), #i int, #count int = 1;
declare #c varchar(10), #Text varchar(8000), #PrevCase varchar, #ModPrefix varchar(10);
DECLARE #FileDataTable TABLE(TableName varchar(200))
INSERT INTO #FileDataTable
select name FROM sys.tables where object_name(object_id) not like 'sys%' order by name
SET #ModPrefix = 'Pur'
DECLARE crsTablesTruncIns CURSOR
FOR select TableName FROM #FileDataTable
OPEN crsTablesTruncIns
FETCH NEXT FROM crsTablesTruncIns INTO #Text
WHILE ##FETCH_STATUS = 0
BEGIN
SET #RetVal = '';
select #i=1, #Ret = '';
while (#i <= len(#Text))
begin
SET #c = substring(#Text,#i,1)
--SET #Ret = #Ret + case when #Reset=1 then UPPER(#c) else LOWER(#c)
IF(#PrevCase = '_' OR #i = 1)
SET #Ret = UPPER(#c)
ELSE
SET #Ret = LOWER(#c)
--#Reset = case when #c like '[a-zA-Z]' then 0 else 1 end,
if(#c like '[a-zA-Z]')
SET #RetVal = #RetVal + #Ret
if(#c = '_')
SET #PrevCase = '_'
else
SET #PrevCase = ''
SET #i = #i +1
end
SET #RetVal = #ModPrefix + #RetVal
print cast(#count as varchar) + ' ' + #RetVal
SET #count = #count + 1
EXEC sp_RENAME #Text , #RetVal
SET #RetVal = ''
FETCH NEXT FROM crsTablesTruncIns INTO #Text
END
CLOSE crsTablesTruncIns
DEALLOCATE crsTablesTruncIns
I'd like to show you my nice and simple solution. It uses Tally function to split the string by pattern, in our case by underscope. For understanding Tally functions, read this article.
So, this is how my tally function looks like:
CREATE FUNCTION [dbo].[tvf_xt_tally_split](
#String NVARCHAR(max)
,#Delim CHAR(1))
RETURNS TABLE
as
return
(
WITH Tally AS (SELECT top (select isnull(LEN(#String),100)) n = ROW_NUMBER() OVER(ORDER BY [name]) from master.dbo.syscolumns)
(
SELECT LTRIM(RTRIM(SUBSTRING(#Delim + #String + #Delim,N+1,CHARINDEX(#Delim,#Delim + #String + #Delim,N+1)-N-1))) Value, N as Ix
FROM Tally
WHERE N < LEN(#Delim + #String + #Delim)
AND SUBSTRING(#Delim + #String + #Delim,N,1) = #Delim
)
)
This function returns a table, where each row represents part of string between #Delim (in our case between underscopes). Rest of the work is simple, just cobination of LEFT, RIGHT, LEN, UPPER and LOWER functions.
declare #string varchar(max)
set #string = ' HRM_APPLICATION_DELAY_IN'
-- convert to lower case
set #string = LOWER(#string)
declare #output varchar(max)
-- build string
select #output = coalesce(#output + '_','') +
UPPER(left(Value,1)) + RIGHT(Value, LEN(Value) - 1)
from dbo.tvf_xt_tally_split(#string, '_')
-- lower first char
select left(lower(#output),1) + RIGHT(#output, LEN(#output) - 1)
Need help on how to improve my SQL script for better performance. dbo.Products table has a million rows. I'm hesitant to rewrite it using dynamic SQL. Thanks!
DECLARE
#Brand varchar(MAX) = 'Brand 1, Brand 2, Brand 3',
#ItemCategory varchar(MAX) = 'IC1, IC2, IC3, IC4, IC5'
--will return all records if params where set to #Brand = NULL, #ItemCategory = NULL
SELECT
[Brand],
SUM([Amount]) AS [Amount]
FROM dbo.Products (NOLOCK)
LEFT JOIN [dbo].[Split](#Brand, ',') FilterBrand ON Brand = [FilterBrand].[Items]
LEFT JOIN [dbo].[Split](#ItemCategory, ',') FilterItemCategory ON ItemCategory = [FilterItemCategory].[Items]
WHERE
(#Brand IS NULL OR (#Brand IS NOT NULL AND [FilterBrand].[Items] IS NOT NULL)) AND
(#ItemCategory IS NULL OR (#ItemCategory IS NOT NULL AND [FilterItemCategory].[Items] IS NOT NULL))
GROUP BY
[Brand]
Below is the split table-valued function that I found on the web:
CREATE function [dbo].[Split]
(
#String varchar(8000),
#Delimiter char(1)
)
RETURNS #Results TABLE (Items varchar(4000))
AS
BEGIN
IF (#String IS NULL OR #String = '') RETURN
DECLARE #i int, #j int
SELECT #i = 1
WHILE #i <= LEN(#String)
BEGIN
SELECT #j = CHARINDEX(#Delimiter, #String, #i)
IF #j = 0
BEGIN
SELECT #j = len(#String) + 1
END
INSERT #Results SELECT RTRIM(SUBSTRING(#String, #i, #j - #i))
SELECT #i = #j + LEN(#Delimiter)
END
RETURN
END
Following solution are with out using functions
Declare #IDs Varchar(100)
SET #IDs = '2,4,6'
Select IsNull(STUFF((Select ', '+ CAST([Name] As Varchar(100)) From [TableName]
Where CharIndex(','+Convert(Varchar,[ID])+',', ','+#IDs+',')> 0
For XML Path('')),1,1,''),'') As [ColumnName]
Here is the function I use. I also have another that wraps this to return numeric values which I find helpful as well.
Edit: Sorry, as for how to improve the performance of the query, I usually split the values into table variables and perform my joins to that but that probably won't change your performance, just your readability. The only thing I can see in terms of performance is your double checking whether your joins produce anything. You really can't get much better performance with two conditional left joins on two tables. It basically boils down to indexes at that point.
(#Brand IS NULL OR [FilterBrand].[Items] IS NOT NULL)
Function:
ALTER FUNCTION [dbo].[fn_SplitDelimittedList]
(
#DelimittedList varchar(8000),
#Delimitter varchar(20)
)
RETURNS
#List TABLE
(
Item varchar(100)
)
AS
BEGIN
DECLARE #DelimitterLength INT
SET #DelimitterLength = LEN(#Delimitter)
-- Tack on another delimitter so we get the last item properly
set #DelimittedList = #DelimittedList + #Delimitter
declare #Position int
declare #Item varchar(500)
set #Position = patindex('%' + #Delimitter + '%' , #DelimittedList)
while (#Position <> 0)
begin
set #Position = #Position - 1
set #Item = LTRIM(RTRIM(left(#DelimittedList, #Position)))
INSERT INTO #List (Item) VALUES (#Item)
set #DelimittedList = stuff(#DelimittedList, 1, #Position + #DelimitterLength, '')
set #Position = patindex('%' + #Delimitter + '%' , #DelimittedList)
end
RETURN
END
Hey just try the split function I have created without using any while loops here.And just use this in place of your split function and use col to match in LEFT join.
ALTER function dbo.SplitString(#inputStr varchar(1000),#del varchar(5))
RETURNS #table TABLE(col varchar(100))
As
BEGIN
DECLARE #t table(col1 varchar(100))
INSERT INTO #t
select #inputStr
if CHARINDEX(#del,#inputStr,1) > 0
BEGIN
;WITH CTE as(select ROW_NUMBER() over (order by (select 0)) as id,* from #t)
,CTE1 as (
select id,ltrim(rtrim(LEFT(col1,CHARINDEX(#del,col1,1)-1))) as col,RIGHT(col1,LEN(col1)-CHARINDEX(#del,col1,1)) as rem from CTE
union all
select c.id,ltrim(rtrim(LEFT(rem,CHARINDEX(#del,rem,1)-1))) as col,RIGHT(rem,LEN(rem)-CHARINDEX(#del,rem,1))
from CTE1 c
where CHARINDEX(#del,rem,1)>0
)
INSERT INTO #table
select col from CTE1
union all
select rem from CTE1 where CHARINDEX(#del,rem,1)=0
END
ELSE
BEGIN
INSERT INTO #table
select col1 from #t
END
RETURN
END
DECLARE #Brand varchar(MAX) = 'Brand 1,Brand 2,Brand 3',
#ItemCategory varchar(MAX) = ' IC1 A ,IC2 B , IC3 C, IC4 D' --'IC1, IC2, IC3, IC4, IC5'
select * from dbo.SplitString(#ItemCategory,',')
If I have a string with values like A,B,C,D,E,F, and I wanted to remove the last comma, is there a function or script I can use to do that?
The below example will trim off the last char of a string but wont care if it's a comma or not
DECLARE #s VARCHAR(50)
SET #s = 'a,b,'
SELECT LEFT(#s, LEN(#s)-1)
If I'm understanding you additional question correctly, I believe you want to archive something like the below...
DECLARE #b TABLE
(
[stuff] VARCHAR(2)
)
INSERT INTO #b VALUES('c')
DECLARE #s VARCHAR(MAX)
SET #s = 'a,b,c'
SELECT [stuff]
FROM #b b
INNER JOIN dbo.list(#s) lst
ON lst.val = b.[stuff]
CREATE function [dbo].[list] ( #list VARCHAR(MAX) )
RETURNS #list_table TABLE ([val] VARCHAR(20))
AS
BEGIN
DECLARE #index INT,
#start_index INT,
#val VARCHAR(20)
SELECT #index = 1
SELECT #start_index = 1
WHILE #index <= DATALENGTH(#list)
BEGIN
IF SUBSTRING(#list,#index,1) = ','
BEGIN
SELECT #val = SUBSTRING(#list, #start_index, #index - #start_index )
INSERT #list_table ([val]) VALUES (#val)
SELECT #start_index = #index + 1
END
SELECT #index = #index + 1
END
SELECT #val = SUBSTRING(#list, #start_index, #index - #start_index )
INSERT #list_table ([val]) VALUES (#val)
RETURN
END
The function just splits up the input string into rows in a table with a column "val" - this means you can then pass in a comma delimited string and use it to filter another table.