I have the following cte expression which I am try to run in a dynamic sql but I am getting invalid identifiers error in it:
DECLARE #t TABLE ( ID INT, V float, D DATE )
INSERT INTO #t
VALUES ( 1, 1.2, '2014-01-01' ),
( 1, 1.33, '2014-01-02' ),
( 1, 1.33, '2014-01-03' ),
( 1, 7, '2014-01-04' ),
( 2, 5, '2014-01-04' ),
( 2, 8, '2014-01-10' ),
( 2, 11, '2014-01-05' );
DECLARE #DealClauseString nvarchar(max)
SET #DealClauseString =';WITH filter
AS ( SELECT ID ,
D ,
V ,
ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY D DESC ) AS RN
FROM #t where id =1
),
cte
AS ( SELECT ID ,
D ,
V ,
MIN(D) OVER ( PARTITION BY ID ORDER BY D ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS Min ,
MAX(D) OVER ( PARTITION BY ID ORDER BY D ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS Max
FROM filter
WHERE RN <= 2
)
SELECT c1.ID ,
c2.V - c1.V AS V
FROM cte c1
JOIN cte c2 ON c1.ID = c2.ID AND c1.D < c2.D
WHERE ( c1.D = c1.MIN OR c1.D = c1.MAX )
AND ( c2.D = c2.MIN OR c2.D = c2.MAX ) '
exec #DealClauseString
Any suggestions?
Also I was going to run this cte expression in a while loop. Are there any performance issue running cte expression in a loop?
Firstly, you can't use a table variable with dynamic sql, so you should use a #temp table instead, although this might just be relevant to your example code.
Secondly, if you are using dynamic sql, you either need to place the variable #DealClauseString in brackets or use sp_executesql, otherwise SQL Server assumes that you're calling a stored procedure.
CREATE TABLE #t ( ID INT, V FLOAT, D DATE )
INSERT INTO #t
VALUES ( 1, 1.2, '2014-01-01' ),
( 1, 1.33, '2014-01-02' ),
( 1, 1.33, '2014-01-03' ),
( 1, 7, '2014-01-04' ),
( 2, 5, '2014-01-04' ),
( 2, 8, '2014-01-10' ),
( 2, 11, '2014-01-05' );
DECLARE #DealClauseString NVARCHAR(MAX)
SET #DealClauseString = ';WITH filter
AS ( SELECT ID ,
D ,
V ,
ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY D DESC ) AS RN
FROM #t where id =1
),
cte
AS ( SELECT ID ,
D ,
V ,
MIN(D) OVER ( PARTITION BY ID ORDER BY D ROWS
BETWEEN UNBOUNDED PRECEDING
AND UNBOUNDED FOLLOWING ) AS Min ,
MAX(D) OVER ( PARTITION BY ID ORDER BY D ROWS
BETWEEN UNBOUNDED PRECEDING AND
UNBOUNDED FOLLOWING ) AS Max
FROM filter
WHERE RN <= 2
)
SELECT c1.ID ,
c2.V - c1.V AS V
FROM cte c1
JOIN cte c2 ON c1.ID = c2.ID AND c1.D < c2.D
WHERE ( c1.D = c1.MIN OR c1.D = c1.MAX )
AND ( c2.D = c2.MIN OR c2.D = c2.MAX ) '
EXEC (#DealClauseString)
Results:
ID V
1 5.67
Related
Really need some help: I have a SQL Server table like this :
And I want to do a query to count value everyday start from 06:30 today until 06:29 tomorrow.
The problem is, the value does not start from 0 every 06:30.
I need query like this: Select maximum value per code first, ex :
(code : value)
M12 : 108,
M77 : 26
then the M77 value not start from 06:30.
I need to do : 26 - 12 (value before 06:30:00), so the M77 now is 14.
Finally sum the M77 and M12 = 14 + 108 = 122.
My expected output only for total value per date.
Based on my DB (image), the total value will be 26 - 12 =14.
26 is the latest value.
12 is value before 06:30
How to do it in SQL Server? Please help me
if i do understand you requirement correctly.
The query explanation are in code comments
; with
-- for getting the max value by code, rn = 1 is the max value
cte1 as
(
select *, rn = row_number() over (partition by code order by value desc)
from sample_table
),
-- for getting rows before 6:30, rn = 1 is the last record before 6:30
cte2 as
(
select *, rn = row_number() over (partition by code order by [date] desc)
from sample_table
where convert(time, [date]) < '06:30'
)
select total = sum(case when convert(time, c1.[date]) < '06:30'
then c1.value
else c1.value - c2.value
end)
from cte1 c1
left join cte2 c2 on c1.code = c2.code
and c1.rn = c2.rn
where c1.rn = 1
Try this
SELECT SUM([output]) AS [output]
FROM (
SELECT [latest]
,[before]
,(CASE WHEN [latest] = [before] THEN [latest]
ELSE [latest] - [before] END) AS [output]
FROM (
SELECT [code]
,MAX([value]) AS [latest]
,(SELECT MAX([value])
FROM [table]
WHERE [date] < CONVERT(datetime, CONVERT(varchar(10), CONVERT(date, [date])) + ' 06:30:00:000')
AND [code] = [t].[code]
GROUP BY [code]) AS [before]
FROM [table] AS [t]
GROUP BY [code]) AS [src]
) AS [rpt]
Try this puzzle:
CREATE TABLE TestTable
(
[date] datetime,
value int,
code varchar(10)
)
GO
INSERT INTO [dbo].[TestTable]
([date]
,[value]
,[code])
VALUES
('2018-09-13 06:20:52.803'
,100
,'M12'),
('2018-09-13 06:21:52.803'
,102
,'M12')
, ('2018-09-13 06:22:52.803'
,104
,'M12')
, ('2018-09-13 06:23:52.803'
,106
,'M12')
, ('2018-09-13 06:24:52.803'
,108
,'M12')
, ('2018-09-13 06:25:52.803'
,2
,'M77')
, ('2018-09-13 06:29:14.803'
,4
,'M77')
, ('2018-09-13 06:29:16.803'
,6
,'M77')
, ('2018-09-13 06:29:18.803'
,8
,'M77')
, ('2018-09-13 06:29:45.803'
,10
,'M77')
, ('2018-09-13 06:29:55.803'
,12
,'M77')
, ('2018-09-13 06:30:18.803'
,14
,'M77')
, ('2018-09-13 06:31:18.803'
,26
,'M77')
;WITH RESULT AS (
SELECT
TT.code
, MaxValueThatday = max(maxval.MAXVALUE )
, MaxValueBefore630NextDay = max(MAXValBefore630.MAXVALUE)
, ResultSubstraction =
CASE WHEN max(maxval.MAXVALUE ) <> max(MAXValBefore630.MAXVALUE)
THEN max(maxval.MAXVALUE ) - max(MAXValBefore630.MAXVALUE)
ELSE max(maxval.MAXVALUE )
END
FROM [dbo].[TestTable] TT
OUTER APPLY(
SELECT max(VALUE) MAXVALUE
, code
FROM [dbo].[TestTable] aa
WHERE Aa.code = tt.code
group by code
)maxval
OUTER APPLY(
SELECT max(A.VALUE) MAXVALUE
, code
FROM [dbo].[TestTable] A
WHERE DATEPART(HOUR,[DATE]) <= 6 AND DATEPART(MINUTE,[DATE]) < 30
and A.code = tt.code
group by code
)MAXValBefore630
where ( [DATE] > DATEADD(MINUTE,390,CAST({ fn CURDATE()} AS DATETIME) ) ) --6:30 today
group by tt.code
)
SELECT SUM(ResultSubstraction)
FROM RESULT
I am looking to find a solution to this problem. I have a table called LogEntry that stores information used by multiple offices, where they have to log any visitors that come in to their office on any given day. If no visitors come in, they are still required to log "No Visitors" for the day. How do I run a query that pulls all dates where an office failed to create even a "No Visitors" log?
I've looked at this question (and the article linked within), but even adapting that query, I'm only able to create a blank row for a date where an office is missing an entry for a date, not specify the actual office that did not create an entry. Is there a way to do what I'm trying to do?
declare #temp table (
CDate datetime,
loc_id varchar(50)
)
insert into #temp SELECT DISTINCT entryDate, locationID FROM LogEntry WHERE entryDate >= '05/01/2017' AND entryDate <= '07-31-2017'
;with d(date) as (
select cast('05/01/2017' as datetime)
union all
select date+1
from d
where date < '07/31/2017'
)
select DISTINCT t.loc_id, CONVERT(date, d.date)
FROM d LEFT OUTER JOIN #temp t ON d.date = t.CDate
GROUP BY t.loc_id, d.date
ORDER BY t.loc_id
As I said, this query returns me a list of dates in the date range, and all locations that submitted entries on that date, but I'd like to find a way to extract essentially the opposite information: if an office (specified by locationID) did not submit an entry on a given day, return only those locationIDs and the dates that they missed.
Sample data
EntryID | locationID | entryDate
=================================
1 1 07-01-2017
2 1 07-02-2017
3 2 07-02-2017
4 1 07-04-2017
Expected Result (for date range of 07-01 to 07-04)
locationID | missedEntryDate
============================
1 07-03-2017
2 07-01-2017
2 07-03-2017
2 07-04-2017
Your first step was good, you create a list of all dates, but you also need a list of all locations. Then you create a cross join to have all combinations and then you perform the left join to find out what is missing.
;with allDates(date) as (
select cast('05/01/2017' as datetime)
union all
select date+1
from d
where date < '07/31/2017'
), allLocations as (
SELECT DISTINCT loc_id
FROM #temp
), allCombinations as (
SELECT date, loc_id
FROM allDates
CROSS JOIN allLocations
)
SELECT AC.loc_id, AC.date
FROM allCombinations AC
LEFT JOIN #temp t
ON AC.date = t.CDate
AND AC.loc_id = t.loc_id
WHERE t.loc_id IS NULL -- didnt find a match on #temp
If your dataset is not too large you can try this:
select t.loc_id, CONVERT(date, d.date)
FROM d
-- Cross join dates to all available locs
CROSS JOIN (SELECT DISTINCT loc_id FROM #temp ) AS Locs
LEFT JOIN
( SELECT loc_id, t.CDate
FROM #temp
GROUP BY loc_id, d.date ) AS t ON d.date = t.CDate AND Locs.loc_id = t.loc_id
ORDER BY Locs.loc_id
This should be a bit faster:
;WITH cte AS (
SELECT a.LocID, RangeStart.CDate, ( CASE WHEN Input.LocID IS NULL THEN 1 ELSE 0 END ) AS IsMissing
FROM ( SELECT DISTINCT LocID FROM #temp ) AS a
CROSS JOIN ( SELECT CONVERT( DATETIME, '2017-05-01' ) AS CDate ) AS RangeStart
LEFT JOIN
( SELECT LocID, MIN( CDate ) AS CDate
FROM #temp
WHERE CDate = '2017-05-01'
GROUP BY LocID ) AS Input ON a.LocID = Input.LocID AND RangeStart.CDate = Input.CDate
UNION ALL
SELECT a.LocID, a.CDate + 1 AS CDate,
ISNULL( ItExists, 0 ) AS IsMissing
FROM cte AS a
OUTER APPLY( SELECT LocID, 1 AS ItExists FROM #temp AS b WHERE a.LocID = b.LocID AND a.CDate + 1 = b.CDate ) AS c
WHERE a.CDate < '2017-07-01'
)
SELECT * FROM cte OPTION( MAXRECURSION 0 )
You can also add an index:
CREATE INDEX IX_tmp_LocID_CDate ON #temp( LocID, CDate )
Sample data set for the second query:
CREATE TABLE #temp( LocID VARCHAR( 50 ), CDate DATETIME )
INSERT INTO #temp
VALUES
( '1', '2017-05-01' ), ( '1', '2017-05-02' ), ( '1', '2017-05-03' ), ( '1', '2017-05-04' ), ( '1', '2017-05-05' ),
( '2', '2017-05-01' ), ( '2', '2017-05-02' ), ( '2', '2017-05-03' ), ( '2', '2017-05-04' ), ( '2', '2017-05-05' )
;WITH d AS (
SELECT CAST( '05/01/2017' AS DATETIME ) AS date
UNION ALL
SELECT date + 2
FROM d
WHERE date < '2018-07-31'
)
INSERT INTO #temp
SELECT LocID, d.date
FROM ( SELECT DISTINCT LocID FROM #temp ) AS a
CROSS JOIN d
OPTION( MAXRECURSION 0 )
My current understanding is that it is impossible to have several aggregates - such as an additional SUM(Measure2) in the following example:
IF OBJECT_ID('tempdb..#Dim1') IS NOT NULL DROP TABLE #Dim1
IF OBJECT_ID('tempdb..#Dim2') IS NOT NULL DROP TABLE #Dim2
IF OBJECT_ID('tempdb..#Facts') IS NOT NULL DROP TABLE #Facts
CREATE TABLE #Dim1
(
Id INT IDENTITY(1,1),
DimName NVARCHAR(100)
)
CREATE TABLE #Dim2
(
Id INT IDENTITY(1,1),
DimName NVARCHAR(100)
)
CREATE TABLE #Facts
(
Id INT IDENTITY(1,1),
Dim1Id INT,
Dim2Id INT,
Measure1 FLOAT,
Measure2 FLOAT
)
INSERT INTO #Dim1
SELECT N'Dim1Name1'
UNION ALL
SELECT N'Dim1Name2'
UNION ALL
SELECT N'Dim1Name3'
INSERT INTO #Dim2
SELECT N'Dim2Name1'
UNION ALL
SELECT N'Dim2Name2'
INSERT INTO #Facts
SELECT 1, 2, 2, 10
UNION ALL
SELECT 1, 2, 10, 3
UNION ALL
SELECT 1, 1, 1, 56
UNION ALL
SELECT 2, 1, 5, 4
UNION ALL
SELECT 2, 2, 4, 4
UNION ALL
SELECT 3, 1, 4, 1
UNION ALL
SELECT 3, 1, 20, 56
;WITH CTE1 AS
(
SELECT
Facts.Measure1,
Dimensions1.DimName AS DimName1,
Dimensions2.DimName AS DimName2
FROM #Facts AS Facts
INNER JOIN #Dim1 AS Dimensions1 ON Facts.Dim1Id = Dimensions1.Id
INNER JOIN #Dim2 AS Dimensions2 ON Facts.Dim2Id = Dimensions2.Id
)
SELECT
*
FROM CTE1
PIVOT
(
SUM(Measure1)
FOR DimName2
IN([Dim2Name1], [Dim2Name2])
) AS X;
Is this really the case and do I have to use the old 'MAX CASE' approach for these scenarios?
Is this helpful?
;WITH CTE1 AS
(
SELECT
Facts.Measure1,
Facts.Measure2,
Dimensions1.DimName AS DimName1,
Dimensions2.DimName AS DimName2
FROM #Facts AS Facts
INNER JOIN #Dim1 AS Dimensions1 ON Facts.Dim1Id = Dimensions1.Id
INNER JOIN #Dim2 AS Dimensions2 ON Facts.Dim2Id = Dimensions2.Id
)
SELECT
DimName1,
CAST(SUBSTRING(X.[Dim2Name1], 1, 10) AS INT) Dim2Name1Measure1,
CAST(SUBSTRING(X.[Dim2Name1], 11, 10) AS INT) Dim2Name1Measure2,
CAST(SUBSTRING(X.[Dim2Name2], 1, 10) AS INT) Dim2Name2Measure1,
CAST(SUBSTRING(X.[Dim2Name2], 11, 10) AS INT) Dim2Name2Measure2
FROM
(
SELECT
CAST(SUM(Measure1) AS CHAR(10)) + CAST(SUM(Measure2) AS CHAR(10)) AS Combined,
DimName1,
DimName2
FROM CTE1
GROUP BY
DimName1,
DimName2
) S
PIVOT
(
MAX(Combined)
FOR DimName2
IN([Dim2Name1], [Dim2Name2])
) AS X;
My table looks something like
deal_id test_value run_date
820117648 1.2 2014-03-31
820117648 1.33 2014-04-30
820117648 1.33 2014-05-30
820117648 1.26 2014-06-30
820117648 1.11 2014-07-31
820117648 0.58 2014-09-30
820117648 1.64 2014-10-31
820117648 0.64 2014-11-28
820117648 3.65 2014-12-31
820117648 3.8 2015-03-11
820117649 0.64 2014-09-31
820117649 0.23 2014-10-31
820117649 0.64 2014-11-28
820117649 3.65 2014-12-31
820117649 3.8 2015-03-11
SELECT deal_id,test_value,run_date FROM ems.cdotests
WHERE run_date >= Dateadd(month, -4 Getdate())
I am trying to grab the last 4 run_date record from the whole set and then
I need to find the difference between the testValue on the first rundate of my select criteria and the last rundate i.e for deal_id 820117649 3.8 - 0.23 = 3.57 and for deal_id 820117648 it should be 3.8 -1.64 = 2.16
This is the large table with multiple deal id and several run_date associated with each dealid and may have data for the last 15 years or so
Any suggestion would be really helpfull
Try this solution. It will not be bound to particular deal_id.
DECLARE #t TABLE ( ID INT, V MONEY, D DATE )
INSERT INTO #t
VALUES ( 1, 1, '20140101' ),
( 1, 4, '20140102' ),
( 1, 2, '20140103' ),
( 1, 7, '20140104' ),
( 2, 5, '20140104' ),
( 2, 8, '20140110' ),
( 2, 11, '20140105' );
WITH cte
AS ( SELECT ID ,
D ,
V ,
MIN(D) OVER ( PARTITION BY ID ORDER BY D ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS Min ,
MAX(D) OVER ( PARTITION BY ID ORDER BY D ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS Max
FROM #t
)
SELECT c1.ID ,
c2.V - c1.V AS V
FROM cte c1
JOIN cte c2 ON c1.ID = c2.ID AND c1.D < c2.D
WHERE ( c1.D = c1.MIN OR c1.D = c1.MAX ) AND ( c2.D = c2.MIN OR c2.D = c2.MAX )
Output:
ID V
1 6.00
2 3.00
You have changed you question. For latest 6 month, add filter CTE above main CTE:
;WITH filter
AS ( SELECT ID ,
D ,
V ,
ROW_NUMBER() OVER ( PARTITION BY ID ORDER BY D DESC ) AS RN
FROM #t
),
cte
AS ( SELECT ID ,
D ,
V ,
MIN(D) OVER ( PARTITION BY ID ORDER BY D ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS Min ,
MAX(D) OVER ( PARTITION BY ID ORDER BY D ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING ) AS Max
FROM filter
WHERE RN <= 6
)
SELECT c1.ID ,
c2.V - c1.V AS V
FROM cte c1
JOIN cte c2 ON c1.ID = c2.ID AND c1.D < c2.D
WHERE ( c1.D = c1.MIN OR c1.D = c1.MAX )
AND ( c2.D = c2.MIN OR c2.D = c2.MAX )
AND (c2.V - c1.V > 5)
You can use window functions
WITH C AS(
SELECT ROW_NUMBER() OVER (PARTITION BY deal_Id ORDER BY deal_Id) Rn
,deal_Id
,test_value
,run_date
FROM ems.cdotests
)
SELECT id1 deal_id, t2 - t1 diff
FROM (SELECT c1.deal_id id1
,c1.test_value t1
,c2.deal_id id2
, c2.test_value t2
FROM C c1
INNER JOIN C c2 ON c1.deal_id = c2.deal_id
WHERE c1.Rn = 1
AND c2.Rn = (SELECT TOP 1 MAX(Rn) FROM C GROUP BY deal_id)
) t
So late to answer, but a way is this:
;with t as (
select *,
row_number() over (partition by deal_id order by run_date desc) seq
from ems.cdotests
)
select
t1.deal_id, t1.run_date run_date1, t1.test_value test_value1,
t2.run_date run_date4, t2.test_value test_value4,
t1.test_value - t2.test_value diff
from t t1
join t t2
on t1.deal_id = t2.deal_id
where t1.seq = 1
and t2.seq = 4
-- as an extra condition of those should have at least 5 records:
and exists (select 1 from t ti where ti.deal_id = t1.deal_id and ti.seq > 5)
[SQL Fiddle]
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)