Split into 3 characters in Sql Server - sql-server

I have a string value in db. I need to insert a , in between each 3 characters. Here is the example string.
Sample String
111100120125 // This sample is a dynamic string. So It can be less or more ..
Result
Val = 111,100,120,125
Please tell me about the builtin SqlServer function to get the , seperated string as a result.
Any help to this will be appreciated.
Thanks.

Please tell me about the builtin SqlServer function to get the ,
seperated string as a result.
You can use Stuff() function. Fiddle demo specific to your given string.
Declare #S varchar(50) = '111100120125'
SELECT STUFF(STUFF(STUFF(#S,4,0,','),8,0,','),12,0,',')
111,100,120,125
EDIT: More generic solution would be to create a function like below (Note that this function starts inserting separator value from end):
CREATE FUNCTION dbo.AddSeparator (#String VARCHAR(max), #Separator VARCHAR(1))
RETURNS VARCHAR(max)
AS
BEGIN
Declare #Length int = Len(#String)
WHILE (#Length > 3)
BEGIN
SELECT #String = STUFF(#String, #Length - 2, 0, #Separator),
#Length = #Length -3
END
RETURN #String
END;
Usage and fiddle demo :
SELECT String Original, dbo.AddSeparator(String, ',') Modified
FROM T
Results:
| ORIGINAL | MODIFIED |
|----------|-----------|
| | |
| 1 | 1 |
| 12 | 12 |
| 123 | 123 |
| 1234 | 1,234 |
| 12345 | 12,345 |
| 123456 | 123,456 |
| 1234567 | 1,234,567 |

You can use recursive CTE:
declare #f varchar(100)='111100120125'
;with pos as(
select 3 n, cast(SUBSTRING(#f,1,3) as varchar(max)) a
union all
select n+3 n, a+','+ SUBSTRING(#f,n+1,3) from pos
where n<LEN(#f)
)
select max(#f) ini, max(a) res from pos;

Related

using REGEXP_EXTRACT to gather data

P2_PCM_C_L112_2011_00_1v
P2_PCM_L212_2012_00_1v
P2_PCM_L119_2011_00_1v
P2_ABB_C_L6712_2012_00_1v
P2_PCM_L17612_2014_00_1v
I would like to gather information like PCM or ABB then the year so 2011 from the above using REGXP_EXTRACT
Could you suggest the code for this
Please try the following solution.
It is based on XQuery. It allows us to tokenize the input string without using REGEX.
Even dynamic structure of tokens is not an issue:
Code (PCM or ABB) is a 2nd token.
Year position is dynamic (4 or 5), but it is always a 3rd token from the right.
At the end, we have a relational/rectangular resultset so we can easily query/filter it via WHERE clause.
SQL
-- DDL and sample data population, start
DECLARE #tbl TABLE (tokens VARCHAR(8000));
INSERT INTO #tbl (tokens) VALUES
('P2_PCM_C_L112_2011_00_1v'),
('P2_PCM_L212_2012_00_1v'),
('P2_PCM_L119_2011_00_1v'),
('P2_ABB_C_L6712_2012_00_1v'),
('P2_PCM_L17612_2014_00_1v');
-- DDL and sample data population, end
DECLARE #separator CHAR(1) = '_';
WITH rs AS
(
SELECT *
, code = c.value('(/root/r[2]/text())[1]', 'CHAR(3)')
, token_year = c.value('(/root/r[last() - 2]/text())[1]', 'INT')
FROM #tbl
CROSS APPLY (SELECT TRY_CAST('<root><r><![CDATA[' +
REPLACE(tokens, #separator, ']]></r><r><![CDATA[') +
']]></r></root>' AS XML)) AS t(c)
)
SELECT * FROM rs
--WHERE code = ... AND token_year = ...;
Output
+----+---------------------------+------+------------+
| ID | tokens | code | token_year |
+----+---------------------------+------+------------+
| 1 | P2_PCM_C_L112_2011_00_1v | PCM | 2011 |
| 2 | P2_PCM_L212_2012_00_1v | PCM | 2012 |
| 3 | P2_PCM_L119_2011_00_1v | PCM | 2011 |
| 4 | P2_ABB_C_L6712_2012_00_1v | ABB | 2012 |
| 5 | P2_PCM_L17612_2014_00_1v | PCM | 2014 |
+----+---------------------------+------+------------+

Insert comma separated values with some constant value in SQL Server

I have one comma separated strings like "1,2,3,4,5,6,7" and I want to insert this values in a table column with other values are constant like
Insert into tbl(value1, value2, value3) values(#v1, #v2, 1)
Insert into tbl(value1, value2, value3) values(#v1, #v2, 2)
Insert into tbl(value1, value2, value3) values(#v1, #v2, 3) etc.
Where #v1,#v2 values are always constant.
So how should I write the query?
In SQL Server 2016+ you can use string_split().
In SQL Server pre-2016, using a CSV Splitter table valued function by Jeff Moden:
create table tbl (value1 int, value2 int, value3 int);
declare #v1 int = 0;
declare #v2 int = -1;
declare #var varchar(8000) = '1,2,3,4,5,6,7';
insert into tbl
select #v1, #v2, s.Item
from delimitedsplit8K(#var, ',') s
select * from tbl;
rextester demo: http://rextester.com/GBVJS38200
returns:
+--------+--------+--------+
| value1 | value2 | value3 |
+--------+--------+--------+
| 0 | -1 | 1 |
| 0 | -1 | 2 |
| 0 | -1 | 3 |
| 0 | -1 | 4 |
| 0 | -1 | 5 |
| 0 | -1 | 6 |
| 0 | -1 | 7 |
+--------+--------+--------+
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
In case you need an in-line version
Example
Declare #v1 varchar(25) = 'SomeValue'
Declare #v2 varchar(25) = 'OtherValue'
Declare #S varchar(max)= '1,2,3,4,5,6,7'
Insert Into tbl (value1, value2, value3)
Select #v1,#v2,RetVal
From (
Select RetSeq = Row_Number() over (Order By (Select null))
,RetVal = LTrim(RTrim(B.i.value('(./text())[1]', 'varchar(max)')))
From (Select x = Cast('<x>' + replace((Select replace(#S,',','§§Split§§') as [*] For XML Path('')),'§§Split§§','</x><x>')+'</x>' as xml).query('.')) as A
Cross Apply x.nodes('x') AS B(i)
) A
The Row Inserted Look Like This

Scalar function occurring request timeout issue in SQL Server while using in stored procedure

This is salar fuction . I am using this in Stored procedure with muliple parameters. For small results it is good but for big data it gets time out and also taking a long execuition time. Please share some other alternate or some enhancements
ALTER FUNCTION [dbo].[FNGETMULIPLEASSIGNESS_NEW2]
(
#TIMELINEID INT,
#MILSTONEID INT,
#TASKID INT
)
RETURNS varchar(max)
AS
BEGIN
DECLARE #Assignees varchar(max)='', #isExists bit=0
if(#TASKID=0)
BEGIN
Select #Assignees = #Assignees+ FIRSTNAME +' ' + LASTNAME+', '
FROM CASETIMELINEPEOPLE
INNER JOIN USERDETAIL ON
CASETIMELINEPEOPLE.PEOPLEUSERID=USERDETAIL.USERID
WHERE (CASETIMELINEID= #TIMELINEID) AND
(TEMPLATEMILESTONEID=#MILSTONEID) AND
(TEMPLATETASKID is null) and CASETIMELINEPEOPLE.isdeleted=0
END
else
BEGIN
Select #Assignees = #Assignees+ FIRSTNAME +' ' + LASTNAME+','
FROM CASETIMELINEPEOPLE
INNER JOIN USERDETAIL ON
CASETIMELINEPEOPLE.PEOPLEUSERID=USERDETAIL.USERID
WHERE (CASETIMELINEID= #TIMELINEID) AND
(TEMPLATEMILESTONEID=#MILSTONEID) AND
(TEMPLATETASKID=#TASKID) and CASETIMELINEPEOPLE.isdeleted=0
END
SELECT #Assignees=SUBSTRING(#Assignees, 0,LEN(#Assignees))
RETURN #Assignees
END
Using an inline table valued function will improve performance.
Reference:
When is a SQL function not a function? "If it’s not inline, it’s rubbish." - Rob Farley
Inline Scalar Functions - Itzik Ben-Gan
Scalar functions, inlining, and performance: An entertaining title for a boring post - Adam Machanic
TSQL User-Defined Functions: Ten Questions You Were Too Shy To Ask - Robert Sheldon
Here is an inline table valued function version of your scalar function that uses the stuff() with select ... for xml path ('') method of string concatenation.:
create function dbo.fn_get_multiple_assigness_itvf (
#timelineid int
, #milstoneid int
, #taskid int
) returns table as return (
select Assignees = stuff((
select ',' + firstname + ' ' + lastname
from casetimelinepeople ctp
inner join userdetail ud
on ctp.peopleuserid=ud.userid
where casetimelineid = #timelineid
and templatemilestoneid = #milstoneid
and (templatetaskid = #taskid
or (#taskid = 0 and templatetaskid is null)
)
and ctp.isdeleted=0
for xml path (''), type).value('.','nvarchar(max)')
,1,1,'')
)
go
rextester demo: http://rextester.com/UZTJS46485
test setup:
create table casetimelinepeople (
casetimelineid int
, peopleuserid int
, templatemilestoneid int
, templatetaskid int
, isdeleted bit not null default 0
);
insert into casetimelinepeople values
(1,1,1,null,0)
,(1,2,1,null,0)
,(1,3,1,null,0)
,(1,2,1,1,0)
,(1,3,1,1,0)
create table userdetail (
userid int not null
, firstname varchar(32) not null
, lastname varchar(32) not null);
insert into userdetail values
(1, 'Some', 'One')
,(2, 'Avinash', 'Raikwar')
,(3, 'Sql','Zim');
go
And querying the inline table valued function like so:
select *
from dbo.fn_get_multiple_assigness_itvf(1,1,0)
returns
+----------------------------------+
| Assignees |
+----------------------------------+
| Some One,Avinash Raikwar,Sql Zim |
+----------------------------------+
select *
from dbo.fn_get_multiple_assigness_itvf(1,1,1)
returns:
+-------------------------+
| Assignees |
+-------------------------+
| Avinash Raikwar,Sql Zim |
+-------------------------+
Using cross apply() to call the function for each row in a query:
select *
from casetimelinepeople ctp
cross apply dbo.fn_get_multiple_assigness_itvf(
ctp.casetimelineid
, ctp.templatemilestoneid
, ctp.templatetaskid
) x
returns:
+----------------+--------------+---------------------+----------------+-----------+----------------------------------+
| casetimelineid | peopleuserid | templatemilestoneid | templatetaskid | isdeleted | Assignees |
+----------------+--------------+---------------------+----------------+-----------+----------------------------------+
| 1 | 1 | 1 | NULL | False | Some One,Avinash Raikwar,Sql Zim |
| 1 | 2 | 1 | NULL | False | Some One,Avinash Raikwar,Sql Zim |
| 1 | 3 | 1 | NULL | False | Some One,Avinash Raikwar,Sql Zim |
| 1 | 2 | 1 | 1 | False | Avinash Raikwar,Sql Zim |
| 1 | 3 | 1 | 1 | False | Avinash Raikwar,Sql Zim |
+----------------+--------------+---------------------+----------------+-----------+----------------------------------+

cursor to get the desired result

I have table like this:
x------x---------x----------------------x-------x
| Id | ACCTNO | name2 | QTY |
x------x---------x----------------------x-------x
| 1 | 1 | here is 2014-01-13 | 10 |
| 1 | 2 | there are 2014-01-12 | 20 |
| 2 | 3 | 2014-01-08 | 40 |
| 2 | 4 | 2014-01-06 | 30 |
x------x---------x----------------------x-------x
I'm trying to get the records where English alphabets are there along with date.
Im trying to do this like:
DECLARE #ACCTNO as INT;
DECLARE #name2 as varchar(max);
declare #st varchar(max)
DECLARE #BusinessCursor as CURSOR;
SET #BusinessCursor = CURSOR FOR
SELECT ACCTNO, Name2
FROM DPBENCHA2;
OPEN #BusinessCursor;
FETCH NEXT FROM #BusinessCursor INTO #ACCTNO, #name2;
--PRINT #Name2;
WHILE ##FETCH_STATUS = 0
BEGIN
set #st = #Name2;
select SUBSTRING(#st, patindex('%[0-9][0-9]/[0-9][0-9]/[0-9][0-9]%', #st), 50)
PRINT #st;
FETCH NEXT FROM #BusinessCursor INTO #ACCTNO,#st;
END
CLOSE #BusinessCursor;
DEALLOCATE #BusinessCursor;
Here instead of getting the records that contains English alphabet along with date. I'm getting all records. How to resolve this? How can I get the records only for account number 1 and 2? Is there any other ways to do it.
I'm trying to get the records where English alphabets are there along with date.
You seem to want a simple query:
SELECT ACCTNO, Name2
FROM DPBENCHA2
WHERE name2 LIKE '%[a-zA-Z]%';
This is much simpler than a cursor. In general, you want to avoid cursors in SQL.
EDIT:
If you want to extract the date from Name2:
SELECT ACCTNO, Name2,
SUBSTRING(name2,
PATINDEX('%[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]%'),
10) as thedate
FROM DPBENCHA2
WHERE name2 LIKE '%[a-zA-Z]%';
Use as following
SUBSTRING(#st, patindex('%[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]%', #st), 50)
you can use a simple select, assuming your date format is yyyy-dd-mm or yyyy-mm-dd
select * from DPBENCHA2 where (name2 like '%[a-z]%')
and name2 like '% [0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]%'
note the space in '%<space>[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]%'
this will ensure that 'some alphabet 22323-12-34' will not be included in your select

SSIS data manipulation

I am currently using SSIS to read the data from a table, modify a column and inset it into a new table.
The modification I want to perform will occur if a previously read row has an identical value in a particular column.
My original idea was to use a c# script with a dictionary containing previously read values and a count of how many times it has been seen.
My problem is that I cannot save a dictionary as an SSIS variable. Is it possible to save a C# variable inside an SSIS script component? or is there another method I could use to accomplish this.
As an example, the data below
/--------------------------------\
| Unique Column | To be modified |
|--------------------------------|
| X5FG | 0 |
| QFJD | 0 |
| X5FG | 0 |
| X5FG | 0 |
| DFHG | 0 |
| DDFB | 0 |
| DDFB | 0 |
will be transformed into
/--------------------------------\
| Unique Column | To be modified |
|--------------------------------|
| X5FG | 0 |
| QFJD | 0 |
| X5FG | 1 |
| X5FG | 2 |
| DFHG | 0 |
| DDFB | 0 |
| DDFB | 1 |
Rather than use a cursor, just use a set based statment
Assuming SQL 2005+ or Oracle, use the ROW_NUMBER function in your source query like so. What's important to note is the PARTITION BY defines your group/when the numbers restart. The ORDER BY clause directs the order in which the numbers are applied (most recent mod date, oldest first, highest salary, etc)
SELECT
D.*
, ROW_NUMBER() OVER (PARTITION BY D.unique_column ORDER BY D.unique_column ) -1 AS keeper
FROM
(
SELECT 'X5FG'
UNION ALL SELECT 'QFJD'
UNION ALL SELECT 'X5FG'
UNION ALL SELECT 'X5FG'
UNION ALL SELECT 'DFHG'
UNION ALL SELECT 'DDFB'
UNION ALL SELECT 'DDFB'
) D (unique_column)
Results
unique_column keeper
DDFB 0
DDFB 1
DFHG 0
QFJD 0
X5FG 0
X5FG 1
X5FG 2
You can create a script component. When given the choice, select the row transformation (instead of source or destination).
In the script, you can create a global variable that you will update in the process row method.
Perhaps SSIS isn't the solution for this one task. Using a cursor with a table-valued variable you would be able to accomplish the same result. I'm not a fan of cursors in most situation, but when you need to iterate through data that depends on previous iterations or is self-reliant then it can be useful. Here's an example:
DECLARE
#value varchar(4)
,#count int
DECLARE #dictionary TABLE ( value varchar(4), count int )
DECLARE cur CURSOR FOR
(SELECT UniqueColumn FROM SourceTable s)
OPEN cur;
FETCH NEXT FROM cur INTO #value;
WHILE ##FETCH_STATUS = 0
BEGIN
DECLARE #innerCount int = 0
IF NOT EXISTS (SELECT 1 FROM #dictionary WHERE value = #value)
BEGIN
INSERT INTO #dictionary ( value, count )
VALUES( #value, 0 )
END
ELSE
BEGIN
SET #innerCount = (SELECT count + 1 FROM #dictionary WHERE value = #value)
UPDATE #dictionary
SET count = #innerCount
WHERE value = #value
END
INSERT INTO TargetTable ( value, count )
VALUES (#value, #innerCount)
FETCH NEXT FROM cur INTO #value;
END

Resources