SQL Self Join / Pivot table query - sql-server

I have the following Table1 in SQL Server 2016:
SELECT Year, Type, Value From Table1
Year Type Value
2010 1 10
2010 2 15
2010 3 20
2011 1 100
2011 2 150
2011 3 200
I would like to convert it to the following table:
Year Type1 Type2 Type3
2010 10 15 20
2011 100 150 200
I think we can do either self join or pivot table to achieve this. What is the best way to achieve this?

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;

Assuming you always have 3 types using conditional aggregation is a simple way to tackle this.
select [Year]
, Type1 = Max(case when [Type] = 1 then Value end)
, Type2 = Max(case when [Type] = 2 then Value end)
, Type3 = Max(case when [Type] = 3 then Value end)
from Table1
group by [Year]
order by [Year]

select *
from myTable PIVOT ( SUM(Value) FOR [Type] IN ( [1], [2], [3] ) ) pvt;
DbFiddle demo

Assuming you always have 3 types, you can use PIVOT in SQL.
Here is an example based on your example:
if object_id('tempdb..#temp1') is not null
drop table #temp1
create table #temp1 (
Year int
,Type int
,Value int
)
insert into #temp1 values
(2010,1,10),
(2010,2,15),
(2010,3,20),
(2011,1,100),
(2011,2,150),
(2011,3,200)
SELECT
Year
, [1] AS Type1
, [2] AS Type2
, [3] AS Type3
FROM
#temp1 p
PIVOT
(
sum(value)
FOR type IN
( [1], [2], [3])
) AS pvt
ORDER BY pvt.Year
Here are the results:

Related

Pivot table missing data in sum in SQL Server

I have the following reference table:
CompanyId ProductType ProductCount ProductBought
3 1 12 12
3 2 5 5
3 4 5 5
Then I'm pivoting the table by ProductType:
SELECT
CompanyId,
SUM(ProductBought) AS ProductBought
SUM(ISNULL([1], 0)) AS [1],
SUM(ISNULL([2], 0)) AS [2],
SUM(ISNULL([3], 0)) AS [3],
SUM(ISNULL([4], 0)) AS [4]
FROM (
SELECT * FROM #ReferenceTable
) AS a
PIVOT (
SUM([ProductCount]) FOR ProductType IN ([1], [2], [3], [4])
) as pvt
GROUP BY pvt.CompanyId
This gives the following result:
CompanyId ProductBought 1 2 3 4
3 17 12 5 0 5
I expect the ProductBought value to be 22, so in the pivot, five are going missing.
How can I achieve the full count of ProductBought with the pivot table?
In your query amounts are grouping up based on ProductBrought column. Since for both ProductType 2,4 ProductBrought values are 5 . The data gets grouped up on one single 5 . Just separate them with a simple Row Id column and try.
SELECT * INTO #TEMP FROM
(
SELECT 3 companyId, 1 ProductType,12 ProductCount,12 ProductBought
UNION ALL
SELECT 3, 2,5 ,5 UNION ALL
SELECT 3, 4,5 ,5
)
AS A
Query
SELECT SUM(ProductBought)ProductBought,SUM([1]) [1],SUM([2])[2],SUM([3])[3],SUM([4])[4]
FROM (
SELECT ROW_NUMBER()OVER(PARTITION BY companyId ORDER BY (SELECT 1 )DESC)RN,* FROM #TEMP
) AS A
PIVOT ( SUM( ProductCOUNT ) FOR ProductType IN([1],[2],[3],[4])
)AS B
GROUP BY COMPANYID
Try This
;WITH CTE(CompanyId, ProductType, ProductCount, ProductBought)
AS
(
SELECT 3, 1,12,12 UNION ALL
SELECT 3, 2,5 ,5 UNION ALL
SELECT 3, 4,5 ,5
)
SELECT CompanyId,
ProductBought,
ISNULL(SUM([1]),0) AS [1],
ISNULL(SUM([2]),0) AS [2],
ISNULL(SUM([3]),0) AS [3],
ISNULL(SUM([4]),0) AS [4]
FROM
(
SELECT CompanyId,
ProductType,
ProductCount,
SUM(ProductBought)OVER(ORDER BY CompanyId) AS ProductBought
FROM CTE
)AS SRC
PIVOT
(
SUM(ProductCount) FOR ProductType IN ([1],[2],[3],[4])
)
AS PVT
GROUP BY CompanyId,
ProductBought
Result
CompanyId ProductBought 1 2 3 4
------------------------------------------------
3 22 12 5 0 5
You can sum separately and join with pivot results as below.
SELECT MainTable.CompanyId, MainTable.ProductBought, PivotTable.[1], PivotTable.[2],
PivotTable.[3], PivotTable.[4]
FROM
(
SELECT ReferenceTable.CompanyId, SUM(ProductBought) AS ProductBought FROM
ReferenceTable
GROUP BY CompanyId
) MainTable
INNER JOIN
(
SELECT
CompanyId,
SUM(ISNULL([1], 0)) AS [1],
SUM(ISNULL([2], 0)) AS [2],
SUM(ISNULL([3], 0)) AS [3],
SUM(ISNULL([4], 0)) AS [4]
FROM (
SELECT * FROM dbo.ReferenceTable
) AS a
PIVOT (
SUM([ProductCount]) FOR ProductType IN ([1], [2], [3], [4])
) as pvt
GROUP BY pvt.CompanyId
) PivotTable
ON PivotTable.CompanyId = MainTable.CompanyId

How to use Unpivot properly

I am a beginner in SQL. I want to convert some columns into rows inside a view, this is my view:
SLPRSNID ITEMNMBR 1 2 3 4 5
1 01-GOGUA-010 0 500 500 500 500
4 01-GOGUA-020 1850 3850 2350 4450 2450
I need:
SLPRSNID ITEMNMBR VOLUME
1 01-GOGUA-010 2000
4 01-GOGUA-020 14950
I used:
SELECT ITEMNMBR, VOLUMEN
FROM dbo.SOPPPTOVOL
UNPIVOT
(
VOLUMEN
FOR ITEMNMBR IN ([1], [2], [3], [4], [5])
) AS UnPvt
But it doesn't work.
using APPLY
DECLARE #table table (
SLPRSNID INT ,
ITEMNMBR VARCHAR(50),
[1] FLOAT,
[2] FLOAT,
[3] FLOAT,
[4] FLOAT,
[5] FLOAT
)
INSERT INTO #table
VALUES (1, '01-GOGUA-010', 0.00,500.00,500.00,500.00,500.00),
(4, '01-GOGUA-020', 1850.0,3850.00,2350.00,4450.00,2450.00)
SELECT T.SLPRSNID,T.ITEMNMBR, P.VOLUME
FROM #table T
CROSS APPLY
(
SELECT SUM(X.VAL)
FROM
(
VALUES ([1]),([2]),([3]),([4]),([5])
) X(VAL)
) P(VOLUME)
Using UNPIVOT
DECLARE #table table (
SLPRSNID INT ,
ITEMNMBR VARCHAR(50),
[1] FLOAT,
[2] FLOAT,
[3] FLOAT,
[4] FLOAT,
[5] FLOAT
)
INSERT INTO #table
VALUES (1, '01-GOGUA-010', 0.00,500.00,500.00,500.00,500.00),
(4, '01-GOGUA-020', 1850.0,3850.00,2350.00,4450.00,2450.00)
SELECT E.SLPRSNID , E.ITEMNMBR , SUM(E.VOL) VOLUME
FROM #table T
UNPIVOT
(
VOL FOR VALS IN ([1],[2],[3],[4],[5])
) E
GROUP BY E.SLPRSNID , E.ITEMNMBR

Query to Find Max row based on N fields

Given the following data:
ID Name, Value, TimeStamp
1, 'A', 7.00, 21/12/2017
2, 'A', 5.00, 21/12/2017
3, 'A', 6.00, 20/12/2017
4, 'B', 1.00, 21/12/2017
Result I want is:
Name, Value, TimeStamp
'A', 5.00, 21/12/2017
'B', 1.00, 21/12/2017
I.e. group by Name and take value with latest TimeStamp, if 2 or more have the same TimeStamp take the one with latest ID
I did seem to find an answer that was similar to another post:
SELECT ID, Name, Value, TimeStamp
FROM MyTable
JOIN ( SELECT Name, MAX(TimeStamp) As TimeStamp
FROM MyTable
GROUP BY Name ) m
ON MyTable.Name = m.Name and MyTable.TimeStamp = a.TimeStamp
This gives me the max timestamp so to get the id, I can repeat the process, i.e. I can use:
WITH CTE AS (
...
)
SELECT Name, Value, TimeStamp
FROM CTE
JOIN ( SELECT Name, MAX(ID)
FROM CTE
GROUP BY Name ) a
ON CTE.Name = a.Name AND CTE.ID = a.ID
However, what happens if I now want to scale it up to 3 fields. Is there an easier way to do this, without experimenting I was thinking recursive CTE. Trying to avoid dynamic sql.
I think you may want to use the ROW_NUMBER function for this. Below is an example.
SQL Example
;WITH
test_data
AS
(
SELECT tbl.* FROM (VALUES
( 1, 'A', 7.00, '21-Dec-2017')
, ( 2, 'A', 5.00, '21-Dec-2017')
, ( 3, 'A', 6.00, '20-Dec-2017')
, ( 4, 'B', 1.00, '21-Dec-2017')
) tbl ([ID], [Name], [Value], [TimeStamp])
)
,
test_data_order
AS
(
SELECT
[ID]
, [Name]
, [Value]
, [TimeStamp]
, EX_ROW_NUMBER = ROW_NUMBER() OVER
(
PARTITION BY
[Name]
ORDER BY
[TimeStamp] DESC
, [ID] DESC
)
FROM
test_data
)
SELECT
*
FROM
test_data_order
WHERE
EX_ROW_NUMBER = 1
db<>fiddle
Results
Try this
SELECT Name, Value, TimeStamp
FROM (
SELECT ID, Name, Value, TimeStamp
, ROW_NUMBER() OVER (PARTITION BY Name ORDER BY TimeStamp DESC, ID DESC) AS RowNum
) b
WHERE RowNum = 1

How to use pivot in this table

Actual Table Structure
===================================
slno ParnetID ParnetName Promotion Marketer
1 SLM1010S SKR.RAJASHEGARAN 2 43640
2 40049 M.KANNAN 3 43640
3 40018 M.PRABU 6 43640
4 SLM1010S SKR.RAJASHEGARAN 2 43641
5 40042 M.KANNAN 3 43641
6 40011 M.PRABU 6 43641
i have my query :
WITH temp
AS (SELECT slno,
parentid,
parentname,
parentpromotionid,
marketerid,
(SELECT Count(*)
FROM parentmaster
WHERE t1.marketerid = marketerid
AND t1.parentid = parentid
AND t1.parentname = parentname
AND slno <= t1.slno) AS RowNum
FROM parentmaster AS t1)
SELECT marketerid,
[2],
[6],
[3]
FROM temp
PIVOT ( Min(parentid)
FOR parentpromotionid IN ([2],
[6],
[3]) ) AS t
But I want This Table Structure
MarketerID 2 6 3
43640 SLM1010S 40018 40049
43641 SLM1010S 40011 40042
This may help u..
select marketer,[2],[3],[6] from
(
select Marketer,Promotion,ParnetID
from parentmaster
) d pivot (min(ParnetID) for Promotion in ([2],[3],[6])) as pvt
try this,
Declare #t table(slno int,ParnetID varchar(50),ParnetName varchar(50),Promotion int,Marketer int)
insert into #t
select 1,'SLM1010S','SKR.RAJASHEGARAN', 2, 43640 union all
select 2,'40049','M.KANNAN', 3, 43640 union all
select 3,'40018', 'M.PRABU', 6, 43640 union all
select 4,'SLM1010S', 'SKR.RAJASHEGARAN', 2, 43641 union all
select 5,'40042', 'M.KANNAN', 3, 43641 union all
select 6,'40011', 'M.PRABU', 6, 43641
select distinct a.Marketer,b.ParnetID ,c.ParnetID,d.ParnetID from #t a
left join #t b on a.Marketer=b.Marketer and b.promotion=2
left join #t c on a.Marketer=c.Marketer and c.promotion=3
left join #t d on a.Marketer=d.Marketer and d.promotion=6
select Marketer, [0],[1],[2],[3],[4],[5],[6]
from (
select Marketer,Promotion,ParnetID
from #t
) d
pivot
(
MIN(ParnetID)
FOR Promotion
IN ([0],[1],[2],[3],[4],[5],[6])
) as TST

Multiple Column Pivot in T-SQL

I am working with a table where there are multiple rows that I need pivoted into columns. So the pivot is the perfect solution for this, and works well when all I need is one field. I am needing to return several fields based upon the pivot. Here is the pseudo code with specifics stripped out:
SELECT
field1,
[1], [2], [3], [4]
FROM
(
SELECT
field1,
field2,
(ROW_NUMBER() OVER(PARTITION BY field1 ORDER BY field2)) RowID
FROM tblname
) AS SourceTable
PIVOT
(
MAX(field2)
FOR RowID IN ([1], [2], [3], [4])
) AS PivotTable;
The above syntax works brilliantly, but what do I do when I need to get additional information found in field3, field4....?
Rewrite using MAX(CASE...) and GROUP BY:
select
field1
, [1] = max(case when RowID = 1 then field2 end)
, [2] = max(case when RowID = 2 then field2 end)
, [3] = max(case when RowID = 3 then field2 end)
, [4] = max(case when RowID = 4 then field2 end)
from (
select
field1
, field2
, RowID = row_number() over (partition by field1 order by field2)
from tblname
) SourceTable
group by
field1
From there you can add in field3, field4, etc.
The trick to doing multiple pivots over a row_number is to modify that row number sequence to store both the sequence and the field number. Here's an example that does what you want with multiple PIVOT statements.
-- populate some test data
if object_id('tempdb..#tmp') is not null drop table #tmp
create table #tmp (
ID int identity(1,1) not null,
MainField varchar(100),
ThatField int,
ThatOtherField datetime
)
insert into #tmp (MainField, ThatField, ThatOtherField)
select 'A', 10, '1/1/2000' union all
select 'A', 20, '2/1/2000' union all
select 'A', 30, '3/1/2000' union all
select 'B', 10, '1/1/2001' union all
select 'B', 20, '2/1/2001' union all
select 'B', 30, '3/1/2001' union all
select 'B', 40, '4/1/2001' union all
select 'C', 10, '1/1/2002' union all
select 'D', 10, '1/1/2000' union all
select 'D', 20, '2/1/2000' --union all
-- pivot over multiple columns using the 1.1, 1.2, 2.1, 2.2 sequence trick
select
MainField,
max([1.1]) as ThatField1,
max([1.2]) as ThatOtherField1,
max([2.1]) as ThatField2,
max([2.2]) as ThatOtherField2,
max([3.1]) as ThatField3,
max([3.2]) as ThatOtherField3,
max([4.1]) as ThatField4,
max([4.2]) as ThatOtherField4
from
(
select x.*,
cast(row_number() over (partition by MainField order by ThatField) as varchar(2)) + '.1' as ThatFieldSequence,
cast(row_number() over (partition by MainField order by ThatField) as varchar(2)) + '.2' as ThatOtherFieldSequence
from #tmp x
) a
pivot (
max(ThatField) for ThatFieldSequence in ([1.1], [2.1], [3.1], [4.1])
) p1
pivot (
max(ThatOtherField) for ThatOtherFieldSequence in ([1.2], [2.2], [3.2], [4.2])
) p2
group by
MainField
I am unsure if you are using MS SQL Server, but if you are... You may want to take a look at the CROSS APPLY functionality of the engine. Basically it will allow you to apply the results of a table-valued UDF to a result set. This would require you to put your pivot query into a table-valued result set.
http://weblogs.sqlteam.com/jeffs/archive/2007/10/18/sql-server-cross-apply.aspx
wrap your sql statement with something like:
select a.segment, sum(field2), sum(field3)
from (original select with case arguments) a
group by a.segment
It should collapse your results into one row, grouped on field1.
It is possible to pivot on multiple columns, but you need to be careful about reusing the pivot column across multiple pivots. Here is a good blog post on the subject:
http://pratchev.blogspot.com/2009/01/pivoting-on-multiple-columns.html

Resources