How to use Unpivot properly - sql-server

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

Related

How to ADD latest values if not 0 [closed]

Closed. This question needs details or clarity. It is not currently accepting answers.
Want to improve this question? Add details and clarify the problem by editing this post.
Closed 3 years ago.
Improve this question
Is there a better way of adding the latest 3 values in a row where the value is not 0 (i.e, skip column when value = 0)?
Example :
[Jan] [Feb] [Mar] [Apr] [May]
[2] [3] [10] [0] [7]
[2] [3] [10] [0] [0]
[2] [3] [0] [0] [7]
[2] [3] [10] [0] [7]
Looking for the row values; 20, 15, 12, 20.
At the moment I'm only thinking of a massive case statement...
You can do this by unpivoting the columns ordering them and then summing like this:
SELECT *
FROM MyRows AS M
CROSS APPLY
(
SELECT SUM(ColumnValue) AS SumValue
FROM
(
SELECT TOP 3 V.ColumnValue
FROM (
VALUES
(1,Column5)
,(2,Column4)
,(3,Column3)
,(4,Column2)
,(5,Column1)
) AS V(RankMe,ColumnValue)
WHERE V.ColumnValue <> 0
ORDER BY V.RankMe
) AS SumValues
) AS TopThree
Example at dbfiddle.uk
However as others point out we're unsure why you'd want to do this, I suspect your data model has issues and you'd be better with these values in columns to start with.
Assuming there is an ID column, and assuming the columns go up to Dec.
You need to unpivot your data and normalise it. Then you can easily aggregate it. This is how you would do it "on the fly":
WITH CTE AS(
SELECT YT.ID,
V.MonthNo,
V.[MonthName],
V.[Value],
ROW_NUMBER() OVER (PARTITION BY YT.ID, IIF(V.[Value] = 0,0,1) ORDER BY V.MonthNo DESC) AS RN
FROM dbo.YourTable YT
CROSS APPLY (VALUES('Jan',1,Jan),
('Feb',2,Feb),
('Mar',3,Mar),
('Apr',4,Apr),
('May',5,May),
('Jun',6,Jun),
('Jul',7,Jul),
('Aug',8,Aug),
('Sep',9,Sep),
('Oct',10,Oct),
('Nov',11,Nov),
('Dec',12,[Dec]))V([MonthName], MonthNo, [Value]))
SELECT YT.ID,
SUM(V.[Value]) AS [Aggregate]
FROM CTE
WHERE RN <= 3
GROUP BY YT.ID;
Ideally, though, you should be fixing your data model.
You could convert columns to rows using CROSS APPLY and aggregate:
WITH cte AS (
SELECT id, ROW_NUMBER() OVER (PARTITION BY id ORDER BY mnum DESC) AS rn, mval
FROM t
CROSS APPLY (VALUES
(1 , Jan),
(2 , Feb),
(3 , Mar),
(4 , Apr),
(5 , May),
(6 , Jun),
(7 , Jul),
(8 , Aug),
(9 , Sep),
(10, Oct),
(11, Nov),
(12, Dec)
) x(mnum, mval)
WHERE mval <> 0
)
SELECT id, SUM(mval)
FROM cte
WHERE rn <= 3
GROUP BY id
Try this:
DECLARE #DataSource TABLE
(
[RowID] INT IDENTITY(1,1)
,[Jan] INT
,[Feb] INT
,[Mar] INT
,[Apr] INT
,[May] INT
);
INSERT INTO #DataSource ([Jan], [Feb], [Mar], [Apr], [May])
VALUES (2, 3, 10, 0, 7)
,(2, 3, 10, 0, 0)
,(2, 3, 0, 0, 7)
,(2, 3, 10, 0, 7);
WITH DataSource AS
(
SELECT [RowID]
,ROW_NUMBER() OVER (PARTITION BY [RowID] ORDER BY CASE [column]
WHEN 'Jan' THEN 1
WHEN 'Feb' THEN 2
WHEN 'Mar' THEN 3
WHEN 'Apr' THEN 4
WHEN 'May' THEN 5
END DESC
) AS [MonthID]
,[value]
FROM #DataSource
UNPIVOT
(
[value] FOR [column] IN ([Jan], [Feb], [Mar], [Apr], [May])
) UNPVT
WHERE [value] <> 0
)
SELECT [RowID]
,SUM([value])
FROM DataSource
WHERE [MonthID] <= 3
GROUP BY [RowID];
The idea is simple:
unpivot the data
order the months from latest to newest excluding months without values
sum the values but for the last 3 months only

SQL Self Join / Pivot table query

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:

convert rows to columns using date column

I have a table containing four rows: id(primary key, auto increment), value, type and time.
id value type time
1 1.2 1 2017-10-26 16:16:49.350
2 12.4 2 2017-10-26 16:16:49.350
3 0.6 3 2017-10-26 16:16:49.350
4 1.1 4 2017-10-26 16:16:49.350
5 1.8 1 2017-10-25 14:12:24.650
6 3.2 2 2017-10-25 14:12:24.650
7 0.2 3 2017-10-25 14:12:24.650
8 1.2 4 2017-10-25 14:12:24.650
Is it possible to convert these rows to columns based on type and time(either by query or stored procedure)? something like this:
(type)1 2 3 4 time
1.2 12.4 0.6 1.1 2017-10-26 16:16:49.350
1.8 3.2 0.2 1.2 2017-10-25 14:12:24.650
PS: Each four types share the same time.
Try this:
DECLARE #DataSource TABLE
(
[id] SMALLINT
,[value] DECIMAL(9,1)
,[type] TINYINT
,[time] DATETIME2
);
INSERT INTO #DataSource ([id], [value], [type], [time])
VALUES (1, 1.2, 1, '2017-10-26 16:16:49.350')
,(2, 12.4, 2, '2017-10-26 16:16:49.350')
,(3, 0.6, 3, '2017-10-26 16:16:49.350')
,(4, 1.1, 4, '2017-10-26 16:16:49.350')
,(5, 1.8, 1, '2017-10-25 14:12:24.650')
,(6, 3.2, 2, '2017-10-25 14:12:24.650')
,(7, 0.2, 3, '2017-10-25 14:12:24.650')
,(8, 1.2, 4, '2017-10-25 14:12:24.650');
SELECT [1], [2], [3], [4], [time]
FROM
(
SELECT [value], [type], [time]
FROM #DataSource
) DS
PIVOT
(
MAX([value]) FOR [type] IN ([1], [2], [3], [4])
) PVT
ORDER BY [time] DESC;
Here is another option using conditional aggregation or cross tab.
select Type1 = max(case when type = 1 then value)
Type2 = max(case when type = 2 then value)
Type3 = max(case when type = 3 then value)
Type4 = max(case when type = 4 then value)
, time
from YourTable
group by time
You can use PIVOT:
SELECT
[1] type1
, [2] type2
, [3] type3
, [4] type4
, time
FROM
(
SELECT
value
, type
, time
FROM table
) T
PIVOT
(
SUM (value)
FOR type IN
(
[1], [2], [3], [4]
)
) P

A Different kind of transformation of data in sql

I'm looking for a query were i can derive a new column based on columns values as shown below in example
P X Y Z A B C
1 1.1 2.1 1.3 1 Null 3
2 Null 1.4 3.1 2 4.7 1
3 2.2 Null 4.6 4 3.5 1
4 Null 1.8 3.4 2 1.7 4
Which i want to show as shown below;
P Group X y Z A B C
1 Xgrp 1.1 - - - - -
1 Ygrp - 2.1 - - - -
1 Zgrp 1.3 - - -
1 Agrp 1
1 Bgrp Null
1 Cgrp 3
Please help me :)
Regards
Andy
I am not sure how you are defining these dashes, so they are ignored in the code below. There is some additional logic in the ORDER BY clause in order to show the results like in your example. Basically, we are performing UNPIVOT to define the group values and then performing again PIVOT:
DECLARE #DataSource TABLE
(
[P] TINYINT
,[X] DECIMAL(9,1)
,[Y] DECIMAL(9,1)
,[Z] DECIMAL(9,1)
,[A] DECIMAL(9,1)
,[B] DECIMAL(9,1)
,[C] DECIMAL(9,1)
);
INSERT INTO #DataSource ([P], [X], [Y], [Z], [A], [B], [C])
VALUES (1, 1.1, 2.1, 1.3, 1, NULL, 3)
,(2, NULL, 1.4, 3.1, 2, 4.7, 1)
,(3, 2.2, NULL, 4.6, 4, 3.5, 1 )
,(4, NULL, 1.8, 3.4, 2, 1.7, 4);
SELECT *
FROM
(
SELECT [P]
,[value]
,[column]
,[column] + 'grp'
FROM #DataSource
UNPIVOT
(
[value] FOR [column] IN ([X], [Y], [Z], [A], [B], [C])
) UNPVT
) DS ([P], [value], [column], [group])
PIVOT
(
MAX([value]) FOR [column] IN ([X], [Y], [Z], [A], [B], [C])
) PVT
ORDER BY [P]
,CASE [group]
WHEN 'Xgrp' THEN 1
WHEN 'Ygrp' THEN 2
WHEN 'Zgrp' THEN 3
WHEN 'Agrp' THEN 4
WHEN 'Bgrp' THEN 5
WHEN 'Cgrp' THEN 6
END
What gotqn posted is missing some rows - specifically ones where all columns should be NULL (such as the "Bgrp" record where P=1).
You can get what you're looking for without PIVOT or UNPIVOT, just a simple CROSS JOIN, GROUP BY and CASE statement like so:
-- sample data
DECLARE #DataSource TABLE
(
[P] TINYINT
,[X] DECIMAL(9,1)
,[Y] DECIMAL(9,1)
,[Z] DECIMAL(9,1)
,[A] DECIMAL(9,1)
,[B] DECIMAL(9,1)
,[C] DECIMAL(9,1)
);
INSERT INTO #DataSource ([P], [X], [Y], [Z], [A], [B], [C])
VALUES (1, 1.1, 2.1, 1.3, 1, NULL, 3)
,(2, NULL, 1.4, 3.1, 2, 4.7, 1)
,(3, 2.2, NULL, 4.6, 4, 3.5, 1 )
,(4, NULL, 1.8, 3.4, 2, 1.7, 4);
-- solution
SELECT
p, [group],
X = CASE [group] WHEN 'Xgrp' THEN MAX(X) END,
Y = CASE [group] WHEN 'Ygrp' THEN MAX(Y) END,
Z = CASE [group] WHEN 'Zgrp' THEN MAX(Z) END,
A = CASE [group] WHEN 'Agrp' THEN MAX(A) END,
B = CASE [group] WHEN 'Bgrp' THEN MAX(B) END,
C = CASE [group] WHEN 'Cgrp' THEN MAX(C) END
FROM (VALUES ('Xgrp'),('Ygrp'),('Zgrp'),('Agrp'),('Bgrp'),('Cgrp')) groups([group])
CROSS JOIN #DataSource d
GROUP BY p, [group]
-- Uncomment this ORDER BY for testing:
--ORDER BY [P]
-- ,CASE [group]
-- WHEN 'Xgrp' THEN 1
-- WHEN 'Ygrp' THEN 2
-- WHEN 'Zgrp' THEN 3
-- WHEN 'Agrp' THEN 4
-- WHEN 'Bgrp' THEN 5
-- WHEN 'Cgrp' THEN 6
-- END;
Results

Adding columns in a query executed from a stored procedure

I have a query in a stored procedure like below
select x,y from table
and the results will look like below
x y
1 a
1 b
2 a
2 b
3 a
3 b
i need to add a blank column or zeros when the value of x changes like below
x y
1 a
1 b
0 0
2 a
2 b
0 0
3 a
3 b
Can this be done by sql or since i'm using the data for birt reports can this be done with birt?
You need UNION ALL to add the extra rows, you also need to ORDER them, the DENSE_RANK is to get rid of the extra row.
here is how it could be done:
DECLARE #t table(x int, y char(1))
INSERT #t values
(1,'a'),(1,'b'),(2,'a'),
(2,'b'),(3,'a'),(3,'b')
;WITH CTE AS
(
SELECT
2 rn, x,y, x sort from #t
UNION ALL
SELECT
distinct dense_rank() over (order by x desc) rn, 0, '0', x+.1 sort
FROM #t
)
SELECT x,y
FROM CTE
WHERE rn > 1
ORDER BY sort, x
Result:
x y
1 a
1 b
0 0
2 a
2 b
0 0
3 a
3 b
This is working example:
DECLARE #DataSource TABLE
(
[x] TINYINT
,[y] CHAR(1)
);
INSERT INTO #DataSource ([x], [y])
VALUES (1, 'a')
,(1, 'b')
,(2, 'a')
,(2, 'b')
,(3, 'a')
,(3, 'b');
WITH DataSource AS
(
SELECT *
FROM #DataSource
UNION ALL
-- the NULL will be always displayed on the first position
SELECT DISTINCT [x]
,NULL
FROM #DataSource
)
SELECT IIF([Rank] = 1, 0, [x])
,IIF([Rank] = 1, 0, [x])
FROM
(
SELECT ROW_NUMBER() OVER(PARTITION BY [x] ORDER BY [y]) AS [Rank]
,[x]
,[y]
FROM DataSource
) DS
ORDER BY [x]
,[Rank]
Few important notes:
the NULL values for each x will be with rank 1 always
the final result set is sorted by x and rank both
declare #t table (X varchar(1),Y varchar(1))
insert into #t(X,y) values (1,'A'),
(1,'B'),
(2,'A'),
(2,'B'),
(3,'A'),
(3,'B')
;with CTE As(
select X,Y,ROW_NUMBER()OVER(PARTITION BY X,Y ORDER BY X)RN
from #t
CROSS APPLY
(
values
('',NULL),
('',NULL)
) C(R, N)),
CTE2 AS(
Select CASE WHEN RN > 1 THEN 0 ELSE X END X ,
CASE WHEN RN > 1 THEN CAST(0 AS VARCHAR) ELSE Y END ID
,ROW_NUMBER()OVER(PARTITION BY X ORDER BY (SELECT NULL)) R
FROM CTE
)
select X,ID from cte2 where R <> 2

Resources