Retrieve Rows as columns in sqlserver 2008 - sql-server

i have a Table DistanceTable
It has Columns like
Slno From To Dist
-----------------------------
1 Vskp Hyd 100
2 Hyd Chennai 200
3 Chennai Vskp 458
Now i want the data to be displayed as
VSKP HYD CHENNAI
VSKP 0km 100km 458km
HYD 100km 0km 200km
CHANNAI 458km 200km 0km
I tried with the query
SELECT *
FROM ( SELECT slno, fromcity ,tocity
FROM DistanceTable ) AS ET
PIVOT(MIN(slno) FOR fromcity IN (tocity) AS PT
Link To the question in .txt file

Try this :
SELECT fromcity
,[Vskp]=isnull([Vskp],0)
,[Hyd]=isnull([Hyd],0)
,[Chennai]=isnull([Chennai],0)
FROM ( SELECT fromcity ,tocity,Dist
FROM DistanceTable
union
SELECT tocity,fromcity ,Dist
FROM DistanceTable
) AS ET
PIVOT
(max(dist) FOR tocity IN ([Vskp],[Hyd],[Chennai])
) AS PT
SQL FIddle
Update :
Use dynamic SQL for variable number of cities as below :
DECLARE
#cols VARCHAR(MAX),
#IsNullCols VARCHAR(MAX),
#query VARCHAR(MAX)
SELECT
#cols = STUFF((
SELECT DISTINCT ', [' + tocity + ']'
FROM (SELECT tocity FROM DistanceTable
union
SELECT fromcity FROM DistanceTable)a
FOR XML PATH('')
), 1, 2, '');
print #cols;
SELECT
#IsNullCols = STUFF((
SELECT DISTINCT ', ['+tocity+']=IsNull([' + tocity + '],0)'
FROM (SELECT tocity FROM DistanceTable
union
SELECT fromcity FROM DistanceTable)a
FOR XML PATH('')
), 1, 2, '');
print #IsNullCols;
set #query = 'SELECT fromcity
,'+ #IsNullCols +'
FROM (
SELECT fromcity ,tocity,Dist
FROM DistanceTable
union
SELECT tocity,fromcity ,Dist
FROM DistanceTable
) AS ET
PIVOT
(max(dist) FOR tocity IN ('+#cols+')
) AS PT '
exec(#query)

here's a solution (but w/out pivot):
SELECT
fromcity as ' '
,ISNULL((SELECT SUM(dist) FROM DistanceTable d2 WHERE (d2.tocity = 'VSKP' AND d2.fromcity = d.fromcity) OR (d2.tocity = d.fromcity AND d2.fromcity = 'VSKP')), 0) as 'VSKP'
,ISNULL((SELECT SUM(dist) FROM DistanceTable d2 WHERE (d2.tocity = 'HYD' AND d2.fromcity = d.fromcity) OR (d2.tocity = d.fromcity AND d2.fromcity = 'HYD')), 0) as 'HYD'
,ISNULL((SELECT SUM(dist) FROM DistanceTable d2 WHERE (d2.tocity = 'CHENNAI' AND d2.fromcity = d.fromcity) OR (d2.tocity = d.fromcity AND d2.fromcity = 'CHENNAI')), 0) as 'CHENNAI'
FROM DistanceTable d
SQL fiddle

Let me explain this issue demonstrating simple example.
USE AdventureWorks
GO
-- Creating Test Table
CREATE TABLE Product(Cust VARCHAR(25), Product VARCHAR(20), QTY INT)
GO
-- Inserting Data into Table
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','VEG',2)
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','SODA',6)
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','MILK',1)
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','BEER',12)
INSERT INTO Product(Cust, Product, QTY)
VALUES('FRED','MILK',3)
INSERT INTO Product(Cust, Product, QTY)
VALUES('FRED','BEER',24)
INSERT INTO Product(Cust, Product, QTY)
VALUES('KATE','VEG',3)
GO
-- Selecting and checking entires in table
SELECT *
FROM Product
GO
-- Pivot Table ordered by PRODUCT
SELECT PRODUCT, FRED, KATE
FROM (
SELECT CUST, PRODUCT, QTY
FROM Product) up
PIVOT (SUM(QTY) FOR CUST IN (FRED, KATE)) AS pvt
ORDER BY PRODUCT
GO
-- Pivot Table ordered by CUST
SELECT CUST, VEG, SODA, MILK, BEER, CHIPS
FROM (
SELECT CUST, PRODUCT, QTY
FROM Product) up
PIVOT (SUM(QTY) FOR PRODUCT IN (VEG, SODA, MILK, BEER, CHIPS)) AS pvt
ORDER BY CUST
GO

Related

Move duplicate values of a row to columns in SQL Server

I have a table with the following sample data:
Name Value
-----------------------
Year 1990
Year 1991
Year 1992
Cash 2000
Cash 4000
Cash 6000
Tax Paid
Tax Unpaid
Tax Pending
Liability 3500
Liability 8500
Liability 9500
I want that data in the following format:
Name Value1 Value2 Value3
-----------------------------------------------------
Year 1990 1991 1992
Cash 2000 4000 6000
Tax Paid Unpaid Pending
Liability 3500 8500 9500
Please note, the number of columns in result table should be dynamic i.e., number of value columns = number of records for an item in original table.
In the example, each item has 3 records so there will be 3 value columns in result table.
Please guide.
Update:
I used below query and it returned data in correct format, but still clueless how to identify number of columns in resultant table. In below example fixed 5 columns are there (name, value1, value2, value3, value4).
with cte as
(
select name ci,value,
row_number() over(partition by [name] order by value) as rn
from TABLE
)
select distinct ci as [name],
(select ct.value from cte ct where ct.ci=cte.ci and ct.rn=1) value1,
(select ct.value from cte ct where ct.ci=cte.ci and ct.rn=2) value2,
(select ct.value from cte ct where ct.ci=cte.ci and ct.rn=3) value3,
(select ct.value from cte ct where ct.ci=cte.ci and ct.rn=4) value4
from cte
create table #t
(
Name varchar(50),
Value varchar(50)
)
insert into #t(Name, Value)
values
('Year', '1990'),
('Year', '1991'),
('Year', '1992'),
('Cash', '2000'),
('Cash', '4000'),
('Cash', '6000'),
('Tax', ' Paid'),
('Tax', ' Unpaid'),
('Tax', ' Pending'),
('Liability', '3500'),
('Liability', '8500'),
('Liability', '9500'),
('Liability', '1500'),
('Liability', '2500'),
('Liability', '4500');
declare #sql nvarchar(max) = stuff(
(
select ',[value'+cast(rownum as varchar(20))+']'
from
(
select top((select max(cnt) from (select count(*) as cnt from #t group by Name) as t)) row_number() over(order by a.v) as rownum
from
(
values(cast(null as bit)),(null),(null),(null),(null),(null),(null),(null),(null),(null)
) as a(v)
cross join
(
values(cast(null as bit)),(null),(null),(null),(null),(null),(null),(null),(null),(null)
) as b(v)
cross join
(
values(cast(null as bit)),(null),(null),(null),(null),(null),(null),(null),(null),(null)
) as c(v)
) as nums
order by rownum
for xml path('')), 1, 1, N'');
select #sql = N'
select *
from
(
select *, ''value''+cast(row_number() over(partition by Name order by (select null)) as varchar(20)) as colname
from #t
) as t
pivot
(
max(value) for colname in (' + #sql + N')
) as unpv
';
exec(#sql);
--
drop table #t;

Variable within SQL query

I have this:
SELECT NEWID() as id,
'OwnerReassign' as name,
1 as TypeId,
'MyOrganisation' as OrgName,
'07DA8E53-74BD-459C-AF94-A037897A51E3' as SystemUserId,
0 as StatusId,
GETDATE() as CreatedAt,
'{"EntityName":"account","Ids":["'+CAST(AccountId as varchar(50))+'"],"OwnerId":"0C01C994-1205-E511-988E-26EE4189191B"}' as [Parameters]
FROM Account
WHERE OwnerIdName IN ('John Smith') AND New_AccountType = 1
Within the parameter field is an id (0C01C994-1205-E511-988E-26EE4189191B). Is it possible it could sequentially assign a different id from a list for each row? There are 5 id's in total.
What i'm trying to get to is this result set equally split between the 5 different id's.
Thanks
You can add one more NEWID() in the sub query and handle in the SELECT as below:
SELECT id, [name], TypeId, OrgName, SystemUserId, StatusId, CreatedAt,
'{"EntityName":"account","Ids":["' + AccountId +'"],"OwnerId":"' + ParamId + '"}' as [Parameters]
FROM (
SELECT NEWID() as id,
'OwnerReassign' as name,
1 as TypeId,
'MyOrganisation' as OrgName,
'07DA8E53-74BD-459C-AF94-A037897A51E3' as SystemUserId,
0 as StatusId,
GETDATE() as CreatedAt,
CAST(NEWID() AS VARCHAR (36)) as ParamId,
CAST(AccountId as varchar(50)) as AccountId
FROM Account
WHERE OwnerIdName IN ('John Smith') AND New_AccountType = 1
) A
You can use something like the following. Basically, use a row number for both your IDs and your data table to update, then do a MOD (%) operation with the amount of ID's you want to assign, so your data table to update is split into N groups. Then use that group ID to assign each ID.
IF OBJECT_ID('tempdb..#IDsToAssign') IS NOT NULL
DROP TABLE #IDsToAssign
CREATE TABLE #IDsToAssign (
IDToAssign VARCHAR(100))
-- 3 IDs example
INSERT INTO #IDsToAssign (
IDToAssign)
SELECT IDToAssign = NEWID()
UNION ALL
SELECT IDToAssign = NEWID()
UNION ALL
SELECT IDToAssign = NEWID()
DECLARE #AmountIDsToAssign INT = (SELECT COUNT(1) FROM #IDsToAssign)
IF OBJECT_ID('tempdb..#Account') IS NOT NULL
DROP TABLE #Account
CREATE TABLE #Account (
PrimaryKey INT PRIMARY KEY,
AssignedID VARCHAR(100))
-- 10 Rows example
INSERT INTO #Account (
PrimaryKey)
VALUES
(100),
(200),
(351),
(154),
(194),
(345),
(788),
(127),
(124),
(14)
;WITH DataRowNumber AS
(
SELECT
A.*,
RowNumber = ROW_NUMBER() OVER (ORDER BY (SELECT NULL))
FROM
#Account AS A
),
IDsRowNumbers AS
(
SELECT
D.IDToAssign,
RowNumber = ROW_NUMBER() OVER (ORDER BY D.IDToAssign)
FROM
#IDsToAssign AS D
),
NewIDAssignation AS
(
SELECT
R.*,
IDRowNumberAssignation = (R.RowNumber % #AmountIDsToAssign) + 1
FROM
DataRowNumber AS R
)
UPDATE A SET
AssignedID = R.IDToAssign
FROM
NewIDAssignation AS N
INNER JOIN IDsRowNumbers AS R ON N.IDRowNumberAssignation = R.RowNumber
INNER JOIN #Account AS A ON N.PrimaryKey = A.PrimaryKey
SELECT
*
FROM
#Account AS A
ORDER BY
A.AssignedID
/* Results:
PrimaryKey AssignedID
----------- ------------------------------------
124 1CC7F0F1-7EDE-4F7F-B0A3-739D74A62390
194 1CC7F0F1-7EDE-4F7F-B0A3-739D74A62390
351 1CC7F0F1-7EDE-4F7F-B0A3-739D74A62390
788 2A58A573-EDCB-428E-A87A-6BFCED265A9C
200 2A58A573-EDCB-428E-A87A-6BFCED265A9C
127 2A58A573-EDCB-428E-A87A-6BFCED265A9C
14 2A58A573-EDCB-428E-A87A-6BFCED265A9C
100 FD8036DA-0E15-453E-8A59-FA3C2BDB8FB1
154 FD8036DA-0E15-453E-8A59-FA3C2BDB8FB1
345 FD8036DA-0E15-453E-8A59-FA3C2BDB8FB1
*/
The ordering of the ROW_NUMBER() function will determine how ID's are assigned.
You could potentially do this by using the ROW_NUMBER() field in a subquery; for example:
SELECT NEWID() as id, 'OwnerReassign' as name, 1 as TypeId,
'MyOrganisation' as OrgName,
'07DA8E53-74BD-459C-AF94-A037897A51E3' as SystemUserId,
0 as StatusId, GETDATE() as CreatedAt,
case B / ##ROWCOUNT
when 0 then '0C01C994-1205-E511-988E-26EE4189191B'
when 1 then '12345677-1205-E511-988E-26EE4189191B'
when 2 then '66666666-1205-E511-988E-26EE4189191B'
etc...
end
FROM
(
SELECT ROW_NUMBER() OVER (ORDER BY A.Id)
FROM Account A
WHERE OwnerIdName IN ('John Smith') AND New_AccountType = 1
) AS B
If you want the system to pick those values then you could put then in their own temporary table, too.

Consolidate rows of data in SQL Server

I have multiple rows of order data which i need to consolidate in one row per part.
An example is as follows:
OrderNum PartNum Qty
-------------------------------
1 24 2
2 25 10
3 24 5
4 24 10
This then needs to be consolidated into:
OrderNum PartNum Qty
-------------------------------
1, 3, 4 24 17
2 25 10
Does anybody have any ideas how I can do this?
I have had a look around online but cannot find a solution to this use case.
Many thanks in advance,
Try this
SELECT STUFF((SELECT ',' + CAST(OrderNum AS VARCHAR(4))
FROM mytable AS s
WHERE s.PartNum = t.PartNum
FOR XML PATH('')), 1, 1, '') AS OrderNum
PartNum, SUM(Qty)
FROM mytable AS t
GROUP BY PartNum
This can be done by grouping on PartNum, sum the quantities with SUM() and concatenating strings using FOR XML PATH('') in a correlated subquery. Using FOR XML PATH('') to concatenate string is explained in this answer on SO.
DECLARE #t TABLE(OrderNum INT, PartNum INT, Qty INT);
INSERT INTO #t(OrderNum,PartNum,Qty)
VALUES(1,24,2),(2,25,10),(3,24,5),(4,24,10);
SELECT
OrderNum=STUFF((
SELECT
','+CAST(i.OrderNum AS VARCHAR)
FROM
#t AS i
WHERE
i.PartNum=o.PartNum
FOR XML
PATH(''), TYPE
).value('.[1]','VARCHAR(MAX)'),1,1,''),
o.PartNum,
Qty=SUM(o.Qty)
FROM
#t AS o
GROUP BY
o.PartNum;
Result:
OrderNum | PartNum | Qty
------------------------
1,3,4 | 24 | 17
2 | 25 | 10
SQL Server 2016 added the STRING_AGG function.
In your case, you could write
select STRING_ACC(OrderId,','),PartNum, Sum(Qty)
from MyTable
Group by PartNum
For earlier versions you'd have to use one of the techniques described by Aaron Bertrand in Grouped Concatenation in SQL Server. The fastest is to use a SQLCLR method. Next comes the FOR XML method posted by #GiorgosBetsos
DECLARE #t TABLE(OrderNum INT, PartNum INT, Qty INT)
INSERT INTO #t VALUES(1 , 24 , 2)
INSERT INTO #t VALUES(2 , 25 , 10)
INSERT INTO #t VALUES(3 , 24 , 5)
INSERT INTO #t VALUES(4 , 24 , 10)
SELECT OrderNum =
STUFF((SELECT ', ' + CONVERT(VARCHAR(50),OrderNum)
FROM #t b
WHERE b.PartNum = a.PartNum
FOR XML PATH('')), 1, 2, ''),
PartNum,
SUM(Qty) as Qty
FROM #t a
GROUP BY PartNum
Result
There are many ways to do this.
create table tablename (Name varchar(100), Rnk int)
Insert into tablename values
('Northshore', 1),
('F3', 2),
('Borderline', 3),
('Mattoon',3),
('Vinemane',5),
('Arizona',5),
('WestShore', 5),
('Schumburg', 5),
('Wilson',5)
--Method2
Select distinct
names= REPLACE(
(
Select a.Name as [data()]
From tablename A
Where A.Rnk = b.Rnk
Order by a.Name
FOR XML PATH ('') ), ' ', ',') ,Rnk
From tablename B Order by Rnk
OR
CREATE TABLE TestTable (ID INT, Col VARCHAR(4))
GO
INSERT INTO TestTable (ID, Col)
SELECT 1, 'A'
UNION ALL
SELECT 1, 'B'
UNION ALL
SELECT 1, 'C'
UNION ALL
SELECT 2, 'A'
UNION ALL
SELECT 2, 'B'
UNION ALL
SELECT 2, 'C'
UNION ALL
SELECT 2, 'D'
UNION ALL
SELECT 2, 'E'
GO
SELECT *
FROM TestTable
GO
-- Get CSV values
SELECT t.ID, STUFF(
(SELECT ',' + s.Col
FROM TestTable s
WHERE s.ID = t.ID
FOR XML PATH('')),1,1,'') AS CSV
FROM TestTable AS t
GROUP BY t.ID
GO
OR
CREATE TABLE #mable(mid INT, token nvarchar(16))
INSERT INTO #mable VALUES (0, 'foo')
INSERT INTO #mable VALUES(0, 'goo')
INSERT INTO #mable VALUES(1, 'hoo')
INSERT INTO #mable VALUES(1, 'moo')
SELECT m1.mid,
( SELECT m2.token + ','
FROM #mable m2
WHERE m2.mid = m1.mid
ORDER BY token
FOR XML PATH('') ) AS token
FROM #mable m1
GROUP BY m1.mid ;
Also, see this.
http://blog.sqlauthority.com/2009/11/25/sql-server-comma-separated-values-csv-from-table-column/

Write a query for this table

Imagine that i have a table like below,i want to write a query that give me below result,is it possible?
Result:
100 , 2015-01-01 , ABC , XYZ
You can use PIVOT.
Query
select userid,
[date],
[job],
[address]
from
(
select userid,name,[value] from tblName
)
as s
pivot
(
max([value]) for [name] in ([date], [job], [address])
) as p;
SQL Fiddle
OR
Query
select userid,
max(case when name = 'date' then [value] else null end) as [date],
max(case when name = 'job' then [value] else null end) as job,
max(case when name = 'address' then [value] else null end) as address
from tblName
group by userid;
SQL Fiddle
Another way,
try using Group_Concatenate and Common Table Expression
;with cte as
(select userid,
STUFF((
SELECT ',' + md.[value]
FROM tblName md
WHERE m.userid = md.userid
FOR XML PATH(''), TYPE).value('.', 'NVARCHAR(MAX)'), 1, 1, '')
as result
from tblName m group by m.userid
)
select (cast(userid as varchar(50))+','+result) as res from cte
see Demo Here

How do I get distinct COUNT in pivot?

I have a following table:
State LAB GROUP DATE CODE ID
UP A I 1-Jan 1 345
UP R S 1-Feb 1 456
UP A S 1-Jan 2 567
DL R S 1-Feb 3 678
DL T S 1-Jan 1 789
DL A S 1-Jan 2 900
MN T S 1-Jan 3 1011
MN R I 1-Feb 1 1122
MN S I 1-Feb 2 1233
I need a pivot table of following type:
STATE A R T TOTAL
UP 2 1 0 3
DL 1 1 1 3
MN 0 1 1 2
DISTINCT COUNT OF ID FOR EACH LAB FOR EACH STATE.
I then need the pivot tables filtered for following columns:
GROUP
DATE
CODE
So 1st table will have the pivot table above counting only those records which have GROUP=S
2nd table will have the pivot table above counting only those records which have CODE=1
and so on, I wish to put multiple conditions. and generate several tables one by one and export them.
If this is possible in SQL please let me know! I ruled out excel vba due to the size of table (source table will have 800,000 records approx).
Try this :-
Select [State],[A],[R],[T],Total = [A] + [R]+ [T]
from
(
Select [State],
[A] = Sum(Case when LAB='A' then 1 else 0 END) ,
[R] = Sum(Case when LAB='R' then 1 else 0 END) ,
[T] = Sum(Case when LAB='T' then 1 else 0 END)
from YourTable
group by [State]
)a
SQL FIDDLE
CREATE TABLE #t(States VARCHAR(10),LAB VARCHAR(5),GROUPs VARCHAR(5),DATEs VARCHAR(10),CODE INT,ID INT)
INSERT INTO #t values('UP','A','I','1-Jan',1,345)
INSERT INTO #t values('UP','R','S','1-Feb',1,456)
INSERT INTO #t values('UP','A','S','1-Jan',2,567)
INSERT INTO #t values('DL','R','S','1-Feb',3,678)
INSERT INTO #t values('DL','T','S','1-Jan',1,789)
INSERT INTO #t values('DL','A','S','1-Jan',2,900)
INSERT INTO #t values('MN','T','S','1-Jan',3,1011)
INSERT INTO #t values('MN','R','I','1-Feb',1,1122)
INSERT INTO #t values('MN','S','I','1-Feb',2,1233)
SELECT States,ISNULL(A,0) A,ISNULL(R,0) R,ISNULL(T,0) T,ISNULL(A,0)+ISNULL(R,0)+ISNULL(T,0) total
FROM
(
SELECT States,LAB,Count(ID) AS cnt FROM #t GROUP BY States,LAB /*apply GROUP DATE CODE condition here*/
) AS PVT
PIVOT(MAX(cnt) FOR LAB IN (A,R,T)) pvt
Another solution using PIVOT :
WITH PivotInUse AS (
SELECT state,lab,COUNT(*) AS cnt
FROM YourTable
GROUP BY state,lab
)
SELECT STATE
,COALESCE([A], 0) AS A
,COALESCE([R], 0) AS R
,COALESCE([T], 0) AS T
,COALESCE([A], 0) + COALESCE([R], 0) + COALESCE([T], 0) AS TOTAL
FROM PivotInUse
PIVOT(SUM(cnt) FOR lab IN ([A],[R],[T])) AS p;
Your sample table
SELECT * INTO #TEMP FROM
(
SELECT 'UP' [State],'A' LAB,'I' [GROUP],'1-Jan' [DATE],1 CODE,345 ID
UNION ALL
SELECT 'UP','R','S','1-Feb',1,456
UNION ALL
SELECT 'UP','A','S','1-Jan',2,567
UNION ALL
SELECT 'DL','R','S','1-Feb',3,678
UNION ALL
SELECT 'DL','T','S','1-Jan',1,789
UNION ALL
SELECT 'DL','A','S','1-Jan',2,900
UNION ALL
SELECT 'MN','T','S','1-Jan',3,1011
UNION ALL
SELECT 'MN','R','I','1-Feb',1,1122
UNION ALL
SELECT 'MN','S','I','1-Feb',2,1233
)TAB
Now you need to get the distinct count of each state and get the sum as the result to show Total
in pivoted result.
SELECT DISTINCT [State],LAB,SUM(CNT) CNT
INTO #NEWTABLE
FROM
(
SELECT DISTINCT
[State],LAB,
CASE WHEN [State] IS NULL THEN NULL ELSE COUNT([State]) OVER(PARTITION BY [State],LAB) END CNT
FROM #TEMP
)TAB
GROUP BY [State],LAB
WITH ROLLUP
Now we need to get the distinct columns for pivot(#cols) and columns to identify and replace null with zero in pivot(#NullToZeroCols)
DECLARE #cols NVARCHAR (MAX)
DECLARE #NullToZeroCols NVARCHAR (MAX)
SET #cols = SUBSTRING((SELECT DISTINCT ',['+LAB+']' FROM #NEWTABLE GROUP BY LAB FOR XML PATH('')),2,8000)
SET #NullToZeroCols = SUBSTRING((SELECT DISTINCT ',ISNULL(['+LAB+'],0) AS ['+LAB+']'
FROM #NEWTABLE GROUP BY LAB FOR XML PATH('')),2,8000)
Join the pivotted query with the #NEWTABLE to get the Total for each State
DECLARE #query NVARCHAR(MAX)
SET #query = 'SELECT P.State,' + #NullToZeroCols + ',T2.CNT TOTAL FROM
(
SELECT DISTINCT [State],LAB,CNT FROM #NEWTABLE
) x
PIVOT
(
SUM(CNT)
FOR [LAB] IN (' + #cols + ')
) p
JOIN #NEWTABLE T2 ON P.[STATE]=T2.[STATE]
WHERE P.State IS NOT NULL AND T2.LAB IS NULL AND T2.[STATE] IS NOT NULL;'
EXEC SP_EXECUTESQL #query
Here is your result
Here is the SQLFiddle http://sqlfiddle.com/#!3/c2588/1 (If it shows any error while loading the page, just click RUNSQL, it will work)
Now if you want to get the result as you said DISTINCT COUNT OF ID FOR EACH LAB FOR EACH STATE, just change
OVER(PARTITION BY [State],LAB)
to
OVER(PARTITION BY [State],LAB,Id)
which will show the following result after executing the pivot query

Resources