Sql Server Split a string to a table using - sql

I have a string : Brand=b1-b2&Vendor=v1-v2-v3&CustomField=cf1-cf2-cf3
I want to make a table..
**Name** | **Value**
------------- | ------
Brand | b1
Brand | b2
Vendor | v1
Vendor | v2
Vendor | v3
CustomField | cf1
CustomField | cf2
CustomField | cf3
Thanks for your help
Edit Note : I tried, but I do not think it's performance
CREATE PROCEDURE [dbo].[SplitTableInStr]
(
#StrParameter NVARCHAR(MAX)
)
AS
BEGIN
DECLARE #Delimiter CHAR = '&',
#Index SMALLINT,
#Start SMALLINT,
#DelSize SMALLINT;
SET #DelSize = LEN(#Delimiter);
CREATE TABLE #FilteredItems
(
[Key] NVARCHAR(400),
[Values] NVARCHAR(MAX)
)
WHILE LEN(#StrParameter) > 0
BEGIN
SET #Index = CHARINDEX(#Delimiter, #StrParameter)
IF #Index = 0
BEGIN
DECLARE #Key NVARCHAR(400) = SUBSTRING(#StrParameter, 1, CHARINDEX('=', #StrParameter) - 1);
DECLARE #Values NVARCHAR(MAX) = SUBSTRING(#StrParameter, CHARINDEX('=', #StrParameter) + 1, LEN(#StrParameter));
INSERT INTO #FilteredItems ([Key],[Values])
VALUES (#Key,#Values)
BREAK
END
ELSE
BEGIN
DECLARE #IndexItem NVARCHAR(MAX) = SUBSTRING(#StrParameter, 1, #Index - 1);
DECLARE #IndexKey NVARCHAR(400) = SUBSTRING(#IndexItem, 1, CHARINDEX('=', #IndexItem) - 1);
DECLARE #IndexValues NVARCHAR(MAX) = SUBSTRING(#IndexItem, CHARINDEX('=', #IndexItem) + 1, LEN(#IndexItem));
INSERT INTO #FilteredItems ([Key],[Values])
VALUES (#IndexKey,#IndexValues)
SET #Start = #Index + #DelSize
SET #StrParameter = SUBSTRING(#StrParameter, #Start , LEN(#StrParameter) - #Start + 1)
END
END
DECLARE #KeyBase NVARCHAR(400),
#ValueBase NVARCHAR(MAX);
CREATE TABLE #DisplayOrderTmp
(
[EndName] NVARCHAR(400),
[EndValue] NVARCHAR(400)
)
WHILE EXISTS(SELECT * From #FilteredItems)
BEGIN
SELECT TOP 1 #KeyBase = [Key], #ValueBase = [Values] FROM #FilteredItems
DECLARE #OptionDelimiter CHAR = '-',
#OptionIndex SMALLINT,
#OptionStart SMALLINT,
#OptionDelSize SMALLINT;
SET #OptionDelSize = LEN(#OptionDelimiter)
WHILE LEN(#ValueBase) > 0
BEGIN
SET #OptionIndex = CHARINDEX(#OptionDelimiter, #ValueBase)
IF #OptionIndex = 0
BEGIN
INSERT INTO #DisplayOrderTmp VALUES (#KeyBase, #ValueBase)
BREAK;
END
ELSE
BEGIN
INSERT INTO #DisplayOrderTmp VALUES (#KeyBase, SUBSTRING(#ValueBase, 1, CHARINDEX(#OptionDelimiter, #ValueBase) - 1))
SET #OptionStart = #OptionIndex + #OptionDelSize
SET #ValueBase = SUBSTRING(#ValueBase, #OptionStart , LEN(#ValueBase) - #OptionStart + 1)
END
END
DELETE #FilteredItems WHERE [Key] = #KeyBase
END
SELECT * FROM #DisplayOrderTmp;
END
EXEC dbo.[SplitTableInStr] 'Brand=b1-b2&Vendor=v1-v2-v3&CustomField=cf1-cf2-cf3'

using a CSV Splitter table valued function by Jeff Moden with cross apply()
declare #s nvarchar(4000) = 'Brand=b1-b2&Vendor=v1-v2-v3&CustomField=cf1-cf2-cf3';
select
Name = left(s.Item,charindex('=',s.Item)-1)
, Value = i.Item
from dbo.DelimitedSplitN4k(#s,'&') s
cross apply dbo.DelimitedSplitN4k(replace(s.Item,'=','-'),'-') i
where i.ItemNumber > 1
rextester demo: http://rextester.com/RTAT28301
returns:
+-------------+------+
| Name | Item |
+-------------+------+
| Brand | b1 |
| Brand | b2 |
| Vendor | v1 |
| Vendor | v2 |
| Vendor | v3 |
| CustomField | cf1 |
| CustomField | cf2 |
| CustomField | cf3 |
+-------------+------+
splitting strings reference:
Tally OH! An Improved SQL 8K “CSV Splitter” Function - Jeff Moden
Splitting Strings : A Follow-Up - Aaron Bertrand
Split strings the right way – or the next best way - Aaron Bertrand
string_split() in SQL Server 2016 : Follow-Up #1 - Aaron Bertrand

I think you should get the best performance with CLR function (in C#) for this purpose:
https://msdn.microsoft.com/en-us/library/ms131103.aspx
You might manually split the input or create compiled Regex with named groups to parse input values.

Related

Display letters instead of string numbers in SQL

How can I write a query that prints in response to letters by giving row ID and size number?
Example:
ID = 4000
NAME = 38/39/40/41/42/43/44
My Input :
ID = 4000
NAME = 40
Result = A/B/C/D/E/F/G
40 = C
Please try the following solution.
SQL
-- DDL and sample data population, start
DECLARE #tbl AS TABLE (ID INT PRIMARY KEY, tokens VARCHAR(MAX));
INSERT INTO #tbl (ID, tokens) VALUES
(2000, '44/46/48/50/52/54'),
(4000, '38/39/40/41/42/43/44');
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = '/'
, #alphabet VARCHAR(50) = 'ABCDEFGHIJKLMNOPQRSTUVWXYZ'
, #ID INT = 4000
, #token VARCHAR(20) = '40';
SELECT *
, Result = IIF(pos > 0, SUBSTRING(#alphabet,pos,1),NULL)
FROM #tbl AS t
CROSS APPLY (SELECT TRY_CAST('<root><r>' +
REPLACE(tokens, #separator, '</r><r>') +
'</r></root>' AS XML)
.query('
for $x in /root/r
let $pos := count(/root/r[. << $x[1]]) + 1
return if ($x/text()=sql:variable("#token")) then $pos
else ()
').value('.', 'INT')) AS t1(pos)
WHERE ID = #ID;
Output
+------+----------------------+-----+--------+
| ID | tokens | pos | Result |
+------+----------------------+-----+--------+
| 4000 | 38/39/40/41/42/43/44 | 3 | C |
+------+----------------------+-----+--------+

Table Value Function, Select Multiple Rows

I have a table value function which splits up strings by a common delimiter and outputs them into a table with the following structure :
#ValueLookup TABLE
(
Value nvarchar(100),
ValueIndex int
)
I'm using this mostly to split combination ID values, such as 1234-5678 :
dbo.SplitString('1234-5678', '-')
Currently, I'm using two SELECT's to get both values once they are split, along with converting them to integers :
DECLARE #FirstID INT
DECLARE #SecondID INT
SELECT
#FirstID = CONVERT(INT, Value)
FROM dbo.SplitString('1234-5678', '-')
WHERE ValueIndex = 1
SELECT
#SecondID = CONVERT(INT, Value)
FROM dbo.SplitString('1234-5678', '-')
WHERE ValueIndex = 2
Is there a way I could get both values and assign them in a single SELECT statement?
declare #FirstId int, #SecondId int;
select
#FirstID = convert(int,min(case when ValueIndex = 1 then Value end))
, #SecondID = convert(int,min(case when ValueIndex = 2 then Value end))
from dbo.SplitString('1234-5678', '-')
select
FirstId = #FirstId
, SecondId = #SecondId
rextester demo: http://rextester.com/TRTDI68038
returns:
+---------+----------+
| FirstId | SecondId |
+---------+----------+
| 1234 | 5678 |
+---------+----------+
Demo was done using a CSV Splitter table valued function by Jeff Moden with the function name and output columns renamed.
Splitting strings reference:
Tally OH! An Improved SQL 8K “CSV Splitter” Function - Jeff Moden
Splitting Strings : A Follow-Up - Aaron Bertrand
Split strings the right way – or the next best way - Aaron Bertrand
string_split() in SQL Server 2016 : Follow-Up #1 - Aaron Bertrand
Ordinal workaround for **string_split()** - Solomon Rutzky
Yet another option
Example
Declare #String varchar(max) = '1234-5678'
Declare #FirstID INT
Declare #SecondID INT
Select #FirstID = xDim.value('/x[1]','int')
,#SecondID = xDim.value('/x[2]','int')
From (Select Cast('<x>' + replace(#String,'-','</x><x>')+'</x>' as xml) as xDim) as A

Concatenate champ name1

I have a table with champ: Letter1, Letter2, Letter3 ... until 6.
Structure of my table:
TYPEID | BenefitsID | Letter1 | Coef1 | Letter2 | Letter3 | Coef3 | Letter4 | Coef4 | Letter5 | Coef5 | Letter6 | Coef6 | Resultat
I want to make a select request in WHILE loop like this :
#count =1;
While (#count<7)
begin
#letter = **(select letter+#count from ....)**
..
end
The result I want is to select the value of Letter1, Letter2... and Coef1, Coef2 ... and multiply them to insert them in champ Resultat
My question is how to select Letter1, Letter2, ... in one request. Will the bold request works? Or should I use something else?
I used SQL server 2008..
Assuming you are using MySQL, do you want something like this?
select concat(letter, (#count := #count + 1)
from t cross join
(select #count := 0) params
order by ??
limit 7;
As I commented, numerated columns are good indication for a bad design.
Being that said, dynamic SQL will answer your need.
declare #count int = 1
,#stmt nvarchar(max)
While (#count<7)
begin
set #stmt = 'select Letter' + cast(#count as varchar(10)) + ' from mytable'
exec sp_executesql #stmt
set #count = #count + 1
end

How can I add a character into a specified position into string in SQL SERVER?

I have a varchar field like:
195500
122222200
I need to change these values to:
1955.00
1222222.00
try this
Declare #s varchar(50) = '1234567812333445'
Select Stuff(#s, Len(#s)-1, 0, '.')
--> 12345678123334.45
fiddle demo
Query:
SELECT col,
LEFT(col,len(col)-2) + '.' + RIGHT(col,2) as newcol
FROM Table1
Result:
| COL | NEWCOL |
|-----------|------------|
| 195500 | 1955.00 |
| 122222200 | 1222222.00 |
If you want to add a '.' before the last two digits of your values you can do:
SELECT substring(code,0,len(code)-1)+'.'+substring(code,len(code)-1,len(code))
FROM table1;
sqlfiddle demo
Please try:
select
Col,
REVERSE(STUFF(REVERSE(Col), 1, 2, LEFT(REVERSE(Col), 2)+'.'))
from YourTable
SQL Fiddle Demo
CREATE TABLE #T ( Value VARCHAR(20) )
INSERT INTO #T ( Value ) VALUES ( 195500 ), ( 122222200)
SELECT
Value
, NewValue = CONVERT(DECIMAL(17,2),CONVERT(DECIMAL,Value) / 100)
FROM #T
| Value | NewValue |
|-----------|------------|
| 195500 | 1955.00 |
| 122222200 | 1222222.00 |
Please Try :
select reverse(stuff(reverse(columnName),3,0,'.') ) from yourTable
Ran into something similar and came up with this, could probably change this to a function/SP to make it reusable. Scenario faced was inserting a specified character at different positions within the string, for a certain number of times.
/*
--- passed in string or column, N'C4CB6B22250B'
--- desired output, C4:CB:6B:22:25:0D
--- Test values
--- declare #strToChange varchar(50) = N'C4:CB:6B:22:25:0B'
--- declare #strToChange varchar(50) = N'C4CB6B22250B'
*/
declare #strToChange varchar(50) = N'C4CB6B22250B'
IF(SELECT LEN(#strToChange) - LEN(REPLACE(#strToChange, ':', ''))) > 0
BEGIN
---returns count of specified char
SELECT LEN(#strToChange) - LEN(REPLACE(#strToChange, ':', ''))
END
ELSE
BEGIN
declare #charCount int = 5; --# of times to insert the char
declare #shiftPosition int = 2; -- inital insertion shift
While(#charCount > 0)
begin
SET #strToChange = LEFT(#strToChange,len(#strToChange)- #shiftPosition) + ':' + RIGHT(#strToChange,#shiftPosition)
SET #charCount = #charCount - 1 --decrement charCount for each added char
SET #shiftPosition = #shiftPosition + 3 ---increment shift position by 3 for the new char and the chars that were already there
end
SELECT #strToChange
END
Please see the following code. You can choose the symbols and index in variable.
declare #index int,#sym varchar(10)
set #sym='#'
set #index=2
select left(195500,#index) +''+#sym+''+right(195500,len(195500)-#index)
declare #a varchar(10) = 'aaa'
select concat(#a,'.00')

SQL parse delimited string

I can't find a good way to do this and because of some restrictions it has to be a coded without the use of variables but I can call a function. Anyway, I need to return a result set from a Select query and one of the rows is a pipe delimited string.
So, it will return something like:
id| Name | Message |
---------------------
1 | Some Name | 'Here is my | delimited | message'
And I need it to be
id| Name | Message1 | Message2 | Message3
------------------------------------------------------
1 | Some Name | 'Here is my' | 'delimited' | 'message'
I was thinking of something like Parsename('','|',1), where you can pass in the delimiter instead of it always being a period but I don't know the best way to accomplish that.
EDIT: I've tried variations of this but of course it is very confusing. There could be 4 or more |
SELECT Field1,
Field2 AS Originalvalue,
--1
SUBSTRING(Field2,0,CHARINDEX('|',Field2)) AS Msg1,
--2
LEFT(SUBSTRING(Field2,CHARINDEX('|',Field2)+1 ,LEN(Field2)),CHARINDEX('|',SUBSTRING(Field2,CHARINDEX('|',Field2)+1 ,LEN(Field2)))-1)
AS ExtractedValue
FROM Table1 T1 JOIN Table2 T2
ON T1.Id = T2.Id
WHERE T1.Id = 12
You can write a sql function as follows.
create Function dbo.fn_Parsename(#Message Varchar(1000), #delimiter char(1), #index int )
Returns Varchar(1000)
As
Begin
Declare
#curIndex int = 0,
#pos int = 1,
#prevPos int = 0,
#result varchar(1000)
while #pos > 0
Begin
set #pos = CHARINDEX(#delimiter, #Message, #prevPos);
if(#pos > 0)
begin-- get the chars between the prev position to next delimiter pos
set #result = SUBSTRING(#message, #prevPos, #pos-#prevPos)
end
else
begin--get last delim message
set #result = SUBSTRING(#message, #prevPos, LEN(#message))
end
if(#index = #curIndex)
begin
return #result
end
set #prevPos = #pos + 1
set #curIndex = #curIndex + 1;
end
return ''--not found
End
And you can call it as below:
select dbo.fn_Parsename('Here is my | delimited | message','|', 0)
select dbo.fn_Parsename('Here is my | delimited | message','|', 1)
select dbo.fn_Parsename('Here is my | delimited | message','|', 2)