SQl Server 2005: Rows to columns -- how to do this challenge? - sql

Please let me know, How to convert the following data ,
[id] cost1 cost2 year
1 5 10 2010
1 4 15 2011
2 10 10 2010
into this format [rows of 'Year' to columns heading]
id [cost1-2010] [cost2-2010] [cost1-2011] [cost2-2011]
1 5 10 4 15
2 10 10 0 0

Use PIVOT
example: http://www.simple-talk.com/community/blogs/andras/archive/2007/09/14/37265.aspx

try something like this:
DECLARE #YourTable table (id int, cost1 int, cost2 int, year int)
INSERT #YourTable VALUES (1,5,10,2010)
INSERT #YourTable VALUES (1,4,15,2011)
INSERT #YourTable VALUES (2,10,10,2010)
SELECT
id
,SUM(CASE WHEN year=2010 THEN cost1 else 0 END) AS "Cost1-2010"
,SUM(CASE WHEN year=2010 THEN cost2 else 0 END) AS "Cost2-2010"
,SUM(CASE WHEN year=2011 THEN cost1 else 0 END) AS "Cost1-2011"
,SUM(CASE WHEN year=2011 THEN cost2 else 0 END) AS "Cost2-2010"
FROM #YourTable
GROUP BY id
OUTPUT
id Cost1-2010 Cost2-2010 Cost1-2011 Cost2-2010
----------- ----------- ----------- ----------- -----------
1 5 10 4 15
2 10 10 0 0
(2 row(s) affected)

If you want to do this dynamically based on data, it is going to be more difficult than just using PIVOT. For PIVOT or the conditional sum technique
[2010Values] = ( SUM(Case when year = 2010 then FieldValue Else 0 End)
you must know the column name ahead of time.
If you wish to set column names dynamically based on the data received then you'll have to go the dynamic SQL route which can get ugly.

Check out the discussion on this post. That should have you dialed.

Related

MSSQL How to handle null values to count them within pivot

I'm here again because I've another problem with pivot table that concerns with null values.
IF OBJECT_ID(N'tempdb..#exams') IS NOT NULL
BEGIN
DROP TABLE #exams
END
GO
create table #exams (
id uniqueidentifier,
exam nvarchar(max),
technician nvarchar(max)
)
insert into #exams
values
(newid(),'Esame1','Tecnico1'),
(newid(),'Esame2','Tecnico1'),
(newid(),'Esame1','Tecnico2'),
(newid(),'Esame3','Tecnico1'),
(newid(),'Esame3','Tecnico2'),
(newid(),'Esame3','Tecnico3'),
(newid(),'Esame3','Tecnico1'),
(newid(),'Esame1',NULL)
I have to handle in some way null values in my reports.
With sum case clause I could do simply in this way:
select
exam,
sum(case when technician = 'Tecnico1' then 1 else 0 end) as Tecnico1,
sum(case when technician = 'Tecnico2' then 1 else 0 end) as Tecnico2,
sum(case when technician = 'Tecnico3' then 1 else 0 end) as Tecnico3,
sum(case when technician is null then 1 else 0 end) as Unknown
from #exams
group by exam
order by exam
exam
Tecnico1
Tecnico2
Tecnico3
Unkwnon
Esame1
1
1
0
1
Esame2
1
0
0
0
Esame3
2
1
1
0
but using pivot table (thanks again to Tole1010) null values stay outside from my pivot
select * from (
select id,exam,
technician
from #exams
) as t
pivot
( count(id)
for technician in (Tecnico1,Tecnico2,Tecnico3)
) as t
and I get only:
exam
Tecnico1
Tecnico2
Tecnico3
Esame1
1
1
0
Esame2
1
0
0
Esame3
2
1
1
Is there a way to add a column to count those null values using pivot syntax?
You would typically replace null with something else, that does not otherwise appear in the column:
select * from (
select id, exam,
coalesce(technician , 'Unknown') as technician
from #exams
) as t
pivot (
count(id)
for technician in (Tecnico1,Tecnico2,Tecnico3, Unknown)
) as t
Demo on DB Fiddlde:
exam
Tecnico1
Tecnico2
Tecnico3
Unknown
Esame1
1
1
0
1
Esame2
1
0
0
0
Esame3
2
1
1
0

Conditionally convert a decimal value to integer

create table #Temp
(
id int,
Volume decimal(18,3)
)
insert into #Temp(id,Volume)values(1,10)
insert into #Temp(id,Volume)values(2,10.2)
id Volume
-----------------
1 10.000
2 10.200
Declare #Type as int
set #Type=1
select Id,Convert(varchar(10),CASE WHEN #Type=1 THEN CAST(Volume AS INT)
ELSE Volume END) AS Quantity from #Temp
It is showing a result like this
id Volume
-----------------
1 10.000
2 10.000
But I want result like this when type is 1 then result should be in integer format:
id Volume
-----------------
1 10
2 10
on else condition (#Type rather than 1) I want result like this
id Volume
-----------------
1 10.000
2 10.200
I have tried this query
select Id,Convert(varchar(10),CASE WHEN #Type=1 THEN CAST(Volume AS INT)
ELSE Volume END) AS Quantity from #Temp
This will work for you.
select Id, (CASE WHEN #Type=1 THEN convert(varchar(10),convert(int,volume))
ELSE convert(varchar(10),Volume) END) AS Quantity from #Temp
When #type is 1, the result will be 10 for both values. Otherwise it will be exact value in the table.

sql pulling data in in certain rows but not others

table 1 looks like this:
filekey hourstype hours
123 1 40
123 2 5
123 3 6
123 4 7
123 5 8
needed output should look like this:
filekey hours1 hours2 otherhourstype otherhourstotal
123 40 5 '' ''
123 '' '' 3 6
123 '' '' 4 7
123 '' '' 5 8
hours1 and hours2 occupy the same row, all other hours occupy their own row
there is one other possible format that can work:
filekey hours1 hours2 difhrstype difhrstotal difhourstype difhrstotal
123 40 5 3 6 4 7
in this scenario start with lowest hours type, then total extended through columns instead of rows with one row per filekey. I'm not sure how to make this one happen either. especially because there can be up to 8 hourstypes each of which may or may not exist for a given filekey
Try this for scenario 1:
CREATE TABLE #TMP(filekey INT, hourstype INT, [hours] INT)
INSERT INTO #TMP VALUES
(123,1,40)
,(123,2,5)
,(123,3,6)
,(123,4,7)
,(123,5,8)
SELECT
T.filekey
,SUM(CASE WHEN hourstype = 1 THEN [hours] ELSE 0 END) AS hours1
,SUM(CASE WHEN hourstype = 2 THEN [hours] ELSE 0 END) AS hours2
,CASE WHEN hourstype > 2 THEN [hourstype] ELSE 0 END AS otherhourstype
,CASE WHEN hourstype > 2 THEN [hours] ELSE 0 END AS otherhourstotal
FROM
#TMP T
GROUP BY
T.filekey
,CASE WHEN hourstype > 2 THEN [hourstype] ELSE 0 END
,CASE WHEN hourstype > 2 THEN [hours] ELSE 0 END
UNION a query that will produce the first row over a query that will produce the other three rows.
Hard-code the NULLs (or blanks or whatever you want) for the unused columns in each query.
Try the following:
select
pivottable.filekey,
[1] as hours1,
[2] as hours2,
[3] as hours3,
[4] as hours4,
[5] as hours5,
[6] as hours6,
[7] as hours7,
[8] as hours8
from table1
PIVOT (
sum(hours)
FOR hourstype IN ([1],[2],[3],[4],[5],[6],[7],[8])
) as pivottable

Multiple counts for each ID

I'm trying to get my head around this, but unfortunately neither of my approaches works:
I need a table with 3 columns:
ItemID
Number cases where ItemID has CostcentreID x
Number cases where ItemID has CostcentreID y
SELECT ItemID, Count1, Count2
FROM Table
Output should be like:
--ItemID--Count1--Count2
1 12 5
2 3 2
What i get when using
SELECT ItemdID, SUM(case when costc...),...
FROM Table
is:
--ItemID--Count1--Count2
1 12 0
2 3 0
due to the GROUP BY statement.
Anyway to solve this without a Cursor?
Also, a JOIN of 5 tables is needed.
Thanks in advance!
I'm not sure what you need with the joins, but here is the first part.
DECLARE #table TABLE(ItemID INT, CostCentreID CHAR(1));
INSERT INTO #table
VALUES (1,'X'),
(1,'X'),
(1,'Y'),
(2,'X'),
(2,'Y'),
(2,'Y'),
(2,'Y');
SELECT ItemID,
SUM(
CASE
WHEN CostCentreID = 'X' THEN 1 ELSE 0
END
) AS CostCentreX,
SUM(
CASE
WHEN CostCentreID = 'Y' THEN 1 ELSE 0
END
) AS CostCentreY
FROM #table
GROUP BY ItemID
Results:
ItemID CostCentreX CostCentreY
----------- ----------- -----------
1 2 1
2 1 3

SQL query with join, sum, group by, etc

I'm trying to build a report that will look like this:
jan feb mar apr may jun jul ago sep oct nov dec
food 0 1 1 2 0 0 3 1 0 0 1 1
car 1 0 0 0 1 2 1 0 1 2 3 4
home 0 0 1 2 2 2 5 1 2 4 0 0
other 0 0 0 0 0 0 0 0 0 0 0 0
I have two tables: t_item and t_value. t_item has 2 columns: itemID and itemName. t_value has 3 columns: itemID, value, date.
With the following query I can generate a list with all the itens, even with the empty ones.
SELECT t_item.itemID, ISNULL(SUM(t_value.value), 0) AS value
FROM t_value RIGHT OUTER JOIN t_item ON t_value.itemID = t_item.itemID
GROUP BY t_item.itemID
But, if I try to include a MONTH column (as follows) the result will show only the items with values...
SELECT t_item.itemID, ISNULL(SUM(t_value.value), 0) AS value, MONTH(date) AS date
FROM t_value RIGHT OUTER JOIN t_item ON t_value.itemID = t_item.itemID
GROUP BY t_item.itemID, MONTH(date)
Is it possible to do it? How do I include into the results the itens with no values and group then by month?
TIA,
Bob
WITH calendar(mon) AS
(
SELECT 1
UNION ALL
SELECT mon + 1
FROM calendar
WHERE mon < 12
)
SELECT itemID, mon, SUM(value)
FROM calendar c, t_item i
LEFT OUTER JOIN
t_value v
ON v.itemID = i.itemID
AND MONTH(date) = mon
GROUP BY
i.itemID, mon
For the "holes" in your data you need a filler table. Join this table with a full outer join to the fact table on month.
month
------
month --values jan through dec
For the formating you have a couple options.
In your reporting tool use the cross tab or matrix function.
In SQL use the CASE function.
In SQL use the Pivot function.
Are you using a reporting tool with crosstab like ability?
If not, you can create a sum column for each month. so your resultset would actually look like that report sample.
SELECT t_item.itemID,
--ISNULL(SUM(t_value.value), 0) AS value,
sum(case when MONTH(date) = 1 then t_value.value else 0 end) AS m1_sum,
sum(case when MONTH(date) = 2 then t_value.value else 0 end) AS m2_sum,
sum(case when MONTH(date) = 3 then t_value.value else 0 end) AS m3_sum,
--etc
FROM t_value RIGHT OUTER JOIN t_item ON t_value.itemID = t_item.itemID
GROUP BY t_item.itemID
Here's an example:
create table #months (value int, name varchar(12))
create table #items (value int, name varchar(24))
create table #sales (month int, item int, sales int)
insert into #months values (1, 'jan')
insert into #months values (2, 'feb')
insert into #months values (3, 'mar')
insert into #items values (1, 'apple')
insert into #items values (2, 'pear')
insert into #items values (3, 'nut')
insert into #sales values (1,1,12)
insert into #sales values (2,2,3)
insert into #sales values (2,2,5)
insert into #sales values (3,3,7)
You can query it using a PIVOT table, like:
select *
from (
select
item = #items.name
, month = #months.name
, sales = isnull(sum(#sales.sales),0)
from #months
cross join #items
left join #sales on #months.value = #sales.month
and #items.value = #sales.item
group by #months.name, #items.name
) vw
pivot (sum(sales) for month in ([jan],[feb],[mar])) as PivotTable
Or as an alternative, a regular query:
select
item = #items.name
, jan = sum(case when #sales.month = 1 then sales else 0 end)
, feb = sum(case when #sales.month = 2 then sales else 0 end)
, mar = sum(case when #sales.month = 3 then sales else 0 end)
from #items
left join #sales on #items.value = #sales.item
group by #items.name
Both result in:
item jan feb mar
apple 12 0 0
nut 0 0 7
pear 0 8 0
In the first example, the "cross join" ensures all months and values are present. They're then "left joined", so even the rows with no values are displayed.
The IsNull() is just so that it displays 0 instead of NULL for a month in which that particular item was not sold.