how to fetch '/' separated node/tag name from a given xml in sql - sql

i want to fetch '/' separated node name from a given xml such that only node/tag name are getting fetched instead of node/tag value from a given xml.
Suppose if i have below xml :
<ns:manageWorkItemRequest>
<ns:wiFocus>
<act:orderDate>2020-03-16T10:30:56.000Z</act:orderDate>
<act:orderItem>
<agr:instance>
<spec1:customerServiceIdentifier>ETHA15302121</spec1:customerServiceIdentifier>
<spec1:instanceCharacteristic>
<spec1:action>
<spec1:code>Modify</spec1:code>
</spec1:action>
<spec1:instanceIdentifier>
<spec1:value>OS014-AHEFV5T9</spec1:value>
</spec1:instanceIdentifier>
</agr:instance>
</act:orderItem>
<act:orderVersion>1</act:orderVersion>
</ns:wiFocus>
<ns:wiAction>Create</ns:wiAction>
<ns:wiVersion>1</ns:wiVersion>
</ns:manageWorkItemRequest>
I want result as :
ns:manageWorkItemRequest/ns:wiFocus/act:orderItem/agr:instance/spec1:customerServiceIdentifier/ETHA15302121
actually the requirement is if i get this "ETHA15302121" value in above xml then i should show the path i.e. where exactly in xml that value is in '/' separated format.

Your XML was not well-formed (missing closing tag in the middle and missing namespace declarations.
After adding the missing parts it looks as so and you might try something along this route (warning: this won't be fast...):
Your XML
DECLARE #xml XML=
N'<root xmlns:ns="dummy1" xmlns:act="dummy2" xmlns:agr="dummy3" xmlns:spec1="dummy4">
<ns:manageWorkItemRequest>
<ns:wiFocus>
<act:orderDate>2020-03-16T10:30:56.000Z</act:orderDate>
<act:orderItem>
<agr:instance>
<spec1:customerServiceIdentifier>ETHA15302121</spec1:customerServiceIdentifier>
<spec1:instanceCharacteristic>
<spec1:action>
<spec1:code>Modify</spec1:code>
</spec1:action>
<spec1:instanceIdentifier>
<spec1:value>OS014-AHEFV5T9</spec1:value>
</spec1:instanceIdentifier>
</spec1:instanceCharacteristic>
</agr:instance>
</act:orderItem>
<act:orderVersion>1</act:orderVersion>
</ns:wiFocus>
<ns:wiAction>Create</ns:wiAction>
<ns:wiVersion>1</ns:wiVersion>
</ns:manageWorkItemRequest>
</root>';
--the query
WITH AllNamespaces As
(
SELECT CONCAT('ns',ROW_NUMBER() OVER(ORDER BY (B.namespaceUri))) Prefix
,B.namespaceUri
FROM #xml.nodes('//*') A(nd)
CROSS APPLY(VALUES(A.nd.value('namespace-uri(.)','nvarchar(max)')))B(namespaceUri)
WHERE LEN(B.namespaceUri)>0
GROUP BY B.namespaceUri
)
,recCte AS
(
SELECT 1 AS NestLevel
,ROW_NUMBER() OVER(ORDER BY A.nd) AS ElementPosition
,CAST(REPLACE(STR(ROW_NUMBER() OVER(ORDER BY A.nd),5),' ','0') AS VARCHAR(900)) COLLATE DATABASE_DEFAULT AS SortString
,CONCAT(ns.Prefix+':',A.nd.value('local-name(.)','nvarchar(max)'),'[',ROW_NUMBER() OVER(PARTITION BY CONCAT(ns.Prefix+':',A.nd.value('local-name(.)','nvarchar(max)')) ORDER BY A.nd),']') AS FullName
,CAST(CONCAT('/',ns.Prefix+':',A.nd.value('local-name(.)','nvarchar(max)'),'[',ROW_NUMBER() OVER(PARTITION BY CONCAT(ns.Prefix+':',A.nd.value('local-name(.)','nvarchar(max)')) ORDER BY A.nd),']') AS NVARCHAR(MAX)) COLLATE DATABASE_DEFAULT AS XPath
,A.nd.value('text()[1]','nvarchar(max)') AS NodeValue
,A.nd.query('./*') NextFragment
FROM #xml.nodes('/*') A(nd)
LEFT JOIN AllNamespaces ns ON ns.namespaceUri=A.nd.value('namespace-uri(.)','nvarchar(max)')
UNION ALL
SELECT r.NestLevel+1
,ROW_NUMBER() OVER(ORDER BY A.nd)
,CAST(CONCAT(r.SortString,REPLACE(STR(ROW_NUMBER() OVER(ORDER BY A.nd),5),' ','0')) AS VARCHAR(900)) COLLATE DATABASE_DEFAULT
,CONCAT(ns.Prefix+':',A.nd.value('local-name(.)','nvarchar(max)'),'[',ROW_NUMBER() OVER(PARTITION BY CONCAT(ns.Prefix+':',A.nd.value('local-name(.)','nvarchar(max)')) ORDER BY A.nd),']') AS FullName
,CONCAT(r.XPath,'/',ns.Prefix+':',A.nd.value('local-name(.)','nvarchar(max)'),'[',ROW_NUMBER() OVER(PARTITION BY CONCAT(ns.Prefix+':',A.nd.value('local-name(.)','nvarchar(max)')) ORDER BY A.nd),']') AS FullName
,A.nd.value('text()[1]','nvarchar(max)') AS NodeValue
,A.nd.query('./*') NextFragment
FROM recCte r
CROSS APPLY NextFragment.nodes('*') A(nd)
OUTER APPLY(SELECT Prefix FROM AllNamespaces ns WHERE ns.namespaceUri=A.nd.value('namespace-uri(.)','nvarchar(max)')) ns
)
SELECT XPath
,NodeValue
,NestLevel
,ElementPosition
,SortString
FROM recCte
--WHERE NodeValue IS NOT NULL
ORDER BY SortString;
--The result
/*
+------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------+-----------+-----------------+------------------------------------------+
| XPath | NodeValue | NestLevel | ElementPosition | SortString |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------+-----------+-----------------+------------------------------------------+
| /root[1]/ns1:manageWorkItemRequest[1]/ns1:wiFocus[1]/ns2:orderDate[1] | 2020-03-16T10:30:56.000Z | 4 | 1 | 00001000010000100001 |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------+-----------+-----------------+------------------------------------------+
| /root[1]/ns1:manageWorkItemRequest[1]/ns1:wiFocus[1]/ns2:orderItem[1]/ns3:instance[1]/ns4:customerServiceIdentifier[1] | ETHA15302121 | 6 | 1 | 000010000100001000020000100001 |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------+-----------+-----------------+------------------------------------------+
| /root[1]/ns1:manageWorkItemRequest[1]/ns1:wiFocus[1]/ns2:orderItem[1]/ns3:instance[1]/ns4:instanceCharacteristic[1]/ns4:action[1]/ns4:code[1] | Modify | 8 | 1 | 0000100001000010000200001000020000100001 |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------+-----------+-----------------+------------------------------------------+
| /root[1]/ns1:manageWorkItemRequest[1]/ns1:wiFocus[1]/ns2:orderItem[1]/ns3:instance[1]/ns4:instanceCharacteristic[1]/ns4:instanceIdentifier[1]/ns4:value[1] | OS014-AHEFV5T9 | 8 | 1 | 0000100001000010000200001000020000200001 |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------+-----------+-----------------+------------------------------------------+
| /root[1]/ns1:manageWorkItemRequest[1]/ns1:wiFocus[1]/ns2:orderVersion[1] | 1 | 4 | 3 | 00001000010000100003 |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------+-----------+-----------------+------------------------------------------+
| /root[1]/ns1:manageWorkItemRequest[1]/ns1:wiAction[1] | Create | 3 | 2 | 000010000100002 |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------+-----------+-----------------+------------------------------------------+
| /root[1]/ns1:manageWorkItemRequest[1]/ns1:wiVersion[1] | 1 | 3 | 3 | 000010000100003 |
+------------------------------------------------------------------------------------------------------------------------------------------------------------+--------------------------+-----------+-----------------+------------------------------------------+
*/
--just to show, that the created XPath is working as expected:
WITH XMLNAMESPACES('dummy1' AS ns1,'dummy2' AS ns2,'dummy3' AS ns3,'dummy4' AS ns4,'dummy5' AS ns5)
SELECT #xml.value('/root[1]/ns1:manageWorkItemRequest[1]/ns1:wiFocus[1]/ns2:orderDate[1]','nvarchar(max)')
,#xml.value('/root[1]/ns1:manageWorkItemRequest[1]/ns1:wiFocus[1]/ns2:orderItem[1]/ns3:instance[1]/ns4:customerServiceIdentifier[1]','nvarchar(max)')
,#xml.value('/root[1]/ns1:manageWorkItemRequest[1]/ns1:wiFocus[1]/ns2:orderItem[1]/ns3:instance[1]/ns4:instanceCharacteristic[1]/ns4:action[1]/ns4:code[1]','nvarchar(max)')
,#xml.value('/root[1]/ns1:manageWorkItemRequest[1]/ns1:wiFocus[1]/ns2:orderItem[1]/ns3:instance[1]/ns4:instanceCharacteristic[1]/ns4:instanceIdentifier[1]/ns4:value[1]','nvarchar(max)')
,#xml.value('/root[1]/ns1:manageWorkItemRequest[1]/ns1:wiFocus[1]/ns2:orderVersion[1]','nvarchar(max)')
,#xml.value('/root[1]/ns1:manageWorkItemRequest[1]/ns1:wiAction[1]','nvarchar(max)')
,#xml.value('/root[1]/ns1:manageWorkItemRequest[1]/ns1:wiVersion[1]','nvarchar(max)');
The idea in short:
The namespace prefixes can be defined by your own. The underlying URI is important.
The first cte will create a set of all occuring URIs and return this together with a prefix.
The recursive CTE will traverse deeper and deeper into the XML. This will continue as long as APPLY with .nodes() can return nested nodes.
The full name is concatenated as well as the full XPath.
The CASTs and COLLATEs help to avoid data type mismatch (recursive CTEs are very picky with this).
The concatenated SortString is needed to ensure the same order in your output.
UPDATE: You might think about FROM OPENXML
Just to mention it: There is the absolutely outdated FROM OPENXML, which is - afaik - the only way to get literally everything back:
DECLARE #xml XML=
N'<root xmlns="default" xmlns:ns="dummy">
<a ns:test="blah">blub</a>
<ns:b test2="hugo">blubber</ns:b>
</root>';
DECLARE #DocHandle INT;
EXEC sp_xml_preparedocument #DocHandle OUTPUT, #xml;
SELECT * FROm OPENXML(#DocHandle,'/*');
EXEC sp_xml_removedocument #DocHandle;
the result
+----+----------+----------+-----------+--------+--------------+----------+------+---------+
| id | parentid | nodetype | localname | prefix | namespaceuri | datatype | prev | text |
+----+----------+----------+-----------+--------+--------------+----------+------+---------+
| 0 | NULL | 1 | root | NULL | default | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+---------+
| 2 | 0 | 2 | xmlns | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+---------+
| 10 | 2 | 3 | #text | NULL | NULL | NULL | NULL | default |
+----+----------+----------+-----------+--------+--------------+----------+------+---------+
| 3 | 0 | 2 | ns | xmlns | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+---------+
| 11 | 3 | 3 | #text | NULL | NULL | NULL | NULL | dummy |
+----+----------+----------+-----------+--------+--------------+----------+------+---------+
| 4 | 0 | 1 | a | NULL | default | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+---------+
| 5 | 4 | 2 | test | ns | dummy | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+---------+
| 12 | 5 | 3 | #text | NULL | NULL | NULL | NULL | blah |
+----+----------+----------+-----------+--------+--------------+----------+------+---------+
| 6 | 4 | 3 | #text | NULL | NULL | NULL | NULL | blub |
+----+----------+----------+-----------+--------+--------------+----------+------+---------+
| 7 | 0 | 1 | b | ns | dummy | NULL | 4 | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+---------+
| 8 | 7 | 2 | test2 | NULL | NULL | NULL | NULL | NULL |
+----+----------+----------+-----------+--------+--------------+----------+------+---------+
| 13 | 8 | 3 | #text | NULL | NULL | NULL | NULL | hugo |
+----+----------+----------+-----------+--------+--------------+----------+------+---------+
| 9 | 7 | 3 | #text | NULL | NULL | NULL | NULL | blubber |
+----+----------+----------+-----------+--------+--------------+----------+------+---------+
As you can see, this result contains namespaces, prefixes and content. But it is very clumsy and far away from "today" :-)

Related

TSQL - Convert Rows per record to Columns

Consider the following table:
+------------------------------------------------------------------------------+
| GUID | DeviceGUID | DetailGUID | sValue | iValue | gValue | DateStored |
| ENTRY1 | DEVICE1 | Detail1 | SN112 | | | 01/01/2020 |
| ENTRY2 | DEVICE1 | Detail4 | | 1241 | | 01/01/2020 |
| ENTRY3 | DEVICE1 | Detail7 | | | GUID12 | 01/01/2020 |
| ENTRY4 | DEVICE2 | Detail1 | SN111 | | | 01/01/2020 |
| ENTRY5 | DEVICE2 | Detail2 | RND123 | | | 01/01/2020 |
| ENRTY6 | DEVICE2 | Detail4 | | 2351 | | 03/01/2020 |
| ENTRY7 | DEVICE3 | Detail1 | SN100 | | | 02/01/2020 |
| [...] | [...] | [...] | | | | |
| | | | | | | |
+------------------------------------------------------------------------------+
I have a table which links a DeviceGUID with a DetailsGUID, with the idea of having unlimited options for Details (just create a new detail and it will be fetchable). however, this means I have a finite and unknown amount of records per deviceGUID.
What I want to show to my users is a table like this:
+--------+---------------------------------------------------------------------+
| GUID | DeviceGUID |Detail1 |Detail2 |Detail4 |Detail7 |DateStored |
| ENTRY1 | DEVICE1 |SN112 | [NULL] |1241 |GUID12 | [MAX DateStored] |
| ENTRY2 | DEVICE2 |SN111 | RND123 |2351 | [NULL] | [MAX DateStored] |
| ENTRY3 | DEVICE3 |SN100 | | | | |
| [...] | [...] | | | | | |
+------------------------------------------------------------------------------+
I have been searching a bit and found the PIVOT option but that only seems to function for one field,
another option was CROSS APPLY, but that (seems to) need to convert everything to the same datatype; as I hope is visible n the ColumnNames, I will have 3 types of data: String (VARCHAR) value, Integer value, GUID (uniqueidentifier) value, and they will not be interchangeable (meaning Detail with GUID Detail1 will always have a VARCHAR, Detail with DetailGUID Detail4 will always be an Integer
what I was able to find out until now:
DECLARE #columns NVARCHAR(MAX), #sql NVARCHAR(MAX);
SET #columns = N'';
SELECT #columns+=N', p.'+QUOTENAME([Name])
FROM
(
SELECT GUID AS [Name]
FROM [dbo].Details AS p
) AS x;
SET #sql = N'
SELECT [DeviceObjectGUID], '+STUFF(#columns, 1, 2, '')+' FROM (
SELECT [DeviceObjectGUID], [DateStored], [DetailGUID] as [Name]
FROM [dbo].[DeviceDetails]) AS j PIVOT (MAX(DateStored) FOR [Name] in
('+STUFF(REPLACE(#columns, ', p.[', ',['), 1, 1, '')+')) AS p;';
EXEC sp_executesql #sql
to make a dynamic PIVOT for transposing the data.. but as mentioned this is limited to one column
and
select DeviceObjectGUID, value
from DeviceDetails
cross apply
(
select 'sValue', cast(sValue as varchar(MAX)) union all
select 'gValue', cast(gValue as varchar(MAX))union all
select 'iValue', cast(iValue as varchar(MAX))
) c(col, value)
This will have me converting all fields to VARCHAR..
One other option I tried (to understand PIVOT) was this:
SELECT *
FROM
(SELECT *
FROM [dbo].[DeviceDetails]
) AS SourceTable
PIVOT(
max(sValue)FOR [DetailGUID] IN(
[450533BB-43B2-499B-B2F7-094BFAE949B0],
[7483E518-EB61-4B72-93F7-0F97BBFAFA01],
[29B1BDE8-3AD4-4576-8B76-3CAE83E10B11],
[5FC8CC76-12EB-4924-9320-5D09BBE97C10],
[789AA79B-B1DF-4BA2-860A-7129B39D341F],
[C90F4EFE-D848-4BAB-96BF-8DC6BF4F6E62],
[EC6A4ED3-1475-4B0A-8E08-B2F4E095622F],
[D442B7CA-5825-49D9-9977-D88770304B57],
[99B70FEE-999B-4D44-83E9-EB8119B15930],
[3F83ED76-8CC3-4B3D-936A-F528DEB6C045]
)
) AS PivotTable;
(The GUIDS in the 'IN clause are the DetailGUIDs)
this almost gets me what I want, except that it is not dynamic and the fact that it is still limited to one data column. (max(sValue)) in this case.
===================
in response to
It should be as simple as that:
SELECT *
FROM
(
SELECT DeviceGUID
,DetailGUID
,CONCAT(sValue, iValue, gValue) as [value]
,DateStored
FROM my_table
) DS
PIVOT
(
MAX([value]) FOR DetailGUID IN (......)
) PVT

Import JSON File into SQL Server Table with nested Arrays

I'm trying to import the Census Block GeoJSON file and unable to get "Coordinates" for each block along with it's properties. I'm trying to get the ID, BlockGrp, Block.. and it's associated coordinates. Below is my code, but I'm unable to get the coordinates since it's in an array. Can anybody guide me on how to make it work?
Declare #GeoJSON varchar(max)
SELECT #GeoJSON = BulkColumn
FROM OPENROWSET (BULK 'C:\Temp\Census_Blocks__2010.geojson', SINGLE_CLOB) as j
SELECT *
FROM OPENJSON (#GeoJSON,'$.features')
WITH
(
OBJECTID INT N'$.properties.OBJECTID'
, BLKGRP NVARCHAR(10) N'$.properties.BLKGRP'
, BLOCK INT N'$.properties.BLOCK'
, GEOID NVARCHAR(100) N'$.properties.GEOID'
, GEOID10 NVARCHAR(100) N'$.properties.GEOID10'
, ACRES nvarchar(100) N'$.properties.ACRES'
, Shape_Length nvarchar(100) N'$.properties.Shape_length'
, Shape_Area nvarchar(100) N'$.properties.Shape_Area'
, SQMILES nvarchar(100) N'$.properties.SQMILES'
, Longitude nvarchar(100) N'$.geometry.coordinates[0]'
, Latitude nvarchar(100) N'$.geometry.coordinates[1]'
) a
The output is as follows:
+----------+---------+-------+-----------------+-----------------+-------------+--------------------+--------------------+------------+-----------+----------+
| OBJECTID | BLKGRP | BLOCK | GEOID | GEOID10 | ACRES | Shape_Length | Shape_Area | SQMILES | Longitude | Latitude |
+----------+---------+-------+-----------------+-----------------+-------------+--------------------+--------------------+------------+-----------+----------+
| 1 | 0005011 | 1004 | 110010005011004 | 110010005011004 | 92.90825947 | 3646.7801257671467 | 375986.38657525991 | 0.14516916 | NULL | NULL |
| 2 | 0005011 | 1005 | 110010005011005 | 110010005011005 | 4.22602654 | 600.80242048281752 | 17102.122624542077 | 0.00660317 | NULL | NULL |
| 3 | 0005011 | 1006 | 110010005011006 | 110010005011006 | 3.37694114 | 567.78401560218686 | 13665.995959875707 | 0.00527647 | NULL | NULL |
| 4 | 0005011 | 1007 | 110010005011007 | 110010005011007 | 6.2465494 | 784.3194030589018 | 25278.888549948519 | 0.00976023 | NULL | NULL |
| 5 | 0005011 | 1008 | 110010005011008 | 110010005011008 | 0.45035641 | 233.98753402256077 | 1822.5277124594836 | 0.00070368 | NULL | NULL |
| 6 | 0005011 | 1009 | 110010005011009 | 110010005011009 | 2.54391236 | 523.98099364773702 | 10294.848087676977 | 0.00397486 | NULL | NULL |
| 7 | 0005011 | 1010 | 110010005011010 | 110010005011010 | 3.65630529 | 511.54127551683035 | 14796.542550295248 | 0.00571298 | NULL | NULL |
| 8 | 0005011 | 1011 | 110010005011011 | 110010005011011 | 5.64727404 | 689.75830443180621 | 22853.707228554606 | 0.00882387 | NULL | NULL |
| 9 | 0005011 | 1012 | 110010005011012 | 110010005011012 | 7.38896984 | 856.70248366785154 | 29902.100049688841 | 0.01154527 | NULL | NULL |
| 10 | 0005011 | 1013 | 110010005011013 | 110010005011013 | 2.45065536 | 590.21583640085453 | 9917.4503661506897 | 0.00382915 | NULL | NULL |
+----------+---------+-------+-----------------+-----------------+-------------+--------------------+--------------------+------------+-----------+----------+
The GeoJSON file structure is as follows:
{
"type":"FeatureCollection",
"features":[
{
"type":"Feature",
"geometry":{
"type":"Polygon",
"coordinates":[
]
},
"properties":{
}
}
]
}
The GEOJSON file is available here.
I think you are on the right track, you should just perform a few additional steps:
retrieve the content of coordinates property using the AS JSON syntax
add two additional OPENJSON statements to shred your array to the [Longitude,Latitude] level with CROSS APPLY statements
retrieve Longitude and Latitude values with JSON_VALUE in your SELECT statement.
This is a sample query that should extract what you need:
SELECT
a.OBJECTID
, a.BLKGRP
, a.BLOCK
, a.GEOID
, a.GEOID10
, a.ACRES
, a.Shape_Length
, a.Shape_Area
, a.SQMILES
, JSON_VALUE(array2,'$[0]') as Longitude
, JSON_VALUE(array2,'$[1]') as Latitude
FROM OPENJSON (#GeoJSON,'$.features')
WITH
(
OBJECTID INT N'$.properties.OBJECTID'
, BLKGRP NVARCHAR(10) N'$.properties.BLKGRP'
, BLOCK INT N'$.properties.BLOCK'
, GEOID NVARCHAR(100) N'$.properties.GEOID'
, GEOID10 NVARCHAR(100) N'$.properties.GEOID10'
, ACRES nvarchar(100) N'$.properties.ACRES'
, Shape_Length nvarchar(100) N'$.properties.Shape_length'
, Shape_Area nvarchar(100) N'$.properties.Shape_Area'
, SQMILES nvarchar(100) N'$.properties.SQMILES'
, coordinates NVARCHAR(MAX) N'$.geometry.coordinates' AS JSON
) a
CROSS APPLY OPENJSON(coordinates) WITH (array nvarchar(max) N'$' as json) b
CROSS APPLY OPENJSON(array) WITH (array2 nvarchar(max) N'$' as json) c
Sample output:
You can see this code in action on a subset of your data in this fiddle.

Converting rows from a table into days of the week

What I thought was going to be a fairly easy task is becoming a lot more difficult than I expected. We have several tasks that get performed sometimes several times per day, so we have a table that gets a row added whenever a user performs the task. What I need is a snapshot of the month with the initials and time of the person that did the task like this:
The 'activity log' table is pretty simple, it just has the date/time the task was performed along with the user that did it and the scheduled time (the "Pass Time" column in the image); this is the table I need to flatten out into days of the week.
Each 'order' can have one or more 'pass times' and each pass time can have zero or more initials for that day. For example, for pass time 8:00, it can be done several times during that day or not at all.
I have tried standard joins to get the orders and the scheduled pass times with no issues, but getting the days of the week is escaping me. I have tried creating a function to get all the initials for the day and just creating
'select FuncCall() as 1, FuncCall() as 2', etc. for each day of the week but that is a real performance suck.
Does anyone know of a better technique?
Update: I think the comment about PIVOT looks promising, but not quite sure because everything I can find uses an aggregate function in the PIVOT part. So if I have the following table:
create table #MyTable (OrderName nvarchar(10),DateDone date, TimeDone time, Initials nvarchar(4), PassTime nvarchar(8))
insert into #MyTable values('Order 1','2018/6/1','2:00','ABC','1st Pass')
insert into #MyTable values('Order 1','2018/6/1','2:20','DEF','1st Pass')
insert into #MyTable values('Order 1','2018/6/1','4:40','XYZ','2nd Pass')
insert into #MyTable values('Order 1','2018/6/3','5:00','ABC','1st Pass')
insert into #MyTable values('Order 1','2018/6/4','4:00','QXY','2nd Pass')
insert into #MyTable values('Order 1','2018/6/10','2:00','ABC','1st Pass')
select * from #MyTable
pivot () -- Can't figure out what goes here since all examples I see have an aggregate function call such as AVG...
drop table #MyTable
I don't see how to get this output since I am not aggregating anything other than the initials column:
Something like this?
DECLARE #taskTable TABLE(ID INT IDENTITY,Task VARCHAR(100),TaskPerson VARCHAR(100),TaskDate DATETIME);
INSERT INTO #taskTable VALUES
('Task before June 2018','AB','2018-05-15T12:00:00')
,('Task 1','AB','2018-06-03T13:00:00')
,('Task 1','CD','2018-06-04T14:00:00')
,('Task 2','AB','2018-06-05T15:00:00')
,('Task 1','CD','2018-06-06T16:00:00')
,('Task 1','EF','2018-06-06T17:00:00')
,('Task 1','EF','2018-06-06T18:00:00')
,('Task 2','GH','2018-06-07T19:00:00')
,('Task 1','CD','2018-06-07T20:00:00')
,('After June 2018','CD','2018-07-15T21:00:00');
SELECT p.*
FROM
(
SELECT t.Task
,ROW_NUMBER() OVER(PARTITION BY t.Task,CAST(t.TaskDate AS DATE) ORDER BY t.TaskDate) AS Taskindex
,CONCAT(t.TaskPerson,' ',CONVERT(VARCHAR(5),t.TaskDate,114)) AS Content
,DAY(TaskDate) AS ColumnName
FROM #taskTable t
WHERE YEAR(t.TaskDate)=2018 AND MONTH(t.TaskDate)=6
) tbl
PIVOT
(
MAX(Content) FOR ColumnName IN([1],[2],[3],[4],[5],[6],[7],[8],[9],[10]
,[11],[12],[13],[14],[15],[16],[17],[18],[19],[20]
,[21],[22],[23],[24],[25],[26],[27],[28],[29],[30],[31])
) P
ORDER BY P.Task,Taskindex;
The result
+--------+-----------+------+------+----------+----------+----------+----------+----------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
| Task | Taskindex | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 |
+--------+-----------+------+------+----------+----------+----------+----------+----------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
| Task 1 | 1 | NULL | NULL | AB 13:00 | CD 14:00 | NULL | CD 16:00 | CD 20:00 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
+--------+-----------+------+------+----------+----------+----------+----------+----------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
| Task 1 | 2 | NULL | NULL | NULL | NULL | NULL | EF 17:00 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
+--------+-----------+------+------+----------+----------+----------+----------+----------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
| Task 1 | 3 | NULL | NULL | NULL | NULL | NULL | EF 18:00 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
+--------+-----------+------+------+----------+----------+----------+----------+----------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
| Task 2 | 1 | NULL | NULL | NULL | NULL | AB 15:00 | NULL | GH 19:00 | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL | NULL |
+--------+-----------+------+------+----------+----------+----------+----------+----------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+------+
The first trick is, to use the day's index (DAY()) as column name. The second trick is the ROW_NUMBER(). This will add a running index per task and day thus replicating the rows per index. Otherwise you'd get just one entry per day.
You input tables will be more complex, but I think this shows the principles...
UPDATE: So we have to get it even slicker :-D
WITH prepareData AS
(
SELECT t.Task
,t.TaskPerson
,t.TaskDate
,CONVERT(VARCHAR(10),t.TaskDate,126) AS TaskDay
,DAY(t.TaskDate) AS TaskDayIndex
,CONVERT(VARCHAR(5),t.TaskDate,114) AS TimeContent
FROM #taskTable t
WHERE YEAR(t.TaskDate)=2018 AND MONTH(t.TaskDate)=6
)
SELECT p.*
FROM
(
SELECT t.Task
,STUFF((
SELECT ', ' + CONCAT(x.TaskPerson,' ',TimeContent)
FROM prepareData AS x
WHERE x.Task=t.Task
AND x.TaskDay= t.TaskDay
ORDER BY x.TaskDate
FOR XML PATH(''),TYPE
).value(N'.',N'nvarchar(max)'),1,2,'') AS Content
,t.TaskDayIndex
FROM prepareData t
GROUP BY t.Task, t.TaskDay,t.TaskDayIndex
) p--tbl
PIVOT
(
MAX(Content) FOR TaskDayIndex IN([1],[2],[3],[4],[5],[6],[7],[8],[9],[10]
,[11],[12],[13],[14],[15],[16],[17],[18],[19],[20]
,[21],[22],[23],[24],[25],[26],[27],[28],[29],[30],[31])
) P
ORDER BY P.Task;
The result
+--------+------+------+----------+----------+----------+------------------------------+----------+------+
| Task | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 |
+--------+------+------+----------+----------+----------+------------------------------+----------+------+
| Task 1 | NULL | NULL | AB 13:00 | CD 14:00 | NULL | CD 16:00, EF 17:00, EF 18:00 | CD 20:00 | NULL |
+--------+------+------+----------+----------+----------+------------------------------+----------+------+
| Task 2 | NULL | NULL | NULL | NULL | AB 15:00 | NULL | GH 19:00 | NULL |
+--------+------+------+----------+----------+----------+------------------------------+----------+------+
This will use a well discussed XML trick within a correlated sub-query in order to get all common entries together as one. With this united content you can go the normal PIVOT path. The aggregate will not compute anything, as there is - for sure - just one value per cell.

How to get a child table records which exist in results of a table valued function

I have a table named TBL_WorkOrder as below :
+----+------------+----------------+
| Id | SystemCode | WorkOrderTitle |
+----+------------+----------------+
| 1 | C001 | Title 1 |
| 2 | C002 | Title 2 |
| 3 | C003 | Title 3 |
+----+------------+----------------+
and another table named TBL_WorkGroup
+----+---------------+
| Id | WorkGroupName |
+----+---------------+
| 1 | WorkGroup1 |
| 2 | WorkGroup2 |
| 3 | WorkGroup3 |
+----+---------------+
Each work order can contain different work groups as below (TBL_WorkOrderGroup)
+----+-------------+-------------+
| Id | WorkOrderId | WorkGroupId |
+----+-------------+-------------+
| 1 | 1 | 1 |
| 2 | 1 | 3 |
| 3 | 2 | 1 |
+----+-------------+-------------+
The problem is that I send a varchar string like '1,3' to the stored procedure. This varchar is changed to a table using a table valued function. I want to obtain the work orders that contain both '1' and '3' as their work groups.
What should i do in this case?
DECLARE #String VARCHAR(100) = '1,3'
;WITH Split AS
(
SELECT SUBSTRING(#String,0,CHARINDEX(',',#String)) SplitStr,SUBSTRING(#String,CHARINDEX(',',#String)+1,LEN(#String)) RemainStr
UNION ALL
SELECT CASE WHEN CHARINDEX(',',RemainStr) = 0 THEN RemainStr ELSE SUBSTRING(RemainStr,0,CHARINDEX(',',RemainStr)) END,
CASE WHEN CHARINDEX(',',RemainStr) = 0 THEN '' ELSE SUBSTRING(RemainStr,CHARINDEX(',',RemainStr)+1,LEN(RemainStr)) END
FROM Split
WHERE ISNULL(RemainStr,'') <> ''
)
SELECT SplitStr FROM Split

Unable to group data in a table

Id | MenuName | vc_Link | SubMenu1 | IsMainMenu | TypeOfMenu
-----------------------------------------------------------------------
1 | Home | NULL | NULL | 1 | 0
2 | Projects | NULL | NULL | 1 | 0
4 | Pr1 | NULL | 2 | 0 | 1
5 | Homesub | NULL | 1 | 0 | 1
6 | Pr1Sub1 | NULL | 4 | 0 | 2
The above is my table structure.
MenuName is names of menus and submenus
SubMenu1 is that submenus belong to a paticular menu whos id is saved as submenu1
MainMenu is a bit to know whether it is a menu or submenu.
typeofmenu is that
0 means main menu
1 means submenu
2 means submenu's submenu
Now my problem is i want to display it as below
1 Home
5 HomeSub
2 Projects
4 Pr1
6 Pr1Sub1
Id | MenuName | vc_Link | SubMenu1 | IsMainMenu | TypeOfMenu
-----------------------------------------------------------------------
1 | Home | NULL | NULL | 1 | 0
5 | HomeSub | NULL | 1 | 0 | 1
2 | Projects | NULL | NULL | 1 | 0
4 | Pr1 | NULL | 2 | 0 | 1
6 | Pr1Sub1 | NULL | 4 | 0 | 2
How to group like this?
Only after grouping like this i can make the result of this query as a tree structure showing menus and its submenus.
Something like this:
with menu_tree (id, menuname, level, sort_path) as (
select id,
cast(menuname as varchar(max)),
1 as level,
cast(id as varchar(max)) as sort_path
from menus
where submenu1 is null
union all
select m.id,
cast(replicate('_', p.level * 2) + m.menuname as varchar(max)) as menuname,
p.level + 1,
p.sort_path + '_' + cast(m.id as varchar(max))
from menus m
join menu_tree p on p.id = m.submenu1
)
select id, menuname
from menu_tree
order by sort_path;
SQLFiddle: http://sqlfiddle.com/#!3/6c4ba/1