I'm attempting to convert a MSSQL Query into Snowflake and I'm getting tripped up when I'm attempting to update my table using a secondary CTE after applying some business logic.
CREATE TEMPORARY TABLE test
(
id integer,
src text,
val text
);
INSERT INTO test
SELECT t.*
FROM (
VALUES (1, 'A', 'AA')
, (1, 'A', 'AB') t(id, src, val)
)
SELECT * FROM TEST
+--+---+---+
|ID|SRC|VAL|
+--+---+---+
|1 |A |AA |
|1 |A |AB |
+--+---+---+
WITH dat AS (
SELECT id, src, val
, ROW_NUMBER() OVER(ORDER BY id) rn
FROM test
)
, src AS (
SELECT id, src, val,
MD5(
ARRAY_TO_STRING(
ARRAY_CONSTRUCT(id,src,val), ':'
)
) AS CHANGE_HASH
FROM dat
WHERE rn = 1
)
MERGE INTO target as tgt using src -- <-- this throws an error
ON src.id = tgt.id
AND src.CHANGE_HASH != tgt.CHANGE_HASH
THEN UPDATE
SET (
...
)
the error I get is
[42000][1003] SQL compilation error: syntax error line 61 at position 15 unexpected '<EOF>'.
Where is my mistake?
The CTE should be in the subquery like this:
MERGE INTO target as tgt using
(
WITH dat AS (
SELECT id, src, val
, ROW_NUMBER() OVER(ORDER BY id) rn
FROM test)
, src AS (
SELECT id, src, val,
MD5(
ARRAY_TO_STRING(
ARRAY_CONSTRUCT(id,src,val), ':'
)
) AS CHANGE_HASH
FROM dat
WHERE rn = 1 )
select * from src) src
ON src.id = tgt.id
AND src.CHANGE_HASH != tgt.CHANGE_HASH
WHEN MATCHED THEN UPDATE SET tgt.src = src.src;
The CTE is no needed as the entire section could be simplified with single QUALIFY:
SELECT id, src, val,
MD5(
ARRAY_TO_STRING(
ARRAY_CONSTRUCT(id,src,val), ':'
)
) AS CHANGE_HASH
FROM dat
QUALIFY ROW_NUMBER() OVER(ORDER BY id) = 1
is the same as:
WITH dat AS (
SELECT id, src, val
, ROW_NUMBER() OVER(ORDER BY id) rn
FROM test
)
, src AS (
SELECT id, src, val,
MD5(
ARRAY_TO_STRING(
ARRAY_CONSTRUCT(id,src,val), ':'
)
) AS CHANGE_HASH
FROM dat
WHERE rn = 1
)
and MERGE becomes:
MERGE INTO target as tgt
USING (
SELECT id, src, val,
MD5(
ARRAY_TO_STRING(
ARRAY_CONSTRUCT(id,src,val), ':'
)
) AS CHANGE_HASH
FROM dat
QUALIFY ROW_NUMBER() OVER(ORDER BY id) = 1
) AS src
ON ...
...;
Related
WITH CTE AS (
SELECT
id,
c_no,
TRY_CAST(B.p_no as INTEGER) as p_no
FROM db_name.schema_name.tbl_1 A
JOIN db_name.schema_name.tbl_2 B
ON B.id_col = A.id_col
WHERE flg_col = '0'
AND cd NOT IN ('1','2','3','4')
AND DATE = '2022-02-02'
AND id='12345678'
ORDER BY p_no
)
SELECT
id,
LISTAGG (distinct c_no , ',') WITHIN GROUP (ORDER BY c_no) AS c01
FROM CTE
GROUP BY id;
When I run the above query, I'm getting result AS
Row ID C01
1 01 110,118,id_not_found,no_record
2 02 id_found
3 03 no_record
I want to display only the numberic values in C01 column in the result along with the corresponding ID. I want to ignore
these 2 values - id_not_found & no_record in the first row.
But in 2nd and 3rd, the value should remain same.
Any suggestions please.
Try to use the TRY_TO_NUMBER function:
WITH CTE AS (
SELECT
id,
c_no,
TRY_CAST(B.p_no as INTEGER) as p_no
FROM db_name.schema_name.tbl_1 A
JOIN db_name.schema_name.tbl_2 B
ON B.id_col = A.id_col
WHERE flg_col = '0'
AND cd NOT IN ('1','2','3','4')
AND DATE = '2022-02-02'
AND id='12345678'
ORDER BY p_no
)
SELECT
id,
IFNULL(LISTAGG(distinct TRY_TO_NUMBER(c_no) , ',') WITHIN GROUP(ORDER BY TRY_TO_NUMBER(c_no)), c_no) AS c01
FROM CTE
GROUP BY id;
The way you have describe it, is if "there are any number of include numbers, other all data is valid"
Using the smallest SQL that demonstrates the code and we do a little preconditioning in a CTE (which could be moved into your CTE)
WITH cte(id, c_no) AS (
SELECT * FROM VALUES
(01, '110'),
(01, '118'),
(01, 'id_not_found'),
(01, 'no_record'),
(02, 'id_found'),
(03, 'no_record')
), pre_condition AS (
SELECT *,
try_to_number(c_no) as c_no_as_num
FROM cte
)
SELECT
id,
count(c_no_as_num) as count_of_nums,
LISTAGG (distinct c_no , ',') WITHIN GROUP (ORDER BY c_no) AS c01_all,
LISTAGG (distinct c_no_as_num , ',') WITHIN GROUP (ORDER BY c_no_as_num) AS c01_nums,
IFF(count_of_nums>0, c01_nums, c01_all) AS c01
FROM pre_condition
GROUP BY id
ORDER BY id;
we get, the answer you want in C01:
ID
COUNT_OF_NUMS
C01_ALL
C01_NUMS
C01
1
2
110,118,id_not_found,no_record
110,118
110,118
2
0
id_found
id_found
3
0
no_record
no_record
so we can mash this smaller:
SELECT * FROM VALUES
(01, '110'),
(01, '118'),
(01, 'id_not_found'),
(01, 'no_record'),
(02, 'id_found'),
(03, 'no_record')
), pre_condition AS (
SELECT *,
try_to_number(c_no) as c_no_as_num
FROM cte
)
SELECT
id,
IFF(count(c_no_as_num)>0, LISTAGG (distinct c_no_as_num , ',') WITHIN GROUP (ORDER BY c_no_as_num), LISTAGG (distinct c_no , ',') WITHIN GROUP (ORDER BY c_no)) AS c01
FROM pre_condition
GROUP BY id
ORDER BY id;
and weave that into your code as:
WITH CTE AS (
SELECT
id,
c_no,
try_to_number(c_no) as c_no_as_num
TRY_CAST(B.p_no as INTEGER) as p_no
FROM db_name.schema_name.tbl_1 A
JOIN db_name.schema_name.tbl_2 B
ON B.id_col = A.id_col
WHERE flg_col = '0'
AND DATE = '2022-02-02'
AND id = '12345678'
AND cd NOT IN ('1','2','3','4')
)
SELECT
id,
IFF(count(c_no_as_num)>0
,LISTAGG (distinct c_no_as_num , ',') WITHIN GROUP (ORDER BY c_no_as_num)
,LISTAGG (distinct c_no , ',') WITHIN GROUP (ORDER BY c_no)
) AS c01
FROM cte
GROUP BY id
;
I have some data which records some sequential processing events.
Each event occurs at a location (Location) and includes both a begin date (BeginDT) and an end date (EndDT).
Processing at a Location may involve multiple sequential steps each of which is recorded with a BeginDT and EndDT.
A step which is non-final at a location should be recorded as complete (Complete as true (1)).
A step which is final at a location should be recorded with incomplete (Complete as false (0)).
However, it is possible for processing at a location to be incomplete due to some missing or erroneous records.
When processing at a location has a step (or steps) which indicate(s) incomplete but no subsequent final step to indicate complete then the incomplete record (or most recent incomplete record of the sequence at the location) is to be considered the final record.
Processing multiple times at the same location in sequence may occur.
When processing multiple times at the same location in sequence and the leading event at the location is complete then each event is to be considered an entirely separate.
I have included a method which I was trying to use to help determine how each event is to be handled by identifying the previous and next records but I am not sure if that is a useful approach.
The desired final result is the Order, Location, earliest BeginDT, and latest EndDT for each sequential set of processing events at a Location:
/*
[Order] Location BeginDT EndDT
1 A 2000-01-01 2000-01-06
2 B 2000-01-10 2000-01-11
3 C 2000-01-20 2000-01-21
4 D 2000-01-30 2000-01-31
5 A 2000-02-01 2000-02-02
6 C 2000-02-10 2000-02-11
7 C 2000-02-20 2000-02-21
8 B 2000-03-01 2000-03-02
9 D 2000-03-10 2000-03-11
10 D 2000-03-20 2000-03-23
11 E 2000-03-30 2000-03-31
*/
declare #T table (
ID int identity,
Location varchar(20),
Complete bit,
BeginDT date,
EndDT date
);
insert #T (Location, Complete, BeginDT, EndDT)
values
('A', 0, '2000-01-01', '2000-01-02'),
('A', 0, '2000-01-03', '2000-01-04'),
('A', 1, '2000-01-05', '2000-01-06'),
('B', 1, '2000-01-10', '2000-01-11'),
('C', 1, '2000-01-20', '2000-01-21'),
('D', 0, '2000-01-30', '2000-01-31'),
('A', 1, '2000-02-01', '2000-02-02'),
('C', 1, '2000-02-10', '2000-02-11'),
('C', 1, '2000-02-20', '2000-02-21'),
('B', 1, '2000-03-01', '2000-03-02'),
('D', 1, '2000-03-10', '2000-03-11'),
('D', 0, '2000-03-20', '2000-03-21'),
('D', 1, '2000-03-22', '2000-03-23'),
('E', 1, '2000-03-30', '2000-03-31');
with cte as (
select
row_number() over (order by BeginDT) as [Order],
Location,
Complete,
BeginDT,
EndDT
from #T
)
select
*
from cte
order by
[Order],
Complete;
with cte as (
select
row_number() over (order by BeginDT) as [Order],
Location,
Complete,
BeginDT,
EndDT
from #T
)
select
[Order],
Location,
BeginDT,
EndDT,
Complete,
max(Previous) as Previous,
max(cast(PreviousComplete as int)) as PreviousComplete,
max([Next]) as [Next]
from (
select
cte.[Order],
cte.Location,
cte.BeginDT,
cte.EndDT,
cte.Complete,
case
when cte.[Order] = j.[Order] + 1
then j.Location
else NULL
end as Previous,
case
when cte.[Order] = j.[Order] + 1
then j.Complete
else NULL
end as PreviousComplete,
case
when cte.[Order] = j.[Order] - 1
then j.Location
else NULL
end as [Next]
from cte
cross join (
select
[Order],
Location,
Complete
from cte
) as j
) as S
group by
[Order],
Location,
BeginDT,
EndDT,
Complete;
I developed a solution:
with
cte1 as (
select
row_number() over (order by BeginDT, EndDT) as [Order],
Location,
IsComplete,
BeginDT,
EndDT
from #T
),
cte2 as (
select
[Order],
Location,
case
when 1 <> 1
or Location <> Previous
or Previous is NULL
or (
1 = 1
and Location = Previous
and PreviousIsComplete = 1
)
then BeginDT
end as BeginDT,
case
when 1 <> 1
or IsComplete = 1
or (
IsComplete = 0
and (
1 <> 1
or Location <> [Next]
or [Next] is NULL
)
)
then EndDT
end as EndDT
from (
select
[Order],
Location,
BeginDT,
EndDT,
IsComplete,
max(Previous) as Previous,
max(cast(PreviousIsComplete as int)) as PreviousIsComplete,
max([Next]) as [Next]
from (
select
cte1.[Order],
cte1.Location,
cte1.BeginDT,
cte1.EndDT,
cte1.IsComplete,
case
when cte1.[Order] = cj1.[Order] + 1
then cj1.Location
else NULL
end as Previous,
case
when cte1.[Order] = cj1.[Order] + 1
then cj1.IsComplete
else NULL
end as PreviousIsComplete,
case
when cte1.[Order] = cj1.[Order] - 1
then cj1.Location
else NULL
end as [Next]
from cte1
cross join (
select
[Order],
Location,
IsComplete
from cte1
) as cj1
) as T1
group by
[Order],
Location,
BeginDT,
EndDT,
IsComplete
) as T2
),
cte3 as (
select
'Begin' as [Type],
row_number() over (order by [Order]) as [Order],
Location,
min(BeginDT) as DT
from cte2
where BeginDT is not NULL
group by
[Order],
Location
union
select
'End' as [Type],
row_number() over (order by [Order]) as [Order],
Location,
min(EndDT) as DT
from cte2
where EndDT is not NULL
group by
[Order],
Location
)
select
[Order],
Location,
[Begin] as BeginDT,
[End] as EndDT
from (
select
[Type],
[Order],
Location,
DT
from cte3
) as T
pivot (
max(DT) for [Type] in ([Begin], [End])
) as pT
order by [Order];
I welcome any improvements. Thanks.
I am trying to convert rows to columns from a table, I was able to get the result set but for few records I am missing the logic. Below is the table and SQL I am running, if you the result of the query for P123 PROCEDURE_END should be NULL, Instead of NULL I am getting the PROCEDURE_BEGIN value. I am not sure what I am missing here, please help me out with this.
INSERT INTO #PAT_TEST
VALUES ( 'P123', 'D457', '2015-11-29 01:48:00.000', 1 )
, ( 'P123', 'D457', NULL, 0 )
, ( 'P872', 'D457', '2015-11-30 02:48:00.000', 1 )
, ( 'P872', 'D457', '2015-11-30 03:48:00.000', 0 );
SELECT PAT_ID
, DOC_ID
, PROCEDURE_BEGIN = MIN(PROC_TIME)
, PROCEDURE_END = MAX(PROC_TIME)
FROM ( SELECT PAT_ID
, DOC_ID
, PROC_TIME
, rn = ( ROW_NUMBER() OVER ( PARTITION BY PAT_ID, DOC_ID ORDER BY PROC_TIME ) - 1 ) / 2
FROM #PAT_TEST
) a
GROUP BY PAT_ID
, DOC_ID
, a.rn
ORDER BY PAT_ID
, DOC_ID
, PROCEDURE_BEGIN;
Would this work for you:
If null should be treated as the largest value:
SELECT PAT_ID ,DOC_ID, PROCEDURE_BEGIN=MIN(ISNULL(PROC_TIME,'99991231')),
PROCEDURE_END=CASE WHEN MAX(ISNULL(PROC_TIME,'99991231')) ='99991231' THEN NULL ELSE MAX(ISNULL(PROC_TIME,'99991231')) END
FROM( SELECT PAT_ID ,DOC_ID,PROC_TIME
,rn=(ROW_NUMBER() OVER (PARTITION BY PAT_ID,DOC_ID ORDER BY PROC_TIME)-1)/2
FROM #PAT_TEST
) a
GROUP BY PAT_ID ,DOC_ID, rn
ORDER BY PAT_ID,DOC_ID, PROCEDURE_BEGIN
If null should be treated as the Smallest value:
SELECT PAT_ID ,DOC_ID, PROCEDURE_BEGIN=CASE WHEN MIN(ISNULL(PROC_TIME,0)) = '19000101' THEN NULL ELSE MIN(ISNULL(PROC_TIME,0)) END,
PROCEDURE_END=MAX(PROC_TIME )
FROM( SELECT PAT_ID ,DOC_ID,PROC_TIME
,rn=(ROW_NUMBER() OVER (PARTITION BY PAT_ID,DOC_ID ORDER BY PROC_TIME)-1)/2
FROM #PAT_TEST
) a
GROUP BY PAT_ID ,DOC_ID, rn
ORDER BY PAT_ID,DOC_ID, PROCEDURE_BEGIN
I have a table like this (SQL Server 2008):
[ATTACHMENTS].[ID]
[ATTACHMENTS].[Name]
[ATTACHMENTS].[DocumentID],
[ATTACHMENTS].[ParentAttachmentID]
If [ParentAttachmentID] is NULL or 0 - then the record is top. Otherwise - the record is a child. Every child can be (and can't be) the parent of some other child elementS.
I need to calculate "path" for every record of the table. Path is:
ParentAttachment.Path + ' > ' + Attachment.Path (ParentAttachment.Path is recursive)
I try something like this:
WITH attachments AS (
SELECT *,
[ATTACHMENTS].[Name] AS Path
FROM [ATTACHMENTS]
WHERE [ATTACHMENTS].[ParentAttachmentID] IS NULL
OR [ATTACHMENTS].[ParentAttachmentID] = 0
UNION ALL
SELECT a.*,
c.Path + ' > ' + a.[Name]
FROM [ATTACHMENTS] a
INNER JOIN attachments c
ON a.[ParentAttachmentID] = c.[ID]
)
But it doesn't work properly (Path is invalid for some elements due to duplicates as I think). Where I made a mistake? Please, help me to fix this problem.
UPD 2: CSV with data from table [ATTACHMENTS] - http://pastebin.com/WMd6HJ7j
CSV with result of recursive query: http://pastebin.com/7pqs0dx1
Thanx!
What is the error you get?
I think that you are not specifying your datatypes explicitly enough. The size of Path is the size of the anchor start point: ATTACHMENTS.Name. Which probably would be a varchar(100) or so.
Then when you keep recursively growing your path, it might run out of space. The CTE itself looks fine. I had a go at it using explicit sizes, then it work fine.
CREATE TABLE #Attachments
(
Id INT PRIMARY KEY
NOT NULL ,
ParentId INT NULL ,
Name VARCHAR(50) NOT NULL
);
-- some sample data:
-------------------------------------
-- 100 top level attachments (lvl0)
INSERT INTO #Attachments
( Id ,
ParentId ,
Name
)
SELECT A.Rnum ,
NULL ,
'A' + CAST(rnum AS VARCHAR(10))
FROM ( SELECT TOP 100
ROW_NUMBER() OVER ( ORDER BY ( SELECT
1
) ) AS Rnum
FROM sys.messages
) A;
-- attachments linked to lvl0
INSERT INTO #Attachments
( Id ,
ParentId ,
Name
)
SELECT 1000 + A.Rnum ,
A.Rnum ,
'A' + CAST(( rnum + 1000 ) AS VARCHAR(10))
FROM ( SELECT TOP 1000
ROW_NUMBER() OVER ( ORDER BY ( SELECT
1
) ) AS Rnum
FROM sys.messages
) A;
-- attachments linked to lvl1
INSERT INTO #Attachments
( Id ,
ParentId ,
Name
)
SELECT 10000 + A.Rnum ,
1000 + A.Rnum ,
'A' + CAST(( rnum + 10000 ) AS VARCHAR(10))
FROM ( SELECT TOP 1000
ROW_NUMBER() OVER ( ORDER BY ( SELECT
1
) ) AS Rnum
FROM sys.messages
) A;
-- Query:
-------------------------------------
WITH Att ( Id, Name, ParentId, Lvl, NamePath, IdPath )
AS ( SELECT P.Id ,
P.Name ,
P.ParentId ,
CAST(0 AS INT) AS Lvl ,
CAST(P.[Name] AS VARCHAR(1000)) AS NamePath ,
CAST(P.[Id] AS VARCHAR(1000)) AS IdPath
FROM #ATTACHMENTS P
WHERE P.[ParentId] IS NULL
OR P.[ParentId] = 0
UNION ALL
SELECT A.Id ,
A.Name ,
A.ParentId ,
P.Lvl + 1 ,
CAST(P.NamePath + ' > ' + A.Name AS VARCHAR(1000)) ,
CAST(P.IdPath + '\' + CAST(A.Id AS VARCHAR(10)) AS VARCHAR(1000))
FROM #ATTACHMENTS A
INNER JOIN Att P ON A.ParentId = P.Id
)
SELECT *
FROM Att;
DROP TABLE #Attachments;
You have to set different names for table and the name after WITH, and use the second name for second part of the join. Consider attachments1 in below code:
WITH attachments1 AS (
-- anchor
SELECT *,
[ATTACHMENTS].[Name] AS [Path]
FROM ATTACHMENTS
WHERE [ATTACHMENTS].[ParentAttachmentID] IS NULL
OR [ATTACHMENTS].ParentAttachmentID = 0
UNION ALL
--recursive member
SELECT a.*,
cast((c.[Path] + N' > ' + a.[Name]) as nvarchar(500)) as [Path]
FROM ATTACHMENTS a
INNER JOIN attachments1 c
ON a.ParentAttachmentID = c.[ID]
)
SELECT * FROM attachments1
You can get the Path from the recursive member.
Your statement seems correct to me:
DECLARE #t TABLE
(
Code NVARCHAR(MAX) ,
ParentCode NVARCHAR(MAX)
)
INSERT INTO #t
VALUES ( '1', NULL ),
( '2', NULL ),
( '1.1', '1' ),
( '1.1.1', '1.1' ),
( '1.1.2', '1.1' ),
( '1.2', '1' ),
( '1.2.1', '1.2' ),
( '2.1', '2' ),
( '2.2', '2' );
WITH cte
AS ( SELECT Code ,
ParentCode ,
ISNULL(Code, '') AS Path
FROM #t
WHERE ParentCode IS NULL
UNION ALL
SELECT t.Code ,
t.ParentCode ,
Path + ISNULL(' > ' + t.Code, '') AS Path
FROM #t t
JOIN cte c ON c.Code = t.ParentCode
)
SELECT * FROM cte
Output:
Code ParentCode Path
1 NULL 1
2 NULL 2
2.1 2 2 > 2.1
2.2 2 2 > 2.2
1.1 1 1 > 1.1
1.2 1 1 > 1.2
1.2.1 1.2 1 > 1.2 > 1.2.1
1.1.1 1.1 1 > 1.1 > 1.1.1
1.1.2 1.1 1 > 1.1 > 1.1.2
i have a piece of code looks like this
declare #t table (record int,string varchar(MAX))
insert into #t (record,string)values (1,'ABC')
insert into #t (record,string)values (2,'DEF/123')
insert into #t (record,string)values (3,'GHI/456/XYZ')
i got a query where i can result like this
SELECT record,
RIGHT(LEFT(T.string,Number-1),CHARINDEX('/',REVERSE(LEFT('/' + T.string,number-1))))
FROM
master..spt_values,
#t T
WHERE
Type = 'P' AND Number BETWEEN 1 AND LEN(T.string)+1
AND
(SUBSTRING(T.string,Number,1) = '/' OR SUBSTRING(T.string,Number,1) = '')
getting output
record values
1 ABC
2 DEF
2 123
3 GHI
3 456
3 XYZ
how can i get output like this
record values
1 ABC
1 NULL
1 NULL
2 DEF
2 123
2 NULL
3 GHI
3 456
3 XYZ
it has been asked by some user .i excelled upto here and from there how can i achieve desire output
The idea is to generate a rows of record cross joined to 1,2,3 to produce combination of record with another column numbered 1,2,3, then use that combination to join to your splitted values. You must add a ROW_NUMBER for your splitted values first to be able to join it with the generated combinations.
;WITH CteThree(record, N) AS(
SELECT
t.record,
x.N
FROM (
SELECT DISTINCT record FROM #t
)t
CROSS JOIN(
SELECT 1 UNION ALL SELECT 2 UNION ALL SELECT 3
)x(N)
),
CteSplitted AS(
SELECT
record,
ROW_NUMBER() OVER(PARTITION BY record ORDER BY Number) AS N,
RIGHT(LEFT(T.string,Number-1),CHARINDEX('/',REVERSE(LEFT('/' + T.string,number-1)))) AS str
FROM master..spt_values v
CROSS JOIN #t T
WHERE
Type = 'P'
AND Number BETWEEN 1 AND LEN(T.string)+1
AND (SUBSTRING(T.string,Number,1) = '/' OR SUBSTRING(T.string,Number,1) = '')
)
SELECT
t.record,
s.str
FROM CteThree t
LEFT JOIN CteSplitted s
ON s.record = t.record
AND s.N = t.N
how about this:
declare #t table (record int,string varchar(MAX));
declare #s char(1) = '/';
WITH counter as (
SELECT MAX(LEN(string)-LEN(REPLACE(string, #s, ''))) lines
) ,
splitter as (
SELECT record, string
, line = 1
, pos = h.pos
, value = CASE WHEN h.pos>0 THEN SUBSTRING(string,1,h.pos) ELSE string END
FROM #t
CROSS APPLY (SELECT CHARINDEX(#s, string) pos ) h
UNION ALL
SELECT record, string
, line = s.line + 1
, pos = CASE WHEN s.pos = 0 THEN 0 ELSE h.pos END
, value = CASE WHEN s.pos = 0 THEN null
WHEN h.pos > 0 THEN SUBSTRING(string,s.pos+1,h.pos-s.pos-1)
ELSE SUBSTRING(string,s.pos+1,99)
END
FROM splitter s
CROSS APPLY (SELECT CHARINDEX(#s, string, s.pos+1) pos ) h
WHERE s.line<=(SELECT lines FROM counter)
)
SELECT *
FROM splitter
ORDER BY record,line
try this
DECLARE #t TABLE
(
record INT ,
string VARCHAR(MAX)
)
INSERT INTO #t
( record, string )
VALUES ( 1, 'ABC' ),
( 2, 'DEF/123' ),
( 3, 'GHI/456/XYZ' );
WITH cte
AS ( SELECT Number = 1
UNION ALL
SELECT Number + 1
FROM cte
WHERE Number <= 100
),
NotNull
AS ( SELECT record ,
RIGHT(LEFT(T.string, Number - 1),
CHARINDEX('/',
REVERSE(LEFT('/' + T.string,
number - 1)))) string ,
ROW_NUMBER() OVER ( PARTITION BY T.record ORDER BY T.record ) AS RN
FROM cte
JOIN #t T ON Number <= ( LEN(T.string) + 1 )
AND SUBSTRING(T.string + '/', Number, 1) = '/'
)
SELECT template.record ,
NotNull.string
FROM ( SELECT *
FROM ( SELECT DISTINCT
RN
FROM NotNull
) AS A
CROSS JOIN ( SELECT Record
FROM NotNull
) AS B
) AS template
LEFT JOIN NotNull ON template.RN = NotNull.RN
AND template.Record = NotNull.Record
try this
declare #t table (record int,string varchar(MAX))
insert into #t (record,string)values (1,'ABC')
insert into #t (record,string)values (2,'DEF/123')
insert into #t (record,string)values (3,'GHI/456/XYZ')
declare #mx int
select #mx= len(string)-len(replace(string,'/','')) from #t
select record,t.c.value('.','varchar(max)') as col2 from
(select record,x=cast('<t>'+replace(left(string+'////////////////////',(len(string)+(#mx-(len(string)-len(replace(string,'/','')))))),'/','</t><t>') +'</t>' as xml) from #t)
a cross apply x.nodes('/t') t(c)