SQL Alternative for FOR XML PATH - sql-server

For some reason my query :
SELECT
MP_SQLQRY_ProjectArchive.ProjectShort,
MP_SQLQRY_ProjectArchive.ProjectName ,
MP_SQLQRY_ProjectArchive.ProjectManagerName ,
ProjectOwnerName,
ProjStateName,
ProjectKategoryShort,
ProjectTypeShort,
MP_SQLQRY_ProjectArchive.ProjectPriority,
DepartmentShort,
ProjectArchivedDate,
CONVERT(NVARCHAR(MAX),ERPAccountCodes.ERPAccountCodes) As ERPAccountCodes
FROM
MP_SQLQRY_ProjectArchive
INNER JOIN
SQLQRY_VALID_PROJECT_ORDER_INFO ON MP_SQLQRY_ProjectArchive.ProjectID = SQLQRY_VALID_PROJECT_ORDER_INFO.ProjectID
INNER JOIN
COM_tbl_MAD_Project ON MP_SQLQRY_ProjectArchive.ProjectID = COM_tbl_MAD_Project.ProjectID
LEFT JOIN
(SELECT
ProjectID,
CONVERT(Nvarchar(max), STUFF((SELECT ';' + [SAPNumber] AS [data()]
FROM [COM_tbl_MAD_ProjectERPAccountCode] t2
WHERE t2.[ProjectID] = t1.[ProjectID]
ORDER BY [SAPNumber]
FOR XML PATH('')), 1, 1, '')) AS ERPAccountCodes
FROM
[COM_tbl_MAD_ProjectERPAccountCode] t1
GROUP BY
[ProjectID], [SAPNumber]) AS ERPAccountCodes ON ERPAccountCodes.ProjectID = COM_tbl_MAD_Project.ProjectID
will not return the values for the ERPAccountCodes column. In SQL Server, it works fine and I get my values, which are seperated with a ; as I want.
But when I run it as extern connection with ADODB in access this single column is empty.
I suppose it is a problem with access or ADODB but I have no clue what to do about it
Is there an alternative to FOR XML PATH or is there a different way to do it?
For clarification, the values return some blank values. Not NULL, just these weird blank symbols when i debugged in VBA in access, altough I tried converting it into a NVarchar which theoretically should work just fine.
Left is an Excel created from the MS Access SQL call. Right is from SQL Server Management Studio:

Related

Using T-SQL find the Stored Procedure associated to an SSRS Report

I had some help writing this query -- I'm at a bit of a loss because i'm trying to find the the query type or procedure used and i'm not sure what else to add to the query or how to change it.
SELECT
Ds.Name as Data_Source_Name,
C2.Name AS Data_Source_Reference_Name,
C.Name AS Dependent_Item_Name,
C.Path AS Dependent_Item_Path,
ds.*
FROM
ReportServer.dbo.DataSource AS DS
INNER JOIN
ReportServer.dbo.Catalog AS C ON DS.ItemID = C.ItemID
AND DS.Link IN (SELECT ItemID
FROM ReportServer.dbo.Catalog
WHERE Type = 5) --Type 5 identifies data sources
FULL OUTER JOIN
ReportServer.dbo.Catalog C2 ON DS.Link = C2.ItemID
WHERE
C2.Type = 5
AND c.name LIKE '%mkt%'
ORDER BY
C.Path, C2.Name ASC, C.Name ASC;
Please advise.
Based on my comment, give this a try, should get you moving in the right direction on how you can parse the xml and zero in on the specific command.
You might have to update the name spaces in the script below and also add your report name.
But try something like this:
;WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/reporting/2008/01/reportdefinition') --You may have to change this based on you SSRS version
SELECT
[Path],
Name,
report_xml.value( '(/Report/DataSources/DataSource/#Name)[1]', 'VARCHAR(50)' ) AS DataSource,
report_xml.value( '(/Report/DataSets/DataSet/Query/CommandText/text())[1]', 'VARCHAR(MAX)' ) AS CommandText,
report_xml.value( '(/Report/DataSets/DataSet/Query/CommandType/text())[1]', 'VARCHAR(100)' ) AS CommandType,
report_xml
FROM
(
SELECT
[Path],
Name,
[Type],
CAST( CAST( content AS VARBINARY(MAX) ) AS XML ) report_xml
FROM dbo.[Catalog]
WHERE Content IS NOT NULL
AND [Type] = 2
) x
WHERE
--use below in where clause if searching for the CommandText. Depending on how the report was developed I would just use the proc name and no brackets or schema.
--Example: if you report was developed as having [dbo].[procName] just use LIKE '%procName%' below. Because other reports could just have dbo.procName.
report_xml.value( '(/Report/DataSets/DataSet/Query/CommandText/text())[1]', 'VARCHAR(MAX)' ) LIKE '%Your Proc Name here%'
--comment out the above and uncomment below if know your report name and want to search for that specific report.
--[x].[Name] = 'The Name Of Your Report'
You're in the right neighborhood... When a report RDL is published its XML is converted into a image data type and stored in dbo.Catalog.Content.
If you convert the image data to VARBINARY(MAX) and then convert to XML, you'll be able to read the XML in plain text.
SELECT TOP (10)
*
FROM
dbo.Catalog c
CROSS APPLY ( VALUES (CONVERT(XML, CONVERT(VARBINARY(MAX), c.Content))) ) cx (content_xml)
WHERE
c.Type = 2;
From there it's just a matter of parsing the XML to dig out what you're looking for. In this case you looking for tags that look like the following...
<DataSet Name="My_stored_proc">
Are you looking for the stored procedure name? If your looking to see where that is at its in the database itself, database > DatabaseName > Programmability > Stored Procedures. If your trying to use the query you built for a report you need to make the stored procedure or change the query type to text and paste it in the box.

SSRS: How to find which reports have subreports?

I have a lot of SQL Server Reporting Services (SSRS) reports (*.rdl). I want to know which of these reports are using subreports. How can I do that? Looking at an easier way instead of opening each report and figuring out if a subreport is being used.
Thanks
I think this should provide you with what you need (With thanks to Bret Stateham ):
--The first CTE gets the content as a varbinary(max)
--as well as the other important columns for all reports,
--data sources and shared datasets.
WITH ItemContentBinaries AS
(
SELECT
ItemID,Name,[Type]
,CASE Type
WHEN 2 THEN 'Report'
WHEN 5 THEN 'Data Source'
WHEN 7 THEN 'Report Part'
WHEN 8 THEN 'Shared Dataset'
ELSE 'Other'
END AS TypeDescription
,CONVERT(varbinary(max),Content) AS Content
FROM ReportServer.dbo.Catalog
WHERE Type IN (2,5,7,8)
),
--The second CTE strips off the BOM if it exists...
ItemContentNoBOM AS
(
SELECT
ItemID,Name,[Type],TypeDescription
,CASE
WHEN LEFT(Content,3) = 0xEFBBBF
THEN CONVERT(varbinary(max),SUBSTRING(Content,4,LEN(Content)))
ELSE
Content
END AS Content
FROM ItemContentBinaries
)
--The outer query gets the content in its varbinary, varchar and xml representations...
,VarcharContent as
(
SELECT
ItemID,Name,[Type],TypeDescription
,Content --varbinary
,CONVERT(varchar(max),Content) AS ContentVarchar --varchar
,CONVERT(xml,Content) AS ContentXML --xml
FROM ItemContentNoBOM
)
SELECT * FROM VarcharContent where ContentVarchar like '%<subreport%'
The following query below will return a list of deployed reports that have subreports. Here's the Microsoft reference and a link for referencing older versions of SSRS. It looks like the only difference is changing the version of SSRS in XMLNAMESPACES part of the CTE.
Query to return all subreports
WITH
XMLNAMESPACES
(
'http://schemas.microsoft.com/sqlserver/reporting/2016/01/reportdefinition' AS rdl
)
,
report_list
AS
(
SELECT
[ReportID] = cat.[ItemID]
, [ReportName] = cat.[Name]
, [ReportPath] = cat.[Path]
, [xmlColumn] = CAST(CAST(cat.[Content] AS VARBINARY(MAX)) AS XML)
FROM
[ReportServer].[dbo].[Catalog] AS cat
WHERE
1=1
AND cat.[Content] IS NOT NULL
AND cat.[Type] = 2
)
SELECT
rpt.[ReportID]
, rpt.[ReportName]
, rpt.[ReportPath]
, [SubReportName] = srpt.x.value('(//rdl:ReportName)[1]', 'NVARCHAR(256)')
FROM
report_list AS rpt
CROSS APPLY xmlColumn.nodes('//rdl:Subreport') AS srpt(x);

Why are my results in SSRS different from SQL SMS?

I've never seen this happen before, so I'm curious if anybody knows why, and maybe how I can correct for it.
This particular part of my code:
SELECT inv_num, co_line,
STUFF((
SELECT '/' + rs2.inv_pro_description
FROM #ReportSet rs2
WHERE rs2.inv_num = rs.inv_num AND
rs2.co_line = rs.co_line
FOR XML PATH('')), 1, 1, '') as ipd_combo
FROM #ReportSet rs
WHERE inv_pro_seq IS NOT NULL AND inv_pro_description <> 'Less Previously Invoiced'
GROUP BY inv_num, co_line
results in a correct concatenation of my two results, e.g., "10% Advance/Fixed $5 Required" in this case. However, when utilizing this procedure in SSRS, my field contains instead "Fixed $5 Required/10% Advance".
I have grown accustomed to being able to predict my SSRS reports based on running SQL queries, so this confused me.
I don't really understand the STUFF / XML PATH code, of course. I've copied it from somebody else (on this website, naturally) so I know that might have something to do with it.
To get consistent ordering, add an order by clause to your inner select:
SELECT inv_num, co_line,
STUFF((
SELECT '/' + rs2.inv_pro_description
FROM #ReportSet rs2
WHERE rs2.inv_num = rs.inv_num AND
rs2.co_line = rs.co_line
ORDER BY rs2.inv_pro_seq --********
FOR XML PATH('')), 1, 1, '') as ipd_combo
FROM #ReportSet rs
WHERE inv_pro_seq IS NOT NULL AND inv_pro_description <> 'Less Previously Invoiced'
GROUP BY inv_num, co_line
The commented asterisks indicate the added line.
(I answered in comments, but adding this here for consistency/permanency).

SQL limit for XML type

I am actually working on a large database and I am querying the following data;
My Query:
SELECT ldd.LDistCD, ldd.LDistDescPay
FROM LDetail ldd
INNER JOIN LDist ld
ON ldd.ID = ld.ID
AND ld.ID = '019458'
AND ld.LDistType = 'F'
Result:
What I am doing next, is to loop across the results (27873) in my VB codes to concatenate the data in the following format;
LDistCD + '|' + LDistDescPay
Normally that would be a very time consuming time looping through all these rows. Hence to optimise the work, I am using the following query which should already concatenate the data for me;
SELECT stuff((SELECT ',' + ldd.LddLabourDistCD + '|' + ldd.LddLabourDistDescPay
FROM LDetail ldd
INNER JOIN LDist ld
ON ldd.ID = ld.ID
AND ld.ID = 019425 AND ld.LDistType = 'F'
FOR XML
PATH ('')), 1, 1, '')
Everything is working fine except for the result, whereby some data is being truncated!! Running the last query on MS SQL Server returns the concatenate result but it is not complete. I get the impression there's a limit to the result which is being exceeded.
Can anyone help on the issue please?
Difficult for me to upload the db or the result but just to tell you that the 27873 rows, when concatenated in one string, is not fitting in the result.
The truncation you are seeing is specific to SQL Server Management Studio (SSMS). If you go to the Query menu and select Query Options..., then go to Results and then to Grid, you should see on the right side a section for "Maximum Characters Retrieved". The "Non XML data" has a max of 65,535 (which should also be the default) and the "XML data" is a drop-down with options:
1 MB
2 MB (default)
5 MB
Unlimited
Since the XML datatype can bring back more than 65,535 characters, you can convert your output to XML (this is only needed when using SSMS; client libraries should pull back the full string):
SELECT CONVERT(XML,
stuff((SELECT ',' + ldd.LddLabourDistCD + '|' + ldd.LddLabourDistDescPay
FROM LDetail ldd
INNER JOIN LDist ld
ON ldd.ID = ld.ID
AND ld.ID = 019425
AND ld.LDistType = 'F'
FOR XML PATH ('')), 1, 1, '')
)

How to import from XML file in SQL Server Import and Export Wizard?

I have data and schema for a table as a single XML file. How do I import this using the SQL Server Import and Export Wizard?
Should I use a "Flat File Source" as my Data Source? Or what?
[For info, I exported the XML from VistaDB, but I have not yet got as far as the point where the system which created the data might be an issue.]
As far as I know, you cannot do this by using a the import export wizard. Assuming you want the data to wind up all relational rather than as XML datatype, you'll need to create the table and use sp_xml_preparedocument and OPENXML.
See How to use OPENXML to load XML data into existing SQL Table?
As far as I know MS SQL Server Management Studio does not have a tool to upload XML to a table.
There is an option that involves a combination of OPENROWSET and XML processing, but it requires that the files reside in the server's file system.
I needed to load into a table a series of log files generated by a Java web Application but had no access to upload them to the server, I had the logs in my local machine. I managed to upload data in a two step process that is not too cumbersome but its definitely too slow for a permanent solution.
I created a table that consists of two columns: an autonumeric primary key, and a varchar(max).
I used the import data to upload the text files to the table so that each line in the file is a record in the table. The primary key coincidentally represents the line number.
So I could write something like:
select LineNumber, TextLine from [LogFile] order by LineNumber
I then prepared another table with an structure that matched the records in my XML.
My XML files had the particularity that each "value" tag was in its own text line, the opening and closing "record" tags where each on a separate line.
For example:
<log>
<record>
<date>2018-07-27T09:54:20</date>
<millis>1532706860250</millis>
<sequence>13587</sequence>
<logger>registroweb.ServReg</logger>
<level>INFO</level>
<class>somepackage.someclass</class>
<method>methodname</method>
<thread>11153</thread>
<message>some very long text</message>
<param>another long text</param>
</record>
...
</log>
This would mean that I could select all records where text_line = '<log>' would give me all opening record tags, but most importantly, self joining the table with t2.line_number = t1.line_number + 1 would always give me the line containing the date tag, line_number+2 would give millis, and so on.
So with the following query I was able to transform the flat linear table into a proper table:
insert into LogFileProcessed(
[date],
[millis],
[sequence],
[logger] ,
[level] ,
[class] ,
[method] ,
[thread] ,
[message],
[param]
)
select
--record.TextLine,
convert(datetime, replace(replace(ltrim(dte.TextLine), '<date>', ''), '</date>', ''), 126) [date],
convert(bigint, replace(replace(ltrim(mls.TextLine), '<millis>', ''), '</millis>', '')) [millis],
convert(bigint, replace(replace(ltrim(seq.TextLine), '<sequence>', ''), '</sequence>', '')) [sequence],
replace(replace(ltrim(logr.TextLine), '<logger>', ''), '</logger>', '') [logger],
replace(replace(ltrim(lvl.TextLine), '<level>', ''), '</level>', '') [level],
replace(replace(ltrim(cls.TextLine), '<class>', ''), '</class>', '') [class],
replace(replace(ltrim(mtd.TextLine), '<method>', ''), '</method>', '') [method],
replace(replace(ltrim(trd.TextLine), '<thread>', ''), '</thread>', '') [thread],
replace(replace(ltrim(msg.TextLine), '<message>', ''), '</message>', '') [message],
replace(replace(ltrim(prm.TextLine), '<param>', ''), '</param>', '') [param]
from LogFile record
left join LogFile dte on dte.LineNumber = record.LineNumber+1
left join LogFile mls on mls.LineNumber = record.LineNumber+2
left join LogFile seq on seq.LineNumber = record.LineNumber+3
left join LogFile logr on logr.LineNumber = record.LineNumber+4
left join LogFile lvl on lvl.LineNumber = record.LineNumber+5
left join LogFile cls on cls.LineNumber = record.LineNumber+6
left join LogFile mtd on mtd.LineNumber = record.LineNumber+7
left join LogFile trd on trd.LineNumber = record.LineNumber+8
left join LogFile msg on msg.LineNumber = record.LineNumber+9
left join LogFile prm on prm.LineNumber = record.LineNumber+10 and prm.TextLine <> '</record>' -- param is actually the only tag that is optional and some times is not present in the record.
where record.TextLine = '<record>'
order by 1, 2
Given the particular restrictions I have at the time and the structure of the files, this worked good enough for a one time task, allowing me to perform regular queries on the data without having to repeatedly run XML parsing or processing code.

Resources