Given the SQL...
declare #xmlDoc xml
set #xmlDoc = '<people>
<person PersonID="8" LastName="asdf" />
<person PersonID="26" LastName="rtert" />
<person PersonID="33" LastName="dfgh" />
<person PersonID="514" LastName="ukyy" />
</people>'
What would be the sql to convert that xml into a table of two columns PersonID and LastName?
SELECT T.c.query('.').value('(//#PersonID)[1]', 'int'),
T.c.query('.').value('(//#LastName)[1]', 'varchar(50)')
FROM #xmlDoc.nodes('/people/person') T(c)
select T.X.value('#PersonID', 'int') as PersonID,
T.X.value('#LastName', 'nvarchar(50)') as LastName
from #xmlDoc.nodes('/people/person') as T(X)
Related
I have following query in ORACLE:
SELECT *
FROM "Supplier" s
OUTER APPLY(
SELECT JSON_ARRAYAGG(JSON_OBJECT(p."Id", p."Description", p."Price")) as "Products"
FROM "Products" p
WHERE p."SupplierId" = s."Id"
) sp
In OUTER APPLY subquery I am creating json from columns I need and then aggregating those objects into json array. I need those two functions because sometimes I use only one of them. The same operation I would like to do in SqlServer. This is solution I managed so far:
SELECT *
FROM "Supplier" as s
OUTER APPLY(
SELECT p."Id", p."Description", p."Price"
FROM "Products" as p
WHERE p."SupplierId" = s."Id"
FOR JSON PATH
) as sp("Products")
The problem is that SqlServer executing those two functions at once (this is purpose for FOR JSON PATH statement). So here are my questions:
1) Is there possible to create json object without wrapping it into array (oracle-like syntax)?
2) Is there possible to aggregate json objects into an array?
UPDATE
I am using SqlServer version 2019 15.0.2000.5
Expected result (single record of products in json format)
"Products":{
"Id":"FEB0646B709B45B5A306E10599716F28",
"Description":"Database Manager",
"Price":149.99
}
If I understand the question correctly, the following statements are possible soltion (of course, they are based on the example data and statements in the question):
How to create a single JSON object:
If you want to generate one single JSON object, you need to use FOR JSON PATh for each row in the OUTER APPLY statement with the appropriate path expression. JSON_QUERY() is needed, because it returns a valid JSON and FOR JSON doesn't escape special characters.
Tables:
CREATE TABLE Supplier (
Id int,
Description varchar(50),
DateStart date
)
CREATE TABLE Products (
Id varchar(5),
SupplierId int,
Description varchar(100),
Price numeric(10, 2)
)
INSERT INTO Supplier (Id, Description, DateStart)
VALUES (1, 'Oracle', '19900505')
INSERT INTO Products (Id, SupplierId, Description, Price)
VALUES ('11111', 1, 'Database Manager', 149.99)
INSERT INTO Products (Id, SupplierId, Description, Price)
VALUES ('22222', 1, 'Chassi', 249.99)
Statement:
SELECT *
FROM "Supplier" s
OUTER APPLY(
SELECT Products = JSON_QUERY((
SELECT
p."Id" AS 'Product.Id',
p."Description" AS 'Product.Description',
p."Price" AS 'Product.Price'
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
))
FROM "Products" as p
WHERE p."SupplierId" = s."Id"
) sp ("Products")
Result:
Id Description DateStart Products
1 Oracle 1990-05-05 {"Product":{"Id":"11111","Description":"Database Manager","Price":149.99}}
1 Oracle 1990-05-05 {"Product":{"Id":"22222","Description":"Chassi","Price":249.99}}
How to aggregate JSON objects into an array:
By default FOR JSON creates a JSON array with one JSON object for each row. You only need to set a root key:
Statement:
SELECT *
FROM "Supplier" s
OUTER APPLY(
SELECT p."Id", p."Description", p."Price"
FROM "Products" p
WHERE p."SupplierId" = s."Id"
FOR JSON PATH
) sp("Products")
Result:
Id Description DateStart Products
1 Oracle 1990-05-05 [{"Id":"11111","Description":"Database Manager","Price":149.99},{"Id":"22222","Description":"Chassi","Price":249.99}]
DECLARE #data varchar(max)
DECLARE #LIST NVARCHAR(MAX)
DECLARE #Temp TABLE (YourColumnName VARCHAR(MAX) NULL);
INSERT INTO #Temp SELECT DISTINCT columnName FROM YourTableName WHERE(Id > 1000);
SELECT #LIST = STRING_AGG(CONVERT(NVARCHAR(max), ISNULL(YourColumnName, 'N/A')), ',') FROM #Temp
SET #data =(select #LIST as Name1,#LIST as Name2 For Json PATH)
select #data
I would like to retrieve values from JSON column.
There are my data :
I try to get firstName, lastName, "home" and "work" from shippingAddress JSON column.
So I use this query :
SELECT id, lastName, firstName, homeAddress, workAddress
FROM dbo.person
CROSS APPLY OPENJSON (shippingAddress ,'$') WITH (
homeAddress nvarchar(4000) '$.home', workAddress nvarchar(4000)
'$.work' )
And the result is :
but I would like to have this result :
Someone can help me ?
Ok I found the solution :
SELECT lastName, firstName, homeAddress =
JSON_VALUE(shippingAddress, '$[0].home'), workAddress =
JSON_VALUE(shippingAddress, '$[1].work') FROM dbo.person pp
Biml (Or SSIS) doesn't seem to want to automatically identify the row Delimiter. Without a column with a manually set on the last column of the column list to the intended Row Delimiter SSIS does not set the delimiter correctly for the row. I'm guessing that SSIS just implies the row delimiter even if it is set in the connection properties. Any one know of a fix for this other than writing around the problem and setting the last column's delmiter to the intended row delimiter (See "T" Column below)?
I checked the properties of the output Connection and it properly states the "RowDelimiter" as the CRLF, but if you look at the
Here is the Biml file:
<Biml xmlns="http://schemas.varigence.com/biml.xsd">
<Connections>
<OleDbConnection Name="Source" ConnectionString="Provider=SQLNCLI11;Server=localhost;Initial Catalog=test;Integrated Security=SSPI;">
</OleDbConnection>
<FlatFileConnection Name="Created" FilePath="D:\\created.dat" FileFormat="Changed">
<Expressions>
<Expression PropertyName="ConnectionString">#[$Package::FileDropRoot] + "\\"+REPLACE((DT_WSTR, 10)(DT_DBDATE)GETDATE(),"-","") + "." + "created.dat"</Expression>
</Expressions>
</FlatFileConnection>
</Connections>
<FileFormats>
<FlatFileFormat Name="Changed" ColumnNamesInFirstDataRow="true" HeaderRowDelimiter="CRLF" RowDelimiter="CRLF">
<Columns>
<Column Name="col1" DataType="String" Delimiter="Comma" ColumnType="Delimited" />
<Column Name="col2" DataType="String" Delimiter="Comma" ColumnType="Delimited"/>
<!-- this must be here in order to terminate the row -->
<Column Name="T" DataType="String" Delimiter="Comma" ColumnType="CRLF"/>
</Columns>
</FlatFileFormat>
</FileFormats>
<Packages>
<Package Name="Test" ConstraintMode="Linear" ProtectionLevel="EncryptSensitiveWithUserKey">
<Tasks>
<Dataflow Name="Test">
<Transformations>
<OleDbSource Name="Select Stagement" ConnectionName="Source">
<DirectInput>
Select * From Test
</DirectInput>
</OleDbSource>
<FlatFileDestination Name="UpdateFile" ConnectionName="Created">
</FlatFileDestination>
</Transformations>
</Dataflow>
</Tasks>
<Parameters>
<Parameter Name="FileDropRoot" DataType="String">D:\FileDrop</Parameter>
</Parameters>
</Package>
</Packages>
</Biml>
Here is the SSIS "Code" with the T Column above, please note that a Row Delimiter is specified (Line Breaks in export file):
<DTS:ConnectionManager DTS:CreationName="FLATFILE" DTS:DTSID="{9CDCB838-2A42-4CCA-A59C-DC60E9B3A967}" DTS:ObjectName="Created" DTS:refId="Package.ConnectionManagers[Created]">
<DTS:ObjectData>
<DTS:ConnectionManager DTS:CodePage="1252" DTS:ColumnNamesInFirstDataRow="True" DTS:ConnectionString="D:\\created.dat" DTS:Format="Delimited" DTS:HeaderRowDelimiter="_x000D__x000A_" DTS:LocaleID="1033" DTS:RowDelimiter="_x000D__x000A_" DTS:TextQualifier="_x003C_none_x003E_" DTS:Unicode="True">
<DTS:FlatFileColumns>
<DTS:FlatFileColumn DTS:ColumnDelimiter="_x002C_" DTS:ColumnType="Delimited" DTS:CreationName="" DTS:DataType="303" DTS:DTSID="{D64391D4-4551-44E9-8539-4C473EB700AA}" DTS:ObjectName="col1" DTS:TextQualified="True">
</DTS:FlatFileColumn>
<DTS:FlatFileColumn DTS:ColumnDelimiter="_x002C_" DTS:ColumnType="Delimited" DTS:CreationName="" DTS:DataType="303" DTS:DTSID="{974ED1AD-7D72-4A65-A877-BADEC09DAF20}" DTS:ObjectName="col2" DTS:TextQualified="True">
</DTS:FlatFileColumn>
<DTS:FlatFileColumn DTS:ColumnDelimiter="_x000D__x000A_" DTS:ColumnType="Delimited" DTS:CreationName="" DTS:DataType="303" DTS:DTSID="{4347C3C1-39BD-40B1-B38F-526730FE7BFB}" DTS:ObjectName="T" DTS:TextQualified="True">
</DTS:FlatFileColumn>
</DTS:FlatFileColumns>
</DTS:ConnectionManager>
</DTS:ObjectData>
<DTS:PropertyExpression DTS:Name="ConnectionString">#[$Package::FileDropRoot] + "\\"+REPLACE((DT_WSTR, 10)(DT_DBDATE)GETDATE(),"-","") + "." + "created.dat"</DTS:PropertyExpression>
</DTS:ConnectionManager>
Here is the SSIS "Code" without the T Column above, please note that a Row Delimiter is specified (No line breaks in export file):
<DTS:ConnectionManager DTS:CreationName="FLATFILE" DTS:DTSID="{79E9C576-FD53-4D4F-A07C-AED8D4CE72E6}" DTS:ObjectName="Created" DTS:refId="Package.ConnectionManagers[Created]">
<DTS:ObjectData>
<DTS:ConnectionManager DTS:CodePage="1252" DTS:ColumnNamesInFirstDataRow="True" DTS:ConnectionString="D:\\created.dat" DTS:Format="Delimited" DTS:HeaderRowDelimiter="_x000D__x000A_" DTS:LocaleID="1033" DTS:RowDelimiter="_x000D__x000A_" DTS:TextQualifier="_x003C_none_x003E_" DTS:Unicode="True">
<DTS:FlatFileColumns>
<DTS:FlatFileColumn DTS:ColumnDelimiter="_x002C_" DTS:ColumnType="Delimited" DTS:CreationName="" DTS:DataType="303" DTS:DTSID="{BBCA22D2-5D3E-47AC-AA0A-413C0C1A5CB2}" DTS:ObjectName="col1" DTS:TextQualified="True">
</DTS:FlatFileColumn>
<DTS:FlatFileColumn DTS:ColumnDelimiter="_x002C_" DTS:ColumnType="Delimited" DTS:CreationName="" DTS:DataType="303" DTS:DTSID="{44E567E4-BE78-432C-A8AC-C388E8BCFADC}" DTS:ObjectName="col2" DTS:TextQualified="True">
</DTS:FlatFileColumn>
</DTS:FlatFileColumns>
</DTS:ConnectionManager>
</DTS:ObjectData>
<DTS:PropertyExpression DTS:Name="ConnectionString">#[$Package::FileDropRoot] + "\\"+REPLACE((DT_WSTR, 10)(DT_DBDATE)GETDATE(),"-","") + "." + "created.dat"</DTS:PropertyExpression>
</DTS:ConnectionManager>
Here is the Script I used to Create the table on the database Connection
CREATE TABLE Test(col1 varchar(25),col2 varchar(25))
INSERT INTO Test
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2' UNION all
SELECT '1','2'
You have an incorrect assumption in that the last column's delimiter shouldn't be CRLF. You'll see in all the linked examples, while counter-intuitive, your flat file format file should use your row delimiter as the column delimiter for the final column. Every other column would use your "standard" column delimiter. And yes, it's repeated from the header declaration of what your row delimiter should be.
http://bimlscript.com/Snippet/Details/18
http://bimlscript.com/Snippet/Details/54
Some people juggle geese...
i have some data in my table
when i select that data with this query
select TblActionHistories.Comments from TblActionHistories WHERE TblActionHistories.Entity=CAST('Task' AS VARCHAR) AND EntityId=CAST(32 AS VARCHAR)
it's coming fine like this
Task- <b>T1</b> has been added by Swapnil Sharma
Task- <b>T1</b> status changed to <b>In Progress</b> by Swapnil Sharma<br/>
but now i want my above result # separated using stuff so i am using this
SELECT STUFF((SELECT '#' + ISNULL(CAST(TblActionHistories.Comments AS VARCHAR(MAX)),'') FROM TblActionHistories WHERE TblActionHistories.Entity=CAST('Task' AS VARCHAR) AND EntityId=CAST(32 AS VARCHAR) for xml path ('')),1,1,'')
it gives me this
Task- <b>T1</b> has been added by Swapnil Sharma#Task- <b>T1</b> status changed to <b>In Progress</b> by Swapnil Sharma<br/>
you can clearly see that all the special char. like < > converted to < > respectively
please help me out with this i want them to come in their original format
well i found the solution as suggested by IvanG
SELECT STUFF((SELECT '#' + ISNULL(CAST(TblActionHistories.Comments AS VARCHAR(MAX)),'') FROM TblActionHistories WHERE TblActionHistories.Entity=CAST('Task' AS VARCHAR) AND EntityId=CAST(32 AS VARCHAR) for xml path(''), root('MyString'), type ).value('/MyString[1]','varchar(max)') ,1,1,'')
ref. to this article
http://blogs.lobsterpot.com.au/2010/04/15/handling-special-characters-with-for-xml-path/
Try casting to NVARCHAR instead of VARCHAR
SELECT STUFF((SELECT '#' + ISNULL(CAST(TblActionHistories.Comments AS NVARCHAR(MAX)),'') FROM TblActionHistories WHERE TblActionHistories.Entity=CAST('Task' AS NVARCHAR) AND EntityId=CAST(32 AS VARCHAR) for xml path ('')),1,1,'')
I have a requirement that i want to search for data in xml data type, from the front end i will get firstname,lastname,dob,email all the fields are not mandatory some fields will come as empty or null i want to search according to that if i will get firstname as 'test' and lastname i will get as empty or null
If it is a varchar datatype then i can create query as
FirstName= ISNULL(#firstname, FirstName) or COALESCE(#firstname, FirstName, '') = '')
but in XML doc how can i use this type of query.
xmlDoc.value('(/personalDetails/firstname)[1]','varchar(100)')
Thanks
In SQLServer 2008, I would write something like :
select
xmlDoc.value('(personalDetails/firstname/text())[1]','varchar(100)') as firstname
from
myTable
where
not xmlDoc is null
and xmlDoc.exist('personalDetails/firstname[.!='''']')>0
You can also do a CASE WHEN ... ELSE ... END if you want to return NULL for non existing values
I think you can achieve this using common table expression. PFB the sample code-
DECLARE #XMLData XML
SET #XMLData ='
<STUDENTS>
<STUDENT>
<StudentID>1</StudentID>
<Name>John Smith</Name>
<Marks>200</Marks>
</STUDENT>
<STUDENT>
<StudentID>2</StudentID>
<Name>Mark Johnson</Name>
<Marks>300</Marks>
</STUDENT>
<STUDENT>
<StudentID>3</StudentID>
<Name></Name>
<Marks>400</Marks>
</STUDENT>
</STUDENTS>'
;with cte as(
SELECT StudentID = Node.Data.value('(StudentID)[1]', 'INT')
, [Name] = Node.Data.value('(Name)[1]', 'VARCHAR(MAX)')
, [Marks] = Node.Data.value('(Marks)[1]', 'INT')
FROM #XMLData.nodes('/STUDENTS/STUDENT') Node(Data)
)
Select * from cte where Name=''