How to shred a XML being passed into a stored procedure? - sql-server

I have an XML being passed into a stored procedure in the following format. I'm struggling with how to shred it in SQL Server 2017:
declare #xml xml = convert(xml, N'<SearchQuery>
<DealTypeDesc>Deal</DealTypeDesc>
<VendorNum>1</VendorNum>
<VendorName>Vendor1</VendorName>
<VendorNum>2</VendorNum>
<VendorName>Vendor2</VendorName>
<VendorNum>3</VendorNum>
<VendorName>Vendor3</VendorName>
<VendorNum>4</VendorNum>
<VendorName>Vendor4</VendorName>
<VendorNum>5</VendorNum>
<VendorName>Vendor5</VendorName>
</SearchQuery>')
-- this is how it is being consumed now. 1 element at a time
SELECT
t.c.value('text()[1]', 'NVARCHAR(MAX)') AS LookupValue
FROM
#xml.nodes('//SearchQuery/VendorNum') AS t(c)
-- I want the results to look like this where I can use the node name as a column and the value as a lookup.
-- I wanted to do this in one pass without having to union all and select from the XML variable every time.
-- I can't change the XML structure since it's coming from a third party, so that option is out.
SELECT
'VendorName' ColumnName,
t.c.value('text()[1]', 'NVARCHAR(MAX)') AS LookupValue
FROM
#xml.nodes('//SearchQuery/VendorName') AS t(c)
UNION ALL
SELECT
'VendorNum' ColumnName,
t.c.value('text()[1]', 'NVARCHAR(MAX)') AS LookupValue
FROM
#xml.nodes('//SearchQuery/VendorNum') AS t(c)

Two options here. The first is a simple query. The second is a helper function which will shred virtually any XML without having to know anything about it. I'll often use this during the Discovery Phase.
First Option
Select ColumnName = a.value('local-name(.)','varchar(100)')
,LookupValue = a.value('.','varchar(max)')
From #XML.nodes('SearchQuery') as C1(n)
Cross Apply C1.n.nodes('*') as C2(a)
Where a.value('local-name(.)','varchar(100)') like 'Vendor%'
Returns
ColumnName LookupValue
VendorNum 1
VendorName Vendor1
VendorNum 2
VendorName Vendor2
VendorNum 3
VendorName Vendor3
VendorNum 4
VendorName Vendor4
VendorNum 5
VendorName Vendor5
The Second Option
Select * from [dbo].[tvf-XML-Hier](#xml) Order by R1
Returns
Perhaps more than you want, but it is easy to trim down
The TVF if Interested
CREATE FUNCTION [dbo].[tvf-XML-Hier](#XML xml)
Returns Table
As Return
with cte0 as (
Select Lvl = 1
,ID = Cast(1 as int)
,Pt = Cast(NULL as int)
,Element = x.value('local-name(.)','varchar(150)')
,Attribute = cast('' as varchar(150))
,Value = x.value('text()[1]','varchar(max)')
,XPath = cast(concat(x.value('local-name(.)','varchar(max)'),'[' ,cast(Row_Number() Over(Order By (Select 1)) as int),']') as varchar(max))
,Seq = cast(1000000+Row_Number() over(Order By (Select 1)) as varchar(max))
,AttData = x.query('.')
,XMLData = x.query('*')
From #XML.nodes('/*') a(x)
Union All
Select Lvl = p.Lvl + 1
,ID = Cast( (Lvl + 1) * 1024 + (Row_Number() Over(Order By (Select 1)) * 2) as int ) * 10
,Pt = p.ID
,Element = c.value('local-name(.)','varchar(150)')
,Attribute = cast('' as varchar(150))
,Value = cast( c.value('text()[1]','varchar(max)') as varchar(max) )
,XPath = cast(concat(p.XPath,'/',c.value('local-name(.)','varchar(max)'),'[',cast(Row_Number() Over(PARTITION BY c.value('local-name(.)','varchar(max)') Order By (Select 1)) as int),']') as varchar(max) )
,Seq = cast(concat(p.Seq,' ',10000000+Cast( (Lvl + 1) * 1024 + (Row_Number() Over(Order By (Select 1)) * 2) as int ) * 10) as varchar(max))
,AttData = c.query('.')
,XMLData = c.query('*')
From cte0 p
Cross Apply p.XMLData.nodes('*') b(c)
)
, cte1 as (
Select R1 = Row_Number() over (Order By Seq),A.*
From (
Select Lvl,ID,Pt,Element,Attribute,Value,XPath,Seq From cte0
Union All
Select Lvl = p.Lvl+1
,ID = p.ID + Row_Number() over (Order By (Select NULL))
,Pt = p.ID
,Element = p.Element
,Attribute = x.value('local-name(.)','varchar(150)')
,Value = x.value('.','varchar(max)')
,XPath = p.XPath + '/#' + x.value('local-name(.)','varchar(max)')
,Seq = cast(concat(p.Seq,' ',10000000+p.ID + Row_Number() over (Order By (Select NULL)) ) as varchar(max))
From cte0 p
Cross Apply AttData.nodes('/*/#*') a(x)
) A
)
Select A.R1
,R2 = IsNull((Select max(R1) From cte1 Where Seq Like A.Seq+'%'),A.R1)
,A.Lvl
,A.ID
,A.Pt
,A.Element
,A.Attribute
,A.XPath
,Title = Replicate('|---',Lvl-1)+Element+IIF(Attribute='','','#'+Attribute)
,A.Value
From cte1 A
/*
Source: http://beyondrelational.com/modules/2/blogs/28/posts/10495/xquery-lab-58-select-from-xml.aspx
Declare #XML xml='<person><firstname preferred="Annie" nickname="BeBe">Annabelle</firstname><lastname>Smith</lastname></person>'
Select * from [dbo].[tvf-XML-Hier](#XML) Order by R1
*/

You could query the two columns separately then join as follows:
select VendorNum, VendorName
from
(
SELECT
n = ROW_NUMBER() OVER (Order by t.vendor.value('text()[1]','NVARCHAR(MAX)')),
VendorNum= t.vendor.value('text()[1]','NVARCHAR(MAX)')
FROM #xml.nodes('SearchQuery/VendorNum') AS t(vendor)
) N
join
(
SELECT
n = ROW_NUMBER() OVER (Order by t.vendor.value('text()[1]','NVARCHAR(MAX)')),
VendorName = t.vendor.value('text()[1]','NVARCHAR(MAX)')
FROM #xml.nodes('SearchQuery/VendorName') AS t(vendor)
) V on V.n =N.n

Related

Converting highly complex XML structure to table

I have a stored procedure that spits out two columns. One is an ID and the other column is a highly complex XML format for that ID. My requirement is to convert this XML to table format. The XML has 300 nodes and sub nodes. The structure looks like:
<Main>
<Version>1.0</Version>
<CId>459876569</CId>
<Overview>
<Type>Y</Type>
<CreateDate>20180505</CreateDate>
<PlanType>A</PlanType>
<EffectiveDate>20171201</EffectiveDate>
<EndDate>20181130</EndDate>
<Comments>No other comments</Comments>
</Overview>
<EssentialInfo>
<ContactI>
<LastName>Doe</LastName>
<MiddleName>A</MiddleName>
<FirstName>John</FirstName>
<DateOfBirth>19500808</DateOfBirth>
<Gender>F</Gender>
<Address>
<AddressLine1>dfsfsdf</AddressLine1>
<AddressLine2>dsfsdfa</AddressLine2>
<City>gdfgdfg</City>
</Address>
<HomePhone>98745632148</HomePhone>
</Contact>
</EssentialInfo>
</Main>
I am aware of the OPENXML method but naming the 300 columns make it tedious. Any other method to resolve this?
I am trying to implement Workflow suggestion - XML & SQL this functionality and to do so I am trying to convert my huge XML to SQL table
If open to a TVF as a helper function, and assuming the XML is to be pivoted to ONE record.
Clearly the fully declared SQL would be more performant.
Example
Declare #XML xml ='
<Main>
<Version>1.0</Version>
<CId>459876569</CId>
<Overview>
<Type>Y</Type>
<CreateDate>20180505</CreateDate>
<PlanType>A</PlanType>
<EffectiveDate>20171201</EffectiveDate>
<EndDate>20181130</EndDate>
<Comments>No other comments</Comments>
</Overview>
<EssentialInfo>
<Contact>
<LastName>Doe</LastName>
<MiddleName>A</MiddleName>
<FirstName>John</FirstName>
<DateOfBirth>19500808</DateOfBirth>
<Gender>F</Gender>
<Address>
<AddressLine1>dfsfsdf</AddressLine1>
<AddressLine2>dsfsdfa</AddressLine2>
<City>gdfgdfg</City>
</Address>
<HomePhone>98745632148</HomePhone>
</Contact>
</EssentialInfo>
</Main>
'
Select * Into #Temp from [dbo].[tvf-XML-Hier](#XML) Order by R1
Declare #SQL varchar(max) = '
Select *
From (
Select Item = concat(Element,IIF(Attribute='''','''',''_''+Attribute))
,Value
From #Temp
) A
Pivot (max([Value]) For [Item] in (' + Stuff((Select ','+QuoteName(concat(Element,IIF(Attribute='','','_'+Attribute)))
From #Temp
Where Value is not null
Order by R1
For XML Path('')),1,1,'') + ') ) p'
Exec(#SQL);
Returns
The TVF if Interested
ALTER FUNCTION [dbo].[tvf-XML-Hier](#XML xml)
Returns Table
As Return
with cte0 as (
Select Lvl = 1
,ID = Cast(1 as int)
,Pt = Cast(NULL as int)
,Element = x.value('local-name(.)','varchar(150)')
,Attribute = cast('' as varchar(150))
,Value = x.value('text()[1]','varchar(max)')
,XPath = cast(concat(x.value('local-name(.)','varchar(max)'),'[' ,cast(Row_Number() Over(Order By (Select 1)) as int),']') as varchar(max))
,Seq = cast(1000000+Row_Number() over(Order By (Select 1)) as varchar(max))
,AttData = x.query('.')
,XMLData = x.query('*')
From #XML.nodes('/*') a(x)
Union All
Select Lvl = p.Lvl + 1
,ID = Cast( (Lvl + 1) * 1024 + (Row_Number() Over(Order By (Select 1)) * 2) as int ) * 10
,Pt = p.ID
,Element = c.value('local-name(.)','varchar(150)')
,Attribute = cast('' as varchar(150))
,Value = cast( c.value('text()[1]','varchar(max)') as varchar(max) )
,XPath = cast(concat(p.XPath,'/',c.value('local-name(.)','varchar(max)'),'[',cast(Row_Number() Over(PARTITION BY c.value('local-name(.)','varchar(max)') Order By (Select 1)) as int),']') as varchar(max) )
,Seq = cast(concat(p.Seq,' ',10000000+Cast( (Lvl + 1) * 1024 + (Row_Number() Over(Order By (Select 1)) * 2) as int ) * 10) as varchar(max))
,AttData = c.query('.')
,XMLData = c.query('*')
From cte0 p
Cross Apply p.XMLData.nodes('*') b(c)
)
, cte1 as (
Select R1 = Row_Number() over (Order By Seq),A.*
From (
Select Lvl,ID,Pt,Element,Attribute,Value,XPath,Seq From cte0
Union All
Select Lvl = p.Lvl+1
,ID = p.ID + Row_Number() over (Order By (Select NULL))
,Pt = p.ID
,Element = p.Element
,Attribute = x.value('local-name(.)','varchar(150)')
,Value = x.value('.','varchar(max)')
,XPath = p.XPath + '/#' + x.value('local-name(.)','varchar(max)')
,Seq = cast(concat(p.Seq,' ',10000000+p.ID + Row_Number() over (Order By (Select NULL)) ) as varchar(max))
From cte0 p
Cross Apply AttData.nodes('/*/#*') a(x)
) A
)
Select A.R1
,R2 = IsNull((Select max(R1) From cte1 Where Seq Like A.Seq+'%'),A.R1)
,A.Lvl
,A.ID
,A.Pt
,A.Element
,A.Attribute
,A.XPath
,Title = Replicate('|---',Lvl-1)+Element+IIF(Attribute='','','#'+Attribute)
,A.Value
From cte1 A
/*
Source: http://beyondrelational.com/modules/2/blogs/28/posts/10495/xquery-lab-58-select-from-xml.aspx
Declare #XML xml='<person><firstname preferred="Annie" nickname="BeBe">Annabelle</firstname><lastname>Smith</lastname></person>'
Select * from [dbo].[tvf-XML-Hier](#XML) Order by R1
*/
If it helps with the Visualization, the TVF returns
Clearly this can be thinned down if you don't need all the columns

Getting OpenXML to display structure

I know OpenXML will parse an XML statement and return results in a table view. However, is there a way to get OpenXML to display its structure and what fields are available in each? It would certainly be helpful for very complex XML files to help decode them.
As an example:
/ROOT/Customer/
-- LastName
-- FirstName
-- SSN
/ROOT/Customer/Order
-- OrderID
/Root/Customer/Order/Details
-- Item ID
-- Item QTY
If open to a TVF, consider the following:
The original source was http://beyondrelational.com/modules/2/blogs/28/posts/10495/xquery-lab-58-select-from-xml.aspx I just made a few teaks.
Example
Declare #XML xml='<person><firstname preferred="Annie" nickname="BeBe">Annabelle</firstname><lastname>Smith</lastname></person>'
Select * from [dbo].[tvf-XML-Hier](#XML) Order by R1
Returns
The UDF if Interested
CREATE FUNCTION [dbo].[tvf-XML-Hier](#XML xml)
Returns Table
As Return
with cte0 as (
Select Lvl = 1
,ID = Cast(1 as int)
,Pt = Cast(NULL as int)
,Element = x.value('local-name(.)','varchar(150)')
,Attribute = cast('' as varchar(150))
,Value = x.value('text()[1]','varchar(max)')
,XPath = cast(concat(x.value('local-name(.)','varchar(max)'),'[' ,cast(Row_Number() Over(Order By (Select 1)) as int),']') as varchar(max))
,Seq = cast(1000000+Row_Number() over(Order By (Select 1)) as varchar(max))
,AttData = x.query('.')
,XMLData = x.query('*')
From #XML.nodes('/*') a(x)
Union All
Select Lvl = p.Lvl + 1
,ID = Cast( (Lvl + 1) * 1024 + (Row_Number() Over(Order By (Select 1)) * 2) as int ) * 10
,Pt = p.ID
,Element = c.value('local-name(.)','varchar(150)')
,Attribute = cast('' as varchar(150))
,Value = cast( c.value('text()[1]','varchar(max)') as varchar(max) )
,XPath = cast(concat(p.XPath,'/',c.value('local-name(.)','varchar(max)'),'[',cast(Row_Number() Over(PARTITION BY c.value('local-name(.)','varchar(max)') Order By (Select 1)) as int),']') as varchar(max) )
,Seq = cast(concat(p.Seq,' ',10000000+Cast( (Lvl + 1) * 1024 + (Row_Number() Over(Order By (Select 1)) * 2) as int ) * 10) as varchar(max))
,AttData = c.query('.')
,XMLData = c.query('*')
From cte0 p
Cross Apply p.XMLData.nodes('*') b(c)
)
, cte1 as (
Select R1 = Row_Number() over (Order By Seq),A.*
From (
Select Lvl,ID,Pt,Element,Attribute,Value,XPath,Seq From cte0
Union All
Select Lvl = p.Lvl+1
,ID = p.ID + Row_Number() over (Order By (Select NULL))
,Pt = p.ID
,Element = p.Element
,Attribute = x.value('local-name(.)','varchar(150)')
,Value = x.value('.','varchar(max)')
,XPath = p.XPath + '/#' + x.value('local-name(.)','varchar(max)')
,Seq = cast(concat(p.Seq,' ',10000000+p.ID + Row_Number() over (Order By (Select NULL)) ) as varchar(max))
From cte0 p
Cross Apply AttData.nodes('/*/#*') a(x)
) A
)
Select A.R1
,R2 = IsNull((Select max(R1) From cte1 Where Seq Like A.Seq+'%'),A.R1)
,A.Lvl
,A.ID
,A.Pt
,A.Element
,A.Attribute
,A.XPath
,Title = Replicate('|---',Lvl-1)+Element+IIF(Attribute='','','#'+Attribute)
,A.Value
From cte1 A

how shall i transpose rows into new columns only if duplicates appears in rows?

I have a table like this:
I need to get output like this:
I tried to use transposing, but I am not getting someone please give idea to do this.
Creation script for table:
CREATE TABLE #T1 (
itemid int,
pack int,
UOM nvarchar(2)
)
INSERT INTO #T1 VALUES
(1,1,'EA'),
(1,10,'BX'),
(1,100,'CA'),
(2,1,'EA'),
(2,10,'RL')
If you need simple, fixed column solution, you can use:
SELECT * INTO #temp FROM
(VALUES
(1, 1, 'EA'),
(1, 10, 'BX'),
(1, 100, 'CA'),
(2, 1, 'EA'),
(2, 10, 'RL')) T(ItemId, Pack, UOM)
SELECT ItemId, MAX([1]) Pack1, MAX([U1]) UOM1, MAX([2]) Pack1, MAX([U2]) UOM1, MAX([3]) Pack1, MAX([U3]) UOM1 FROM
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ItemId ORDER BY Pack) PackNum,
'U'+CONVERT(varchar(10), ROW_NUMBER() OVER (PARTITION BY ItemId ORDER BY Pack)) UOMNum
FROM #temp
) T
PIVOT
(
MAX(Pack) FOR PackNum IN ([1], [2], [3])
) PPack
PIVOT
(
MAX(UOM) FOR UOMNum IN ([U1],[U2],[U3])
) PUOM
GROUP BY ItemId
If number of columns is not fixed, you must do it dynamically (see #gofr1) or:
DECLARE #count TABLE(N varchar(10))
INSERT #count
SELECT CONVERT(varchar(10), ROW_NUMBER() OVER (ORDER BY (SELECT 1)))
FROM #temp WHERE ItemId =
(
SELECT TOP 1 ItemId
FROM #temp
GROUP BY ItemId
ORDER BY COUNT(*) DESC
)
DECLARE #select varchar(MAX) = STUFF((SELECT ', MAX(['+N+']) Pack'+N+', MAX([U'+N+']) UOM'+N FROM #count FOR XML PATH('')), 1, 2, '')
DECLARE #pivot1 varchar(MAX) = STUFF((SELECT ', ['+N+']' FROM #count FOR XML PATH('')), 1, 2, '')
DECLARE #pivot2 varchar(MAX) = STUFF((SELECT ', [U'+N+']' FROM #count FOR XML PATH('')), 1, 2, '')
DECLARE #sql varchar(MAX) = '
SELECT ItemId, '+#select+' FROM
(
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ItemId ORDER BY Pack) PackNum,
''U''+CONVERT(varchar(10), ROW_NUMBER() OVER (PARTITION BY ItemId ORDER BY Pack)) UOMNum
FROM #temp
) T
PIVOT
(
MAX(Pack) FOR PackNum IN ('+#pivot1+')
) PPack
PIVOT
(
MAX(UOM) FOR UOMNum IN ('+#pivot2+')
) PUOM
GROUP BY ItemId'
EXEC(#sql)
Dynamic SQL and some pivoting:
DECLARE #sql nvarchar(max),
#columns nvarchar(max)
SELECT #columns = (
SELECT DISTINCT ','+QUOTENAME('pack' + cast(ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY itemid) as nvarchar(max)))+','+
QUOTENAME('UOM'+ cast(ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY itemid) as nvarchar(max)))
FROM #T1
FOR XML PATH('')
)
SELECT #sql = N'
SELECT *
FROM (
SELECT itemid,
Items+rn as Items,
[Values]
FROM (
SELECT itemid,
CAST(UOM as nvarchar(max)) as UOM,
CAST(pack as nvarchar(max)) as pack,
CAST(ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY (SELECT NULL)) as nvarchar(max)) as rn
FROM #T1
) as t
UNPIVOT (
[Values] FOR Items in ([UOM],[pack])
) as up
) t1
PIVOT (
MAX([Values]) FOR Items IN ('+STUFF(#columns,1,1,'')+')
) as pvt'
EXEC sp_executesql #sql
Output:
itemid pack1 UOM1 pack2 UOM2 pack3 UOM3
1 1 EA 10 BX 100 CA
2 1 EA 10 RL NULL NULL
EDIT#1
There is one more way:
DECLARE #sql nvarchar(max),
#columns nvarchar(max),
#colsrn nvarchar(max)
;WITH cte AS (
SELECT DISTINCT cast(ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY itemid) as nvarchar(max)) as rn
FROM #T1
)
SELECT #columns = (
SELECT ',MAX('+QUOTENAME('pack' + rn) +') as '+ QUOTENAME('pack' + rn) +',MAX('+
QUOTENAME('UOM'+ rn) +') as ' + QUOTENAME('UOM'+ rn)
FROM cte
FOR XML PATH('')
),
#colsrn = (
SELECT DISTINCT ',CASE WHEN rn = ' + rn +' THEN '+QUOTENAME('pack')+' ELSE NULL END as [pack'+rn+'],'
+'CASE WHEN rn = ' + rn +' THEN '+QUOTENAME('UOM')+' ELSE NULL END as [UOM'+rn+']'
FROM cte
FOR XML PATH('')
)
SELECT #sql = N'
SELECT itemid'+#columns+'
FROM (
SELECT itemid'+#colsrn+'
FROM (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY itemid ORDER BY (SELECT NULL)) as rn
FROM #T1
) as t
) s
GROUP BY s.itemid'
EXEC sp_executesql #sql
Same output.
select itemid,
max(case when seq = 1 then pack end) pack1, max(case when seq = 1 then uom end)uom1,max(case when seq = 2 then pack end) pack2, max(case when seq = 2 then uom end) uom2,
max(case when seq = 3 then pack end) pack3, max(case when seq = 3 then uom end) uom3
from
(
select itemid, pack, uom, row_number() over(partition by itemid order by itemid) seq from T1
) d
group by itemid

Compare Two Comma Separated String return Matching String Index,matching count,blank count

How to compare to String of type below
#Qstring='C,D,B,C,D,C,E,B,E,A,D,'
#Astring='C,D,C,E,D, ,E, ,E, ,A,'
and produce the string similar below.
is there method compare without using loop and temp table
Blankstring='6,8,10'
BlankCount=3
MatchingString=1,2,5,7,9
matchcount =5
You probably shouldn't do it in DB but why not:
Warning: this is sketch only and should be improved if needed.
SqlFiddleDemo
Code:
DECLARE
#Qstring NVARCHAR(100) ='C,D,B,C,D,C,E,B,E,A,D,',
#Astring NVARCHAR(100) ='C,D,C,E,D, ,E, ,E, ,A,';
DECLARE
#Qxml XML = CONVERT(XML,'<Vals><Val>' + REPLACE(LEFT(#Qstring,LEN(#Qstring)-1),',', '</Val><Val>') + '</Val></Vals>'),
#Axml XML = CONVERT(XML,'<Vals><Val>' + REPLACE(LEFT(#Astring,LEN(#Astring)-1),',', '</Val><Val>') + '</Val></Vals>');
;WITH QStringTab AS
(
SELECT
[val] = x.i.value('.', 'NVARCHAR(10)')
,[rn] = ROW_NUMBER() OVER (ORDER BY (SELECT 1))
FROM #Qxml.nodes('/Vals/Val') AS x(i)
), AStringTab AS
(
SELECT
[val] = x.i.value('.', 'NVARCHAR(10)')
,[rn] = ROW_NUMBER() OVER (ORDER BY (SELECT 1))
FROM #Axml.nodes('/Vals/Val') AS x(i)
), matchcount AS
(
SELECT c = COUNT(*)
FROM QStringTab q
JOIN AStringTab a
ON q.rn = a.rn
AND q.val = a.val
), blankcount AS
(
SELECT c = COUNT(*)
FROM AStringTab a
WHERE val = ''
), blankstring AS
(
SELECT
[s] = STUFF( (SELECT ',' + CAST(a.rn AS NVARCHAR(10))
FROM AStringTab a
WHERE a.val = ''
ORDER BY rn ASC
FOR XML PATH('')),
1, 1, '')
), matchingstring AS
(
SELECT
[s] = STUFF( (SELECT ',' + CAST(a.rn AS NVARCHAR(10))
FROM QStringTab q
JOIN AStringTab a
ON q.rn = a.rn
AND q.val = a.val
ORDER BY q.rn ASC
FOR XML PATH('')),
1, 1, '')
)
SELECT [statistics] = 'BlankString = ' + ISNULL(bs.s, '')
FROM blankstring bs
UNION ALL
SELECT [statistics] = 'BlankCount = ' + CAST(b.c AS NVARCHAR(100))
FROM blankcount b
UNION ALL
SELECT [statistics] = 'MatchingString = ' + ISNULL(ms.s, '')
FROM matchingstring ms
UNION ALL
SELECT [statistics] = 'Matchcount = ' + CAST(m.c AS NVARCHAR(100))
FROM matchcount m;
Doubts:
I assume that you want to compare values between commas and strings have the same number of commas
BlankCount should count from both or only #AString?
BlankString only for #AString, what if there are blank in both strings?
MatchingString should match blanks?
I removed last comma because it will be one more blank

How do I store the result of a query inside a temporary table in a stored procedure?

I am trying to store the results of a complex query inside a temporary table but I keep getting an error. Below is the code from my stored procedure:
DECLARE #TempItems TABLE
(
ID int IDENTITY, ForumThreadID int, ForumID int, ParentID int, title NVARCHAR(MAX), title_path NVARCHAR(MAX),
level_id NVARCHAR(MAX), level_id_path NVARCHAR(MAX), PostBody NVARCHAR(MAX), CreatedBy int, UserName NVARCHAR(50), Created DateTime
)
DECLARE #SQL NVARCHAR(MAX)
SET #SQL = 'WITH TreeList (ForumThreadID, ForumID, ParentID, title, title_path, level_id, level_id_path) as
(
SELECT p.ForumThreadID,
p.ForumID,
p.ParentID,
p.PostSubject,
CONVERT(nvarchar(max), p.ForumThreadID),
ROW_NUMBER() OVER(PARTITION BY ParentID ORDER BY p.ForumThreadID),
RIGHT(''0000'' + CAST(ROW_NUMBER() OVER(PARTITION BY ParentID ORDER BY p.ForumThreadID) AS varchar(max)),4)
FROM ForumThreads p
WHERE (p.ParentID = ' + #ParentID + ') AND (p.Deleted IS NULL)
UNION ALL
SELECT c.ForumThreadID,
c.ForumID,
c.ParentID,
c.PostSubject,
r.title_path + ''/'' + CAST(c.ForumThreadID AS VARCHAR(MAX)),
ROW_NUMBER() OVER(PARTITION BY c.ParentID ORDER BY c.ForumThreadID),
CONVERT(varchar(max), r.level_id_path + ''.'' + RIGHT(''0000'' + CAST(ROW_NUMBER() OVER(PARTITION BY c.ParentID ORDER BY c.ForumThreadID) AS VARCHAR),4))
FROM ForumThreads AS c
INNER JOIN treelist AS r
ON c.ParentID = r.ForumThreadID
WHERE (c.Deleted IS NULL))
SELECT TOP 100 TreeList.*, d.PostBody, d.CreatedBy, Members.UserName, COALESCE(d.Created,''1-JAN-1900'') AS Created
FROM TreeList INNER JOIN ForumThreads AS d ON TreeList.ForumThreadID = d.ForumThreadID INNER JOIN
Members ON d.CreatedBy = Members.MemberID
WHERE (d.Deleted IS NULL)
ORDER BY level_id_path;'
INSERT INTO #TempItems (ForumThreadID, ForumID, ParentID, title, title_path, level_id, level_id_path, PostBody, CreatedBy, UserName, Created) EXEC #SQL
SELECT * FROM #TempItems
The error I am getting is:
Msg 203, Level 16, State 2, Procedure spPagedForumThreads, Line 53
The name 'WITH TreeList (ForumThreadID, ForumID, ParentID, title, title_path, level_id, level_id_path) as
(
SELECT p.ForumThreadID,
p.ForumID,
p.ParentID,
p.PostSubject,
CONVERT(nvarchar(max), p.ForumThreadID),
ROW_NUMBER() OVER(PARTITION BY ParentID ORDER BY p.ForumThreadID),
RIGHT('0000' + CAST(ROW_NUMBER() OVER(PARTITION BY ParentID ORDER BY p.ForumThreadID) AS varchar(max)),4)
FROM ForumThreads p
WHERE (p.ParentID = 10720) AND (p.Deleted IS NULL)
UNION ALL
SELECT c.ForumThreadID,
c.ForumID,
c.ParentID,
c.PostSubject,
r.title_path + '/' + ' is not a valid identifier.
What am I doing wrong?
Is there another way to create a temporary table, one that does not require saving the query as a string?
Thanks!
What purpose does executing dynamic SQL serve? After your CTE, you should just be able to modify your SELECT statement to be INSERT INTO #TempItems SELECT TOP 100 TreeList.* .....
This should just work without needing dynamic SQL (i.e. EXEC). Note the statement prior to the WITH needs to be terminated with a semi-colon in order to be syntactically valid.
DECLARE #TempItems TABLE
(
ID int IDENTITY, ForumThreadID int, ForumID int, ParentID int, title NVARCHAR(MAX), title_path NVARCHAR(MAX),
level_id NVARCHAR(MAX), level_id_path NVARCHAR(MAX), PostBody NVARCHAR(MAX), CreatedBy int, UserName NVARCHAR(50), Created DateTime
);
WITH TreeList (ForumThreadID, ForumID, ParentID, title, title_path, level_id, level_id_path) as
(
SELECT p.ForumThreadID,
p.ForumID,
p.ParentID,
p.PostSubject,
CONVERT(nvarchar(max), p.ForumThreadID),
ROW_NUMBER() OVER(PARTITION BY ParentID ORDER BY p.ForumThreadID),
RIGHT('0000' + CAST(ROW_NUMBER() OVER(PARTITION BY ParentID ORDER BY p.ForumThreadID) AS varchar(max)),4)
FROM ForumThreads p
WHERE (p.ParentID = ' + #ParentID + ') AND (p.Deleted IS NULL)
UNION ALL
SELECT c.ForumThreadID,
c.ForumID,
c.ParentID,
c.PostSubject,
r.title_path + '/' + CAST(c.ForumThreadID AS VARCHAR(MAX)),
ROW_NUMBER() OVER(PARTITION BY c.ParentID ORDER BY c.ForumThreadID),
CONVERT(varchar(max), r.level_id_path + '.' + RIGHT('0000' + CAST(ROW_NUMBER() OVER(PARTITION BY c.ParentID ORDER BY c.ForumThreadID) AS VARCHAR),4))
FROM ForumThreads AS c
INNER JOIN treelist AS r
ON c.ParentID = r.ForumThreadID
WHERE (c.Deleted IS NULL))
INSERT INTO #TempItems (ForumThreadID, ForumID, ParentID, title, title_path, level_id, level_id_path, PostBody, CreatedBy, UserName, Created)
SELECT TOP 100 TreeList.*, d.PostBody, d.CreatedBy, Members.UserName, COALESCE(d.Created,'1-JAN-1900') AS Created
FROM TreeList INNER JOIN ForumThreads AS d ON TreeList.ForumThreadID = d.ForumThreadID INNER JOIN
Members ON d.CreatedBy = Members.MemberID
WHERE (d.Deleted IS NULL)
ORDER BY level_id_path;
SELECT * FROM #TempItems
EXEC(#SQL) instead of just EXEC #SQL.

Resources