-
Ronen Ariely 14,886 Reputation points MVP
2022-11-15T21:43:03.63+00:00 Hi,
Here is the solution for the new request:
Note! This will work only if the values do not include dot
.
since I use dot as the separator between the ID and the name. If there can be a dot in the values then we simply need to use different separator in the query but the solution is the same.;With MyCTE_00 as ( SELECT RN = ROW_Number() OVER (ORDER BY (SELECT NULL)), e.CanceledPendVerifyPharmacyUserID, e.DispensePharmacyUserID, e.PendVerifyPharmacyUserID, e.VerifyPharmacyUserID FROM #SummaryDispense e ) ,MyCTE_01 as ( select p.RN, p.ColumnName, p.UserID, e.UserName from MyCTE_00 unpivot(UserID for ColumnName in ( [DispensePharmacyUserID],[PendVerifyPharmacyUserID], [CanceledPendVerifyPharmacyUserID],[VerifyPharmacyUserID] )) as P LEFT JOIN #Employee e ON p.UserID = e.UserID ) , MyCTE02 as ( SELECT [DispensePharmacyUserID],[PendVerifyPharmacyUserID], [CanceledPendVerifyPharmacyUserID],[VerifyPharmacyUserID] FROM ( SELECT RN, ColumnName, UserName = UserName + '.' + CONVERT(VARCHAR(10), UserID) FROM MyCTE_01 ) AS TableToPivot PIVOT ( MIN(UserName) FOR ColumnName IN ([DispensePharmacyUserID],[PendVerifyPharmacyUserID], [CanceledPendVerifyPharmacyUserID],[VerifyPharmacyUserID]) ) AS PivotTable ) SELECT PARSENAME([DispensePharmacyUserID] ,1) as [DispensePharmacyUserID] , PARSENAME([DispensePharmacyUserID] ,2) as [DispensePharmacyUserName] , PARSENAME([PendVerifyPharmacyUserID] ,1) as [PendVerifyPharmacyUserID] , PARSENAME([PendVerifyPharmacyUserID] ,2) as [PendVerifyPharmacyUserName] , PARSENAME([CanceledPendVerifyPharmacyUserID] ,1) as [CanceledPendVerifyPharmacyUserID], PARSENAME([CanceledPendVerifyPharmacyUserID] ,2) as [CanceledPendVerifyPharmacyUserName], PARSENAME([VerifyPharmacyUserID] ,1) as [VerifyPharmacyUserID] , PARSENAME([VerifyPharmacyUserID] ,2) as [VerifyPharmacyUserName] FROM MyCTE02 GO
Good day,
You can avoid JOIN the same multiple times (which behind the scene lead to scanning the same table multiple times) by using UNPIVOT and PIVOT.
In this simple example when we are using a small tables without any index, the following execution plan seems better, but you should test the solution in your real database to make the decision.
Here is the solution:
;With MyCTE_00 as (
SELECT
RN = ROW_Number() OVER (ORDER BY (SELECT NULL)),
e.CanceledPendVerifyPharmacyUserID,
e.DispensePharmacyUserID,
e.PendVerifyPharmacyUserID,
e.VerifyPharmacyUserID
FROM #SummaryDispense e
)
,MyCTE_01 as (
select p.RN, p.ColumnName, p.UserID, e.UserName
from MyCTE_00
unpivot(UserID for ColumnName in (
[DispensePharmacyUserID],[PendVerifyPharmacyUserID], [CanceledPendVerifyPharmacyUserID],[VerifyPharmacyUserID]
)) as P
LEFT JOIN #Employee e ON p.UserID = e.UserID
)
SELECT [DispensePharmacyUserID],[PendVerifyPharmacyUserID], [CanceledPendVerifyPharmacyUserID],[VerifyPharmacyUserID]
FROM
(
SELECT RN, ColumnName, UserName
FROM MyCTE_01
) AS TableToPivot
PIVOT
(
MIN(UserName)
FOR ColumnName IN ([DispensePharmacyUserID],[PendVerifyPharmacyUserID], [CanceledPendVerifyPharmacyUserID],[VerifyPharmacyUserID])
) AS PivotTable;
GO
Notice that in the second Execution Plan (my query) you only scan the #Employee table once and the total relative cost is only 31% vs your original query which scan the #Employee 4 times and cost 69% (this is less than half the cost).
With that said, as mentioned you must check that this behaves the same in your real production table where you (hopefully) and indexes and probably other objects which might change the Execution Plan (check the IO and the CPU as well using SET STATISTICS).