SQL parse delimited string - sql

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)

Related

in SQL, how can I find duplicate string values within the same record?

Sample table
Record Number | Filter | Filters_Applied
----------------------------------------------
1 | yes | red, blue
2 | yes | green
3 | no |
4 | yes | red, red, blue
Is it possible to query all records where there are duplicate string values? For example, how could I query to pull record 4 where the string "red" appeared twice? Except in the table that I am dealing with, there are far more string values that can populate in the "filters_applied" column.
CLARIFICATION I am working out of Periscope and pulling data using SQL.
I assume that you have to check that in the logical page.
You can query the table with like '%red%'.
select Filters_Applied from table where Filters_Applied like '%red%';
You will get the data which has red at least one. Then, doing some string analysis in logic page.
In php, You can use the substr_count function to determine the number of occurrences of the string.
//the loop to load db query
while(){
$number= substr_count("Filters_Applied",red);
if($number>1){
echo "this".$Filters_Applied.">1"
}
}
for SQL-SERVER or other versions which can run these functions
Apply this logic
declare #val varchar(100) = 'yellow,blue,white,green'
DECLARE #find varchar(100) = 'green'
select #val = replace(#val,' ','') -- remove spaces
select #val;
select (len(#val)-len(replace(#val,#find,'')))/len(#find) [recurrence]
Create this Function which will parse string into rows and write query as given below. This will works for SQL Server.
CREATE FUNCTION [dbo].[StrParse]
(#delimiter CHAR(1),
#csv NTEXT)
RETURNS #tbl TABLE(Keys NVARCHAR(255))
AS
BEGIN
DECLARE #len INT
SET #len = Datalength(#csv)
IF NOT #len > 0
RETURN
DECLARE #l INT
DECLARE #m INT
SET #l = 0
SET #m = 0
DECLARE #s VARCHAR(255)
DECLARE #slen INT
WHILE #l <= #len
BEGIN
SET #l = #m + 1--current position
SET #m = Charindex(#delimiter,Substring(#csv,#l + 1,255))--next delimiter or 0
IF #m <> 0
SET #m = #m + #l
--insert #tbl(keys) values(#m)
SELECT #slen = CASE
WHEN #m = 0 THEN 255 --returns the remainder of the string
ELSE #m - #l
END --returns number of characters up to next delimiter
IF #slen > 0
BEGIN
SET #s = Substring(#csv,#l,#slen)
INSERT INTO #tbl
(Keys)
SELECT #s
END
SELECT #l = CASE
WHEN #m = 0 THEN #len + 1 --breaks the loop
ELSE #m + 1
END --sets current position to 1 after next delimiter
END
RETURN
END
GO
CREATE TABLE Table1# (RecordNumber int, [Filter] varchar(5), Filters_Applied varchar(100))
GO
INSERT INTO Table1# VALUES
(1,'yes','red, blue')
,(2,'yes','green')
,(3,'no ','')
,(4,'yes','red, red, blue')
GO
--This query will return what you are expecting
SELECT t.RecordNumber,[Filter],Filters_Applied,ltrim(rtrim(keys)), count(*)NumberOfRows
FROM Table1# t
CROSS APPLY dbo.StrParse (',', t.Filters_Applied)
GROUP BY t.RecordNumber,[Filter],Filters_Applied,ltrim(rtrim(keys)) HAVING count(*) >1
You didn't state your DBMS, but in Postgres this isn't that complicated:
select st.*
from sample_table st
join lateral (
select count(*) <> count(distinct trim(item)) as has_duplicates
from unnest(string_to_array(filters_applied,',')) as t(item)
) x on true
where x.has_duplicates;
Online example: http://rextester.com/TJUGJ44586
With the exception of string_to_array() the above is actually standard SQL

Sql Server Split a string to a table using

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.

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

T-SQL Procedure split and join on sub columns

Not really sure how to explain this so I just start with the example.
Say I have a table like this which can include several rows:
id Type Text
1 Test Failure A=123 B=444 C=43343 Error=4 ErroDes=1
I also have a static Error and ErrorDes table which look like this
Id Code Description
1 1 Error1
2 4 Error4
How can I split up the information from the column into seperate fields and also join in the info from the subtables.
Expected result would be something like this:
Type Field1 FieldA FieldB FieldC Error ErrorDes
Test Failure 123 444 43343 Error4 Error1
I used the same table for joining in the example but this is 2 tables in the db.
So to help with this I have a split function in the database.
And if I first split the Text field on "space" and then on "=" I get everything I need (or atleast all the columns in seperate rows)
cross apply dbo.Split(a.Text, ' ') s
cross apply dbo.Split(s.Value, '=') s2
I get "TokenID" and "Value" field back from the split function.
The output from that looks like this:
TokenID Value TokenID Value
1 Failure 1 Failure
2 A=123 1 A
2 A=123 2 123
3 B=444 1 B
3 B=444 2 444
4 C=43343 1 C
4 C=43343 2 43343
5 Error=4 1 Error
5 Error=4 2 4
6 ErrorDes=1 1 ErrorDes
6 ErrorDes=1 2 1
I hope you understand what I ment and can help me how this can be solved.
you can use something like the folowing UDF function to cross apply
create function udf_ReturnTextSplit(#vText varchar(100))
returns #rt table (
Field1 varchar(100),
FieldA varchar(100),
FieldB varchar(100)
) as begin
declare #st varchar(100) = #vText + ' '
declare #sti varchar(100)
declare #stj varchar(100)
insert into #rt (Field1, FieldA, FieldB) values (null, null, null)
declare #i int = charindex(' ', #st)
while #i > 0 begin
set #sti = SUBSTRING(#st, 1, #i)
set #st = substring(#st, #i + 1, 100)
set #i = CHARINDEX('=', #sti)
if #i > 0 begin
set #stj = substring(#sti, #i + 1, 100)
set #sti = substring(#sti, 1, #i - 1)
if #sti = 'A' update #rt set FieldA = #stj
if #sti = 'B' update #rt set FieldB = #stj
end else begin
update #rt set Field1 = #sti
end
set #i = charindex(' ', #st)
end
return
end
go
select * from dbo.udf_ReturnTextSplit('Failure A=123 B=444 C=43343 Error=4 ErroDes=1')

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')