Reputation: 3
i have imported lots of building-data from another system into MS SQL 2017 but facing a problem turning rows into columns. Most fields are var-chars in the system imported from and a field can have leading/trailing spaces. Believe me when i say, i have searched many places/pages for an answer.
data:
key1 Unit Measurement
04A Unit 1 156,8000
04A Unit 2 5.845,3000
678R Unit 1 8,6000
678R Unit 2 409,6000
678R Unit 3 17
how can data be turned into:
key1 Unit 1 Unit 2 Unit 3
04A 156,8000 5.845,3000 NULL
678R 8,6000 409,6000 17
measurement column - should this be float or decimal - we are scandinavians here :-) ?
i have tried pivot with max as aggregate function but too many rows appear with nulls in Unit 1 , 2 , 3 - it even rejects rows having value
Upvotes: 0
Views: 81
Reputation: 38094
You can use PIVOT operator:
SELECT *
FROM
(
SELECT * FROM YourTable
)AS FooData
PIVOT(
AVG(FooData.Measurement)
FOR Unit IN ([Unit 1], [Unit 2], [Unit 3])
) AS FooPivot
An example:
DECLARE @tbl TABLE
(
key1 varchar(50),
Unit VARCHAR(50),
Measurement DECIMAL(18,8)
)
INSERT INTO @tbl
(
key1,
Unit,
Measurement
)
VALUES
('04A', 'Unit 1', 156.8000)
, ('04A', 'Unit 2', 5.8453000)
, ('678R', 'Unit 1', 8.6000)
, ('678R', 'Unit 2', 409.6000)
, ('678R', 'Unit 3', 17)
SELECT *
FROM
(
SELECT * FROM @tbl
)AS FooData
PIVOT(
AVG(FooData.Measurement)
FOR Unit IN ([Unit 1], [Unit 2], [Unit 3])
) AS FooPivot
OUTPUT:
key1 Unit 1 Unit 2 Unit 3
04A 156.80000000 5.84530000 NULL
678R 8.60000000 409.60000000 17.00000000
UPDATE:
I don't know what actual data type of Measurement
field which has value 5.845,3000
, however I strongly believe that this data is type of numeric
. If yes, then it is better to store numerical data as a numerical type as it is responsibility of the presentation layer cause there are different requirements to show numbers in various countries. To add commas you can use pipes at the client side.
In addition, if Measurement
is type of number and you store it as varchar(50)
, then you would get problems while ordering. So it is better to store as numeric
.
Otherwise, you can use datatype varchar(255)
:
DECLARE @tbl TABLE
(
key1 varchar(50),
Unit VARCHAR(50),
Measurement VARCHAR(255)
)
INSERT INTO @tbl
(
key1,
Unit,
Measurement
)
VALUES
('04A', 'Unit 1', '156.8000')
, ('04A', 'Unit 2', '5,845.3000')
, ('678R', 'Unit 1', '8.6000')
, ('678R', 'Unit 2', '409.6000')
, ('678R', 'Unit 3', '17')
SELECT *
FROM
(
SELECT * FROM @tbl
)AS FooData
PIVOT(
MAX(FooData.Measurement)
FOR Unit IN ([Unit 1], [Unit 2], [Unit 3])
) AS FooPivot
Upvotes: 1
Reputation: 14189
Make sure to limit the pivot's column set before actually applying it:
;WITH PrePivot AS -- Limit the columns for the PIVOT's GROUP BY
(
SELECT
Key1,
Unit,
Measurement
FROM
YourTable
)
SELECT
P.Key1,
P.[Unit 1],
P.[Unit 2],
P.[Unit 3]
FROM
PrePivot AS T
PIVOT (
MAX(T.Measurement) FOR T.Unit IN ([Unit 1], [Unit 2], [Unit 3])
) AS P
Please check this post to see how PIVOT
actually does an implicit GROUP BY
with all non-pivoting columns.
Upvotes: 0
Reputation: 5643
You can try this using PIVOT
. Here you need to use MAX()
function rather than AVG()
or SUM()
as data saved is in varchar
.
create table #temp(key1 varchar(10), Unit Varchar(20), Measurement varchar(20))
insert into #temp values
('04A', 'Unit 1', '156,8000'),
('04A', 'Unit 2', '5.845,3000'),
('678R', 'Unit 1', '8,6000'),
('678R', 'Unit 2', '409,6000'),
('678R', 'Unit 3', '17')
SELECT *
FROM
(
SELECT * FROM #temp
)AS FooData
PIVOT(
MAX(FooData.Measurement)
FOR Unit IN ([Unit 1], [Unit 2], [Unit 3])
) AS FooPivot
The output is as shown below:
key1 Unit 1 Unit 2 Unit 3
04A 156,8000 5.845,3000 NULL
678R 8,6000 409,6000 17
Upvotes: 1