Related
Working with XML in SQL Server, given this XML:
<A>
<B>123</B>
<C>
<Cs>234</Cs>
<Cs>345</Cs>
<Cs>12</Cs>
<Cs>2346</Cs>
</Cs>
</A>
I'd like to produce a result set that looks like this:
xpath
value
(/A/B)[1]
123
(/A/C/Cs)[1]
234
(/A/C/Cs)[2]
345
(/A/C/Cs)[3]
12
(/A/C/Cs)[4]
2346
Is there a trick that can do this without walking through the XML? Added bonus would include the ability to start somewhere other than the document root. You could pass /A/C to this routine and it would only give the paths under that element.
This is one of the rare cases when the archaic OPENXML() is handy.
XQuery 3.0 introduced a real solution for such task: fn:path() function long time ago in 2014. Unfortunately, MS SQL Server supports just a subset of XQuery 1.0
Back to mundane earth.
SQL
DECLARE #xml XML =
N'<A>
<B>123</B>
<C>
<Cs>234</Cs>
<Cs>345</Cs>
<Cs>12</Cs>
<Cs>2346</Cs>
</C>
</A>';
DECLARE #DocHandle INT;
EXEC sp_xml_preparedocument #DocHandle OUTPUT, #xml;
;WITH rs AS
(
SELECT * FROM OPENXML(#DocHandle,'/*')
), cte AS
(
-- anchor
SELECT id
,ParentID
--, nodetype
, [text]
,CAST(id AS VARCHAR(100)) AS [Path]
,CAST('/' + rs.localname AS VARCHAR(1000))
+ N'['
+ CAST(ROW_NUMBER() OVER(ORDER BY (SELECT 1)) AS NVARCHAR)
+ N']' AS [XPath]
FROM rs
WHERE ParentID IS NULL
UNION ALL
--recursive member
SELECT t.id
,t.ParentID
--, nodetype = (SELECT nodetype FROM rs WHERE id = t.ParentID)
, t.[text]
, CAST(a.[Path] + ',' + CAST( t.ID AS VARCHAR(100)) AS VARCHAR(100)) AS [Path]
, CAST(a.[XPath] + '/' + IIF(t.nodetype = 2, '#', '')
+ t.localname AS VARCHAR(1000))
+ N'['
+ TRY_CAST(ROW_NUMBER() OVER(PARTITION BY t.localname ORDER BY (SELECT 1)) AS NVARCHAR)
+ N']' AS [XPath]
FROM rs AS t
INNER JOIN cte AS a ON t.ParentId = a.id
)
SELECT ID, ParentID, /*nodetype,*/ [Path]
, REPLACE([XPath],'#text','text()') AS XPath
, [text] AS [Value]
FROM cte
WHERE [text] IS NOT NULL
--AND CAST([text] AS VARCHAR(30)) = '12345'
ORDER BY [Path];
EXEC sp_xml_removedocument #DocHandle;
Output
+----+----------+----------+----------------------------+-------+
| ID | ParentID | Path | XPath | Value |
+----+----------+----------+----------------------------+-------+
| 8 | 2 | 0,2,8 | /A[1]/B[1]/text()[1] | 123 |
| 9 | 4 | 0,3,4,9 | /A[1]/C[1]/Cs[1]/text()[1] | 234 |
| 10 | 5 | 0,3,5,10 | /A[1]/C[1]/Cs[2]/text()[1] | 345 |
| 11 | 6 | 0,3,6,11 | /A[1]/C[1]/Cs[3]/text()[1] | 12 |
| 12 | 7 | 0,3,7,12 | /A[1]/C[1]/Cs[4]/text()[1] | 2346 |
+----+----------+----------+----------------------------+-------+
You can use a recursive CTE. You pass in the XML document in #xml. If you need to use a table to get the XML, you can use CROSS APPLY YourXml.nodes instead of FROM #XML.nodes.
WITH cte AS (
SELECT
xpath = CONCAT(v.name, '[', ROW_NUMBER() OVER (PARTITION BY v.name ORDER BY (SELECT 1)), ']'),
value = x.nd.value('text()[1]','nvarchar(100)'),
child = x.nd.query('*')
FROM #xml.nodes('*') x(nd)
CROSS APPLY (VALUES (x.nd.value('local-name(.)[1]','nvarchar(max)'))) v(name)
UNION ALL
SELECT
xpath = CONCAT(cte.xpath, '/', v.name, '[', ROW_NUMBER() OVER (PARTITION BY xpath, v.name ORDER BY (SELECT 1)), ']'),
value = x.nd.value('text()[1]','nvarchar(100)'),
child = x.nd.query('*')
FROM cte
CROSS APPLY cte.child.nodes('*') x(nd)
CROSS APPLY (VALUES (x.nd.value('local-name(.)[1]','nvarchar(max)'))) v(name)
)
SELECT
xpath = CONCAT('/', xpath, '/text()[1]'),
value
FROM cte
WHERE value IS NOT NULL;
db<>fiddle
Unfortunately, you cannot use the ancestor:: axis, which would have made this much easier.
If SQL Server supported ancestor:: you could do something like this
SELECT
xpath = '(' + x.nd.query('for $n in ancestor::* return concat("/", local-name($n))') + '/text())[1]',
value = x.nd.value('text()[1]','nvarchar(100)')
FROM #xml.nodes('//*[text()]') x(nd)
I have the following table, where ID is the pk of the table and is IDENTITY
+----+----------+-----------+-------------+
| ID | ParentID | SomeValue | FullPath |
+----+----------+-----------+-------------+
| 1 | NULL | A | (1) |
| 2 | 1 | A.1 | (1)/(2) |
| 3 | 2 | A.1.1 | (1)/(2)/(3) |
| 4 | NULL | B | (4) |
| 5 | 4 | B.1 | (4)/(5) |
| 6 | 4 | B.2 | (4)/(6) |
| 7 | 6 | B.2.1 | (4)/(6)/(7) |
+----+----------+-----------+-------------+
This table represents data stored in a hierarchical way. I am creating a procedure that will take as input an ID and new_ParentID as parameters; ID (and its children and children's children, etc) will be the branch to copy into new_ParentID.
I started the procedure, but I cannot figure out how will I get the new ID of the parent I created in order to add it's children. For example, if I want to copy A.1 (and A.1.1) into B.2, once A.1-Copied will be created, I do not know its ID to put it as ParentID of A.1.1-Copied. I'm aware of the function SCOPE_IDENTITY, but I don't know how to use it in a CTE. Here is what I have at the moment:
;WITH Branch
AS
(
SELECT ID,
ParentGroupID,
SomeValue
FROM
#Table1 A
WHERE
ID = #ID
UNION ALL
SELECT E.ID,
E.ParentGroupID,
E.SomeValue
FROM
#Table1 E
INNER JOIN Branch T
ON T.ID = E.ParentGroupID
)
INSERT INTO #Table1
SELECT
CASE WHEN ParentGroupID IS NULL
THEN #new_ParentID
ELSE ???,
SomeValue + '-Copied'
FROM
Branch
How can I manage to use SCOPE_IDENTITY to correctly set the new parent of children of my copied branch ?
EDITS:
Suppose I want to copy branch with ID 4 (so the whole B branch) into ID 2 (so A.1 branch), we should have data as follows:
+----+----------+------------+-----------------------+
| ID | ParentID | SomeValue | FullPath |
+----+----------+------------+-----------------------+
| 1 | NULL | A | (1) |
| 2 | 1 | A.1 | (1)/(2) |
| 3 | 2 | A.1.1 | (1)/(2)/(3) |
| 4 | NULL | B | (4) |
| 5 | 4 | B.1 | (4)/(5) |
| 6 | 4 | B.2 | (4)/(6) |
| 7 | 6 | B.2.1 | (4)/(6)/(7) |
| 8 | 2 | B-Copy | (1)/(2)/(8) |
| 9 | 8 | B.1-Copy | (1)/(2)/(8)/(9) |
| 10 | 8 | B.2-Copy | (1)/(2)/(8)/(10) |
| 11 | 10 | B.2.1-Copy | (1)/(2)/(8)/(10)/(11) |
+----+----------+------------+-----------------------+
I have procedures that update the SomeValue and FullPath values after, so don't worry about those! I'm interested in how to reproduce the hierarchy
Here is the code to insert sample data:
CREATE TABLE #Data
(
ID INT IDENTITY(1,1),
ParentID INT,
SomeValue VARCHAR(30),
FullPath VARCHAR(255)
)
INSERT INTO #Data VALUES(NULL,'A','(1)')
INSERT INTO #Data VALUES('1','A.1','(1)/(2)')
INSERT INTO #Data VALUES('2','A.1.1','(1)/(2)/(3)')
INSERT INTO #Data VALUES(NULL,'B','(4)')
INSERT INTO #Data VALUES('4','B.1','(4)/(5)')
INSERT INTO #Data VALUES('4','B.2','(4)/(6)')
INSERT INTO #Data VALUES('6','B.2.1','(4)/(6)/(7)')
OK, let's not beat around the bush, this is pretty messy, and takes a couple of sweeps.
We need to first use a MERGE here (with no UPDATE clause) so that we can OUTPUT the new and old ID values into a table variable. Then, afterwards we need to use an UPDATE to update all the paths for the new path.
You could likely UPDATE the prior level in the MERGE and at the same time INSERT the current level within the MERGE, however, I didn't go down that path, as it was potentially messier. Therefore, after inserting the rows, I use a further rCTe to create the new paths and UPDATE them.
This gives you the below (annotated) SQL:
USE Sandbox;
GO
CREATE TABLE dbo.Data
(
ID INT IDENTITY(1,1),
ParentID INT,
SomeValue VARCHAR(30),
FullPath VARCHAR(255)
)
INSERT INTO dbo.Data
--VALUES has supported multiple rows in 2008, you should be making use of it.
VALUES(NULL,'A','(1)')
,('1','A.1','(1)/(2)')
,('2','A.1.1','(1)/(2)/(3)')
,(NULL,'B','(4)')
,('4','B.1','(4)/(5)')
,('4','B.2','(4)/(6)')
,('6','B.2.1','(4)/(6)/(7)')
GO
--There are your parameters
DECLARE #BranchToCopy int,
#CopysParent int;
SET #BranchToCopy = 4;
SET #CopysParent = 2;
--Table which will have the data to INSERT in
DECLARE #NewData table (ID int,
ParentID int,
SomeValue varchar(30),
FullPath varchar(255),
Level int);
--Will be used in the MERGE's OUTPUT clause to link the new and old IDs
DECLARE #Keys table (OldID int,
NewID int,
Level int);
--Get the hierachical data and INSERT into the #NewData variable
WITH rCTE AS(
SELECT D.ID,
D.ParentID,
D.SomeValue,
D.FullPath,
1 AS Level
FROM dbo.Data D
WHERE ID = #BranchToCopy
UNION ALL
SELECT D.ID,
D.ParentID,
D.SomeValue,
D.FullPath,
r.[Level] + 1
FROM dbo.Data D
JOIN rCTE r ON D.ParentID = r.ID)
INSERT INTO #NewData (ID,ParentID,SomeValue,FullPath,Level)
SELECT r.ID,
r.ParentID,
CONCAT(r.SomeValue,'-Copy'),
r.FullPath,
r.[Level]
FROM rCTE r;
--Uncomment to see results
--SELECT *
--FROM #NewData;
--Yes, we're using a WHILE!
--This, however, is what is known as a "set based loop"
DECLARE #i int = 1;
WHILE #i <= (SELECT MAX(Level) FROM #NewData) BEGIN
--We use MERGE here as it allows us to OUTPUT columns that weren't inserted into the table
MERGE INTO dbo.Data USING (SELECT ND.ID,
CASE ND.ID WHEN #BranchToCopy THEN #CopysParent ELSE K.NewID END AS Parent,
ND.SomeValue,
ND.Level
FROM #NewData ND
LEFT JOIN #Keys K ON ND.ParentID = K.OldID
WHERE ND.Level = #i) U ON 0=1
WHEN NOT MATCHED THEN
INSERT (ParentID, SomeValue)
VALUES (U.Parent, U.SomeValue)
OUTPUT U.ID, inserted.ID, U.Level
INTO #Keys (OldID, NewID, Level);
--Increment
SET #i = #i + 1;
END;
--Uncomment to see results
--SELECT *
--FROM dbo.[Data];
--Now we need to do the FullPath, as that would be a pain to do on the fly
DECLARE #Paths table (ID int, NewPath varchar(255));
--Work out the new paths
WITH rCTE AS(
SELECT D.ID,
D.ParentID,
D.SomeValue,
D.FullPath,
CONVERT(varchar(255),NULL) AS NewPath
FROM dbo.Data D
WHERE D.ID = #CopysParent
UNION ALL
SELECT D.ID,
D.ParentID,
D.SomeValue,
D.FullPath,
CONVERT(varchar(255),CONCAT(ISNULL(r.FullPath,r.NewPath),'/(',D.ID,')'))
FROM dbo.Data D
JOIN rCTE r ON D.ParentID = r.ID
JOIN #Keys K ON D.ID = K.NewID) --As we want only the new rows
INSERT INTO #Paths (ID, NewPath)
SELECT ID, NewPath
FROM rCTe
WHERE FullPath IS NULL;
--Update the table
UPDATE D
SET FullPath = P.NewPath
FROM dbo.Data D
JOIN #Paths P ON D.ID = P.ID;
SELECT *
FROM dbo.Data;
GO
--Clean up
DROP TABLE dbo.Data;
DB<>Fiddle
Here's a solution, using only CTE's:
The path config as ( ... defines the from and to ids to be used for the computation. This could all be done in a TVF.
WITH T AS (
select 1 id, null parentid, 'A' somevalue, '(1)' fullpath union all
select 2 id, 1 parentid, 'A.1' somevalue, '(1)/(2)' fullpath union all
select 3 id, 2 parentid, 'A.1.1' somevalue, '(1)/(2)/(3)' fullpath union all
select 4 id, NULL parentid, 'B' somevalue, '(4)' fullpath union all
select 5 id, 4 parentid, 'B.1' somevalue, '(4)/(5)' fullpath union all
select 6 id, 4 parentid, 'B.2' somevalue, '(4)/(6)' fullpath union all
select 7 id, 6 parentid, 'B.2.1' somevalue, '(4)/(6)/(7)' fullpath
)
, config as (
select 4 from_id, 2 to_id
)
, maxid as (
select max(id) maxid from t
)
, initpath as (
select fullpath from t cross join config where id = to_id
)
, subset_from as (
select t.*, maxid + ROW_NUMBER() over (order by id) new_id, ROW_NUMBER() over (order by id) rn from t cross join config cross join maxid where fullpath like '(' + cast(from_id as varchar) + ')%'
)
, subset_count as (
select count(*) subset_count from subset_from
)
, fullpath_replacements (id, parentid, somevalue, new_id, fullpath, new_fullpath, lvl) as (
select id, parentid, somevalue, new_id, fullpath, replace(fullpath, '(' + cast((select sf.id from subset_from sf where rn = 1) as varchar) + ')', '(' + cast((select sf.new_id from subset_from sf where rn = 1) as varchar) + ')'), 1
from subset_from
union all
select id, parentid, somevalue, new_id, fullpath, replace(new_fullpath, '(' + cast((select sf.id from subset_from sf where sf.rn = fr.lvl + 1) as varchar) + ')', '(' + cast((select sf.new_id from subset_from sf where sf.rn = fr.lvl + 1) as varchar) + ')'), fr.lvl + 1
from fullpath_replacements fr where fr.lvl < (select subset_count from subset_count)
)
, final_replacement as (
select id, parentid, somevalue, new_id, fullpath, (select fullpath from t where t.id = (select to_id from config)) + '/' + new_fullpath new_fullpath, isnull((select sf.new_id from subset_from sf where sf.id = fr.parentid), (select to_id from config)) new_parentid
from fullpath_replacements fr where fr.lvl = (select subset_count from subset_count)
)
select id, parentid, somevalue, fullpath
from (
select * from t
union all
select new_id, new_parentid, somevalue, new_fullpath from final_replacement
) t order by id
The idea is to create new ids with the row_number window function (see subset_from part).
Then make the replacements in the fullpath id by id. That is done using a recursive CTE fullpath_replacements to simulate a loop.
This works because in the fullpath I can always use the brackets to identify which part of the fullpath needs to be exchanged.
This is the output:
i have a problem with approach how to pivot this table:
How can i convert this:
Would you be kind enough give me hints (not whole code) how to do this? i really dont know where should i start (i dont know whether it is typical pivoting unpivoting)
+------+------+--------+----------+-----------+-----+
| ColI | Col2 | Month | Turnover | Provision | Fee |
+------+------+--------+----------+-----------+-----+
| 123 | Asdf | 201810 | 10000 | 100 | 0,1 |
| 123 | Asdf | 201811 | 20000 | 200 | 0,2 |
| 123 | Asdf | 201812 | 30000 | 300 | 0,3 |
+------+------+--------+----------+-----------+-----+
into this:
+------+------+---------------+-----------------+------------+---------------+-----------------+------------+---------------+-----------------+-----------+
| ColI | Col2 | Turnover20810 | Provision201810 | Fee201810 | Turnover20811 | Provision201811 | Fee201811 | Turnover20812 | Provision201812 | Fee201812 |
+------+------+---------------+-----------------+------------+---------------+-----------------+------------+---------------+-----------------+-----------+
| 123 | Asdf | 10000 | 100 | 0,1 | 20000 | 200 | 0,2 | 30000 | 300 | 0,3 |
+------+------+---------------+-----------------+------------+---------------+-----------------+------------+---------------+-----------------+-----------+
Thank you!
Assuming that each ColI, Col2 group would only have a maximum of three records, then we can try pivoting using the help of ROW_NUMBER:
WITH cte AS (
SELECT *,
ROW_NUMBER() OVER (PARTITION BY ColI, Col2 ORDER BY Month) rn
FROM yourTable
)
SELECT
ColI,
Col2,
MAX(CASE WHEN rn = 1 THEN Turnover END) AS Turnover1,
MAX(CASE WHEN rn = 1 THEN Provision END) AS Provision1,
MAX(CASE WHEN rn = 1 THEN Fee END) AS Fee1,
MAX(CASE WHEN rn = 2 THEN Turnover END) AS Turnover2,
MAX(CASE WHEN rn = 2 THEN Provision END) AS Provision2,
MAX(CASE WHEN rn = 2 THEN Fee END) AS Fee2,
MAX(CASE WHEN rn = 3 THEN Turnover END) AS Turnover3,
MAX(CASE WHEN rn = 3 THEN Provision END) AS Provision3,
MAX(CASE WHEN rn = 3 THEN Fee END) AS Fee3
FROM cte
GROUP BY
ColI,
Col2;
Note that I did not hardwire more specific column names, to keep the query as general as possible. For example, perhaps there might be another ColI, Col2 group which would have a different three months.
By using Dynamic Sql
IF OBJECT_ID('tempdb..#TEMP') IS NOT NULL
DROP TABLE #TEMP
;WITH CTE (ColI , Col2 , Month , Turnover , Provision , Fee )
AS
(
SELECT 123 , 'Asdf' , 201810 , 10000 ,100 ,'0,1' UNION ALL
SELECT 123 , 'Asdf' , 201811 , 20000 ,200 , '0,2' UNION ALL
SELECT 123 , 'Asdf' , 201812 , 30000 ,300 , '0,3'
)
SELECT ColI , Col2,Turnover , Provision , Fee,MixedCol,Reqcol , ROW_NUMBER()OVER(ORDER BY (SELECT NULL)) AS Seq
INTO #Temp
FROM CTE
CROSS APPLY (VALUES (CONCAT('Turnover','_',[Month]),CAST(Turnover AS VARCHAR(20))),
(CONCAT('Provision','_',[Month]),CAST(Provision AS VARCHAR(20))),
(CONCAT('Fee','_',[Month]),CAST(Fee AS VARCHAR(20)))
)DT (MixedCol,Reqcol)
DECLARE #Sql nvarchar(max),
#DynamicColumn nvarchar(max),
#MaxDynamicColumn nvarchar(max)
SELECT #DynamicColumn = STUFF((SELECT ', '+QUOTENAME(CAST(MixedCol AS VARCHAR(100)))
FROM #TEMP ORDER BY Seq FOR XML PATH ('')),1,1,'')
SELECT #MaxDynamicColumn = STUFF((SELECT ', '+'MAX('+QUOTENAME(CAST(MixedCol AS VARCHAR(100)))+') AS '+QUOTENAME(CAST(MixedCol AS VARCHAR(100)))
FROM #TEMP ORDER BY Seq FOR XML PATH ('')),1,1,'')
SET #Sql=' SELECT ColI , Col2,'+ #MaxDynamicColumn+'
FROM
(
SELECT * FROM #TEMP
)AS src
PIVOT
(
MAX(Reqcol) FOR [MixedCol] IN ('+#DynamicColumn+')
) AS Pvt
GROUP BY ColI , Col2 '
EXEC (#Sql)
PRINT #Sql
Q: "...give me hints (not whole code) how to do this"
A: This example with dynamic transpose of table. You can try this. Unpivot your original table and transpose.
CREATE table #tbl (
color varchar(10), Paul int, John int, Tim int, Eric int);
insert #tbl select
'Red' ,1 ,5 ,1 ,3 union all select
'Green' ,8 ,4 ,3 ,5 union all select
'Blue' ,2 ,2 ,9 ,1;
select * FROM #tbl
--1) Transpose. Example without dynamic code. You create list of fields in query
select *
from #tbl
unpivot (value for name in ([Paul],[John],[Tim],[Eric])) up
pivot (max(value) for color in ([Blue],[Green],[Red])) p
--2) Transpose. Example with dynamic code, without names of fields.
DECLARE #cols NVARCHAR(MAX), #query NVARCHAR(MAX), #rows NVARCHAR(MAX)='';
-- XML with all values
SET #cols = STUFF(
(
SELECT DISTINCT
','+QUOTENAME(T.Color) -- Name of first column
FROM #tbl T FOR XML PATH(''), TYPE
).value('.', 'nvarchar(max)'), 1, 1, '');
SELECT #rows=#rows+','+QUOTENAME(Name)
FROM
(SELECT Name,ROW_NUMBER() OVER(ORDER BY column_id) AS 'RowNum'
FROM tempdb.sys.Columns
WHERE Object_ID = OBJECT_ID('tempdb..#tbl')
) AS A WHERE A.RowNum>1
SET #rows=STUFF(#rows,1,1,'')
SET #query='SELECT *
from #tbl
unpivot (value for name in ('+#rows+')) up
pivot (max(value) for color in ('+#Cols+')) p'
EXEC (#query)
I have the following Table definition with sample data. In the following table.
"TP" consecutive 3 records 2 times,then "SL" consecutive 1 records 2 times……
id | Result
1 | TP
2 | TP
3 | TP
4 | SL
5 | TP
6 | NONE
7 | NONE
8 | SL
9 | TP
10 | TP
11 | TP
12 | SL
13 | SL
14 | SL
And I am looking for a result like this:
comboNum | num
TP_3 | 2
SL_1 | 2
TP_1 | 1
SL_3 | 1
Any suggestions?
You can as the below
DECLARE #Tbl TABLE (Id INT, Result VARCHAR(10))
INSERT INTO #Tbl
VALUES
(1,'TP')
,(2,'TP')
,(3,'TP')
,(4,'SL')
,(5,'TP')
,(6,'NONE')
,(7,'NONE')
,(8,'SL')
,(9,'TP')
,(10,'TP')
,(11,'TP')
,(12,'SL')
,(13,'SL')
,(14,'SL')
;WITH CTE1
AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY Result, Id) RowId FROM #Tbl
),CTE2
AS
(
SELECT
Result,
MAX(C.Id) - MIN(C.Id) Cons,
MIN(C.Id) StartP,
MAX(C.Id) EndP
FROM
CTE1 C
WHERE
c.Result <> 'NONE'
GROUP BY
C.Result,
C.RowId - C.Id
)
SELECT
C.Result + '_' + CAST(C.Cons + 1 AS VARCHAR(50)) AS comboNum,
COUNT(*) AS Num
FROM
CTE2 C
GROUP BY
C.Result,
C.Cons
ORDER BY Num DESC
Result:
comboNum Num
------------------ -----------
TP_3 2
SL_1 2
TP_1 1
SL_3 1
Two CTEs with tricky ROW_NUMBER() sequence:
;WITH cte as (
SELECT id,
Result,
ROW_NUMBER() OVER (PARTITION BY Result ORDER BY id) - ROW_NUMBER() OVER (ORDER BY id) as seq
FROM YourTable
WHERE Result != 'NONE'
), final AS (
SELECT MIN(id) as mid,
Result +'_'+ CAST(MAX(id)-MIN(id)+1 as nvarchar(max)) as comboNum
FROM cte
GROUP BY Result, seq
)
SELECT comboNum,
COUNT(mid) as num
FROM final
GROUP BY comboNum
ORDER BY MIN(mid)
Output:
comboNum num
TP_3 2
SL_1 2
TP_1 1
SL_3 1
Declare #tblTest AS TABLE(
ID INT,
Result VARCHAR(50)
)
INSERT INTO #tblTest VALUES(1,'TP')
,(2,'TP')
,(3,'TP')
,(4,'SL')
,(5,'TP')
,(6,'NONE')
,(7,'NONE')
,(8,'SL')
,(9,'TP')
,(10,'TP')
,(11,'TP')
,(12,'SL')
,(13,'SL')
,(14,'SL')
;WITH X AS
(
SELECT
T.*,
ROW_NUMBER() OVER (ORDER BY ID) AS SrNo,
ROW_NUMBER() OVER (PARTITION BY Result ORDER BY id) AS PartNo
FROM #tblTest T
WHERE Result<>'NONE'
)
SELECT
ComboNum,
COUNT(Occurance) AS Num
FROM
(
SELECT
Result +'_'+ CAST((max(ID)-min(ID))+1 AS VARCHAR(5)) AS ComboNum,
(MAX(ID)-MIN(ID))+1 AS Occurance,
MIN(SrNo) AS SrNo
FROM X
GROUP BY Result, (SrNo - PartNo)
) Z
GROUP BY ComboNum,Occurance
ORDER BY MIN(SrNo)
Output:
Not sure how it calls, I need to do this:
+---------------+
| param | value |
| 1 | 3 |
| 1 | 3 |
| 2 | 4 |
| 2 | 4 |
+-------+-------+
to
+-----------------+
| param1 | param2 |
| 3 | 4 |
| 3 | 4 |
+-------+---------+
Is it possible without INNER JOIN ?
Where is my error?
SELECT TOP 10
dbo.RW_ReceivedData.[Value] AS Value1,
dbo.RW_ReceivedData.[Value] AS Value2
FROM
RW_ReceivedData
WHERE
dbo.RW_ReceivedData.LogicalName = CAST(0x01000F0800FF AS varbinary(8000)),
dbo.RW_ReceivedData.LogicalName = CAST(0x01000F0800FF AS varbinary(8000))
This should work:
DECLARE #cols NVARCHAR(4000)
= STUFF(
(
SELECT DISTINCT ',[' + CAST(param AS VARCHAR(10)) + ']'
FROM tbl
FOR XML PATH('')
),1,1,'')
DECLARE #colNames NVARCHAR(4000)
= STUFF(
(
SELECT DISTINCT ',[' + CAST(param AS VARCHAR(10)) + '] AS param' + CAST(param AS VARCHAR(10))
FROM tbl
FOR XML PATH('')
),1,1,'')
DECLARE #sql NVARCHAR(4000) =
'
SELECT '+#colNames+'
FROM
(
SELECT *, ROW_NUMBER() OVER (PARTITION BY param ORDER BY param) n
FROM tbl
) t
PIVOT
(
MIN(value) FOR param IN ('+#cols+')
) pvt'
EXEC(#sql)
Here is SQL Fiddle
This approach uses dynamic PIVOT. In order to get the expected result ROW_NUMBER() with partitioning was used since values in param column are not unique.
UPDATE
You can also try it like this, but you have to manually add columns to the query:
SELECT SUM(CASE WHEN param = 1 THEN value ELSE 0 END) param1
, SUM(CASE WHEN param = 2 THEN value ELSE 0 END) param2
, SUM(CASE WHEN param = 3 THEN value ELSE 0 END) param3
FROM
(
SELECT *
, ROW_NUMBER() OVER (PARTITION BY param ORDER BY param) num
FROM tbl
) t
GROUP BY
num
SQL Fiddle
If you don't want columns to be automatically handled, you can just use static PIVOT:
SELECT [1] AS param1
,[2] AS param2
FROM (
SELECT *
, ROW_NUMBER() OVER (PARTITION BY param ORDER BY param) n
FROM tbl) t
PIVOT (
MIN(value) FOR param IN ([1],[2])
) pvt