SQL server join tables and pivot
This should work:
WITH Sales AS (
SELECT
S.SaleID,
S.SoldBy,
S.SalePrice,
S.Margin,
S.Date,
I.SalePrice,
I.Category
FROM
dbo.Sale S
INNER JOIN dbo.SaleItem I
ON S.SaleID = I.SaleID
)
SELECT *
FROM
Sales
PIVOT (Max(SalePrice) FOR Category IN (Books, Printing, DVD)) P
;
Or alternately:
SELECT
S.SaleID,
S.SoldBy,
S.SalePrice,
S.Margin,
S.Date,
I.Books,
I.Printing,
I.DVD
FROM
dbo.Sale S
INNER JOIN (
SELECT *
FROM
(SELECT SaleID, SalePrice, Category FROM dbo.SaleItem) I
PIVOT (Max(SalePrice) FOR Category IN (Books, Printing, DVD)) P
) I ON S.SaleID = I.SaleID
;
These have the same result set and may in fact be treated the same by the query optimizer, but possibly not. The big difference comes into play when you start putting conditions on the Sale
table—you should test and see which query works better.
Note: it is crucial when using PIVOT
that only the columns that should be part of the resulting output are available. This is why the two above queries have extra derived table subqueries (SELECT ...)
so that only specific columns are exposed. All columns that are available to be seen by PIVOT
that aren't listed in the pivot expression will implicitly be grouped on and included in the final output. This will likely not be what you want.
May I suggest, however, that you do the pivoting in the presentation layer? If, for example, you are using SSRS it is quite easy to use a matrix control that will do all the pivoting for you. That is best, because then if you add a new Category
, you won't have to modify all your SQL code!
There is a way to dynamically find the column names to pivot, but it involves dynamic SQL. I don't really recommend that as the best way, either, though it is possible.
Another way that could work would be to preprocess this query—meaning to set a trigger on the Category
table that rewrites a view to contain all the extant categories that exist. This does solve a lot of the other problems I've mentioned, but again, using the presentation layer is best.
Note: If your column names (that were formerly values) have spaces, are numbers or begin with a number, or are otherwise not valid identifiers, you must quote them with square brackets as in PIVOT (Max(Value) FOR CategoryId IN ([1], [2], [3], [4])) P
. Alternately, you can modify the values before they get to the PIVOT
part of the query to prepend some letters or remove spaces, so that the column list doesn't need escaping. For further reading on this check out the rules for identifiers in SQL Server.
Join two tables, group and pivot in SQL
You want to unpivot. In SQL Server I would recommend cross apply
:
select t1.id, t1.date, x.field, x.pivot, t2.sku
from table1 t1
inner join table2 t2 on t2.id = t1.id and t1.date = t2.date
cross apply (values (t1.avail, 'avail'), (t1.total - t1.avail, 'used')) as x(field, pivot)
It is not clear which logic you want for column group
(which, by the way, is a language keyword, so not a good choice for a column name). Based on your attempt, it looks like you want to assign that based on the sku. If so, use a case
expression:
select t1.id, t1.date, x.field, x.pivot, t2.sku,
case
when t2.sku like 'Ax%' or t2.sku like 'At%' then 'group1'
when t2.sku like 'By%' or t2.sku like 'Bt%' then 'group2'
end as grp
from table1 t1
inner join table2 t2 on t2.id = t1.id and t1.date = t2.date
cross apply (values (t1.avail, 'avail'), (t1.total - t1.avail, 'used')) as x(field, pivot)
Using PIVOT and JOIN together
WITH T
AS (SELECT [Order Details].OrderID,
c.CategoryName,
c.CategoryID
FROM [Order Details]
INNER JOIN Products p
ON p.ProductID = [Order Details].ProductID
INNER JOIN Categories c
ON c.CategoryID = p.CategoryID)
SELECT *
FROM T PIVOT ( COUNT (CategoryID) FOR CategoryName IN (
[Beverages],
[Condiments],
[Confections],
[Dairy Products],
[Grains/Cereals],
[Meat/Poultry],
[Produce],
[Seafood]) ) AS pvt
ORDER BY OrderID
SQL SERVER PIVOT table with joins and dynamic columns
Since you want to transform data from rows into columns, then you will want to use the PIVOT function. If you have a limited number or known values, then you can hard-code the query:
select plan_id, [2012, November], [2012, December], [2013, January], [2013, February]
from
(
SELECT
b.plan_id,
(Convert(varchar(4),b.run_year) + ', ' + DateName(month,CAST('1900-' + Convert(varchar(2),b.run_month) + '-01' AS DATETIME))) AS billdate,
ISNULL(b.total_premium,0) + ISNULL(a.total_adj,0) AS total
FROM cteBills b
LEFT JOIN cteBillsAdj a
ON a.run_month = b.run_month
AND b.run_year = a.run_year
AND b.plan_id = a.plan_id
) d
pivot
(
sum(total)
for billdate in ([2012, November], [2012, December], [2013, January], [2013, February])
) piv;
But if you have an unknown number of values, then you will need to implement dynamic SQL:
DECLARE @cols AS NVARCHAR(MAX),
@query AS NVARCHAR(MAX)
select @cols = STUFF((SELECT ',' + QUOTENAME(Convert(varchar(4),b.run_year) + ', ' + DateName(month,CAST('1900-' + Convert(varchar(2),b.run_month) + '-01' AS DATETIME))) )
from cteBills
group by b.run_year, b.run_month
order by b.run_year, b.run_month
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
set @query = 'SELECT plan_id, ' + @cols + '
from
(
SELECT
b.plan_id,
(Convert(varchar(4),b.run_year) + '', '' + DateName(month,CAST(''1900-'' + Convert(varchar(2),b.run_month) + ''-01'' AS DATETIME))) AS billdate,
ISNULL(b.total_premium,0) + ISNULL(a.total_adj,0) AS total
FROM cteBills b
LEFT JOIN cteBillsAdj a
ON a.run_month = b.run_month
AND b.run_year = a.run_year
AND b.plan_id = a.plan_id
) x
pivot
(
sum(total)
for billdate in (' + @cols + ')
) p '
execute sp_executesql @query;
SQL PIvot for a complex join
You can do it with a CROSS
join of the tables and conditional aggregation:
SELECT e.EMPLOYEEID
FROM managers m CROSS JOIN employees e
WHERE m.MANAGERID = 1
GROUP BY e.EMPLOYEEID
HAVING COUNT(DISTINCT m."GROUP") =
COUNT(CASE WHEN e."GROUP" = m."GROUP" AND e."VALUE" = m."VALUE" THEN 1 END)
See the demo.
Results:
EMPLOYEEID |
---|
101 |
102 |
103 |
SQL Self Join / Pivot table query
CREATE TABLE #myTable (
[Year] int, [Type] int, [Value] int, [ExtraColumn] varchar(10));
INSERT INTO #myTable ([Year], [Type], [Value], [ExtraColumn])
VALUES (2010, 1, 10, 'I'),
(2010, 2, 15, 'G'),
(2010, 3, 20, 'N'),
(2011, 1, 100, 'O'),
(2011, 2, 150, 'R'),
(2011, 3, 200, 'E');
select Year, [1] as Type1, [2] as Type2, [3] as Type3
from (
select [Year], [Type], [Value]
from #myTable
) t
PIVOT ( SUM(Value) FOR [Type] IN ( [1], [2], [3] ) ) pvt;
-- OR
with myData as
(
select [Year], [Type], [Value]
from #myTable
)
select Year, [1] as Type1, [2] as Type2, [3] as Type3
from myData
PIVOT ( SUM(Value) FOR [Type] IN ( [1], [2], [3] ) ) pvt;
drop table #myTable;
Related Topics
Ora-01652: Unable to Extend Temp Segment by 128 in Tablespace System: How to Extend
SQL Query to Obtain Value That Occurs More Than Once
Select Without a from Clause in Oracle
Extbase - Get Created SQL from Query
Convert from Date to Epoch-Oracle
Create Postgresql Role (User) If It Doesn't Exist
Get Previous and Next Row from Rows Selected with (Where) Conditions
How to Convert Unix Epoch Time in SQLite
SQL Error: Misuse of Aggregate
How to Delete the Top 1000 Rows from a Table Using SQL Server 2008
SQL Server 2005 and Temporary Table Scope
How to Improve Performance for Datetime Filtering in SQL Server
Pseudo_Encrypt() Function in Plpgsql That Takes Bigint
Get SQL Xml Attribute Value Using Variable
Oracle Trigger Error Ora-04091
How to Report an Error from a SQL Server User-Defined Function