Como otros han dicho, puede utilizar el pivote y los operadores UNPIVOT. Desafortunadamente, uno de los problemas tanto con PIVOT como con UNPIVOT es que necesita conocer los valores con los que pivotará de antemano o bien usar SQL dinámico.
Parece que, en su caso, va a necesitar usar SQL dinámico. Para que esto funcione bien, deberá obtener una lista de los productos que se utilizan en su consulta. Si estaba utilizando la base de datos AdventureWorks, su código se vería así:
USE AdventureWorks;
GO
DECLARE @columns NVARCHAR(MAX);
SELECT x.ProductName
INTO #products
FROM (SELECT p.[Name] AS ProductName
FROM Purchasing.Vendor AS v
INNER JOIN Purchasing.PurchaseOrderHeader AS poh ON v.VendorID = poh.VendorID
INNER JOIN Purchasing.PurchaseOrderDetail AS pod ON poh.PurchaseOrderID = pod.PurchaseOrderID
INNER JOIN Production.Product AS p ON pod.ProductID = p.ProductID
GROUP BY p.[Name]) AS x;
SELECT @columns = STUFF(
(SELECT ', ' + QUOTENAME(ProductName, '[') AS [text()]
FROM #products FOR XML PATH ('')
), 1, 1, '');
SELECT @columns;
Ahora que tiene sus columnas, se puede tirar todo lo que necesita girar con una consulta dinámica:
DECLARE @sql NVARCHAR(MAX);
SET @sql = 'SELECT CustomerName, ' + @columns + '
FROM (
// your query goes here
) AS source
PIVOT (SUM(order_count) FOR product_name IN (' + @columns + ') AS p';
EXEC sp_executesql @sql
Por supuesto, si necesita asegurarse de obtener valores decentes, es posible que deba duplicar la lógica que está utilizando para compilar @columns y crear una variable @coalesceColumns que contenga el código en COALESCE (col_name, 0) si necesita ese tipo de cosas en tu consulta.