How to use case in pivot - sql

I want to create a pivot of working hour by department and date.
I had a sql like
select department,sum(workinghours),date group by department,date.
then I write a pivot sql
select * from(
select department,sum(workinghours) as hours,date group by department,date
)as RC
PIVOT
(
sum(hours) for date
)as P
but this return lots of null value. so I update the pivot to
select * from(
select department,sum(workinghours) as hours,date group by department,date
)as RC
PIVOT
(
(case when sum(hours) is null then 0 else sum(hours) end) for date
)as P
then it has error.
I don't understand why, anyone can help?Thx.

You need to replace your null with some specific values in sum. As I have replaced 0 instead of null:
create table #t
(
Id int identity (1,1),
Department varchar (100),
WorkingHours int ,
Date datetime
)
Insert into #t (Department,WorkingHours,Date)
Select '1','10',GETDATE ()-1
Insert into #t (Department,WorkingHours,Date)
Select '1','20',GETDATE ()-1
Insert into #t (Department,WorkingHours,Date)
Select '1',null,GETDATE ()
select * from #t
select * from(
select department,sum(ISNULL(workinghours,0)) as hours,
cast (Year(date) as varchar) date
from #t
group by department,date
)as RC
PIVOT
(
sum(hours ) for date IN ([2018])
)as P

Try below with case when inside sum :
select * from(
select department,sum(workinghours) as hours,date group by department,date
)as RC
PIVOT
(
sum(case when hours is null then 0 else hours end) for date
)as P

Related

How to complete and fill in gaps between dates in SQL?

I have data in Redshift that I'm aggregating to the Year-Quarter level i.e. number of items by Year-Quarter
I need to show a continuous trend and hence I need to fill-in the gaps in Year-Quarter. The picture below should give a clearer idea of my current data and desired output.
How can I achieve this in Redshift SQL?
A query like this should do the trick:
create table test (yq int, items int);
INSERT INTO test Values (20201,10),(20204, 15),(20213, 25),(20222, 30);
with recursive quarters(q) as (
select min(yq) as q
from test
union all
select decode(right(q::text, 1), 4, q + 7, q + 1) as q
from quarters
where q < (select max(yq) from test)
)
select q as yq, decode(items is null, true,
lag(items ignore nulls) over (order by q), items) as items
from test t
right join quarters q
on t.yq = q.q
order by q;
It uses a recursive CTE to generate the quarters range needed, right joins this with the source data, and then uses a LAG() window function to populate the items if the value is NULL.
This is known as forward filling values:
CREATE TABLE #Temp
(
[YQ] nvarchar(5),
[items] int
)
INSERT INTO #Temp Values ('20201',10),('20204', 15),('20213', 25),('20222', 30)
---------------------------------------------------------------------------------
DECLARE #start int, #end int, #starty int, #endy int
SELECT #start=1, #end=4
SELECT #starty=MIN(Substring(YQ,0,5)), #endy=MIN(Substring(YQ,0,5)) from #Temp
;With cte1(y) as
(
Select #starty as y
union all
Select y + 1
from cte1
where y <= #endy + 1
)
, cte2(n) as
(
Select #start as n
union all
Select n + 1
from cte2
where n < #end
)
SELECT t1.YQ AS 'Year-Quarter',
CASE WHEN t2.items is null then (SELECT TOP 1 MAX(items) from #Temp WHERE items is not null and YQ < t1.YQ) ELSE t2.items END AS '# Items'
FROM
(
SELECT CAST(cte1.y AS nvarchar(4)) + CAST(cte2.n AS nvarchar(1)) AS YQ
FROM cte1, cte2
) t1
LEFT JOIN #Temp t2 ON t2.YQ = t1.YQ
WHERE t1.YQ <= (SELECT MAX(YQ) FROM #Temp)
ORDER BY t1.YQ, t2.items

Convert three rows values into columns, NOT as comma separated value

I have table structure like
select catalog_item_id,metal_type,metal_color
from catalog_item_castings
where catalog_Item_Id =465173
It returns output as:
And I want output as:
And I want to insert this data into new temp table in SQL Server.
Thanks in advance.
Conditional aggregation is an option:
SELECT
catalog_item_id,
MAX(CASE WHEN rn % 3 = 1 THEN CONCAT(metal_type, '/', metal_color) END) AS Casting_1,
MAX(CASE WHEN rn % 3 = 2 THEN CONCAT(metal_type, '/', metal_color) END) AS Casting_2,
MAX(CASE WHEN rn % 3 = 0 THEN CONCAT(metal_type, '/', metal_color) END) AS Casting_3
FROM (
SELECT
catalog_item_id, metal_type, metal_color, ROW_NUMBER() OVER (ORDER BY (SELECT NULL)) AS rn
FROM (VALUES
(465173, 'na', 'METALCOLOR'),
(465173, 'na', 'METAL-001'),
(465173, 'na', 'na')
) catalog_item_castings (catalog_item_id, metal_type, metal_color)
WHERE catalog_Item_Id = 465173
) t
GROUP BY catalog_item_id
-- or if you have more than three rows per [catalog_item_id]
-- GROUP BY catalog_item_id, (rn - 1) / 3
Result:
catalog_item_id Casting_1 Casting_2 Casting_3
-------------------------------------------------
465173 na/METALCOLOR na/METAL-001 na/na
You can use Conditional Aggregation within a Dynamic Pivot Statement in order to include all distinct combinations of the columns [metal_type] and [metal_color], even different values for combinations are inserted in the future :
DECLARE #cols AS NVARCHAR(MAX), #query AS NVARCHAR(MAX)
SELECT #cols = (SELECT STRING_AGG(CONCAT('MAX(CASE WHEN [dr]=',dr,
' THEN CONCAT([metal_type],''/'',[metal_color]) END) AS [Casting_',dr,']'),',')
WITHIN GROUP ( ORDER BY dr )
FROM
(
SELECT DISTINCT
DENSE_RANK() OVER
(PARTITION BY [catalog_item_id]
ORDER BY CONCAT([metal_type],[metal_color])) AS dr
FROM [catalog_item_castings] ) c);
SET #query =
'SELECT [catalog_item_id],'+ #cols +
' FROM
(
SELECT *, DENSE_RANK() OVER
( PARTITION BY [catalog_item_id]
ORDER BY CONCAT([metal_type], [metal_color]) ) AS dr
FROM [catalog_item_castings]
) c
GROUP BY [catalog_item_id]';
EXEC sp_executesql #query;
Demo

Get a specific string

It's my data and every ThroughRouteSid record has the same pattern.
six number and five comma. then I just want to get three and five
number into two record to template Table and get the same Count()
value to these two record.
For example: First record in the picture.
ThroughRouteSid(3730,2428,2428,3935,3935,3938,) Count(32).
I want a result like this:
2428 32 3935 32
I get What number I want.become two record and both have same Count value into template table
you can use XML to get your result, please refer below sample code -
create table #t1( ThroughRouteSid varchar(500) , Cnt int)
insert into #t1
select '3730,2428,2428,3935,3935,3938,' , len('3730,2428,2428,3935,3935,3938,')
union all select '1111,2222,3333,4444,5555,6666,' , len('1111,2222,3333,4444,5555,6666,')
select cast( '<xml><td>' + REPLACE( SUBSTRING(ThroughRouteSid ,1 , len(ThroughRouteSid)-1),',','</td><td>') + '</td></xml>' as xml) XmlData , Cnt
into #t2 from #t1
select XmlData.value('(xml/td)[3]' ,'int' ), Cnt ,XmlData.value('(xml/td)[5]' ,'int' ), Cnt
from #t2
First create the function referring How to Split a string by delimited char in SQL Server. Then try Querying the following
select (SELECT CONVERT(varchar,splitdata) + ' '+ Convert(varchar, [Count])+' ' FROM (select splitdata, ROW_NUMBER() over (ORDER BY (SELECT 100)) row_no
from [dbo].[fnSplitString](ThroughRouteSid,',')
where splitdata != '') as temp where row_no in (2,5)
for xml path('')) as col1 from [yourtable]
If you are using SQL Server 2016 you can do something like this:
create table #temp (ThroughRouteSid varchar(1024),[Count] int)
insert into #temp values
('3730,2428,2428,3935,3935,3938,',32),
('730,428,428,335,935,938,',28)
select
spt.value,
t.[Count]
from #temp t
cross apply (
select value from STRING_SPLIT(t.ThroughRouteSid,',') where LEN(value) > 0
)spt

SQL Multi Pivot

I am trying to do a pivot table to show order data by dayofyear. My first problem is my pivot doesn't appear to be showing the correct data. My second problem is I don't really want to type out a day for all 365 day columns. Is there an easier way?
Columns would be 1 - 365
Rows would be Year, #orders, #Tags
SELECT Yr, [01],[02],[03],[04],[05]....
FROM (
select TOP 100 PERCENT
YEAR(tagdata.shipdate) AS Yr,
DATEPART(dy,tagdata.shipdate) AS Day,
tagdata.#Orders,
tagdata.#Tags,
from tagData
GROUP BY tagData.ShipDate, tagdata.#Orders, tagdata.#Tags
) AS sourcetable
PIVOT
(
Max(#Orders) FOR Day IN ([01],[02],[03],[04],[05],.......),
Max(#Tags) FOR Day IN ([01],[02],[03],[04],[05],.......)
)
as pivottable
ORDER BY Yr
You have to do two pivots for each set of fields you are looking at and then join them on the year. Something like this:
SELECT isnull(pivottable1.Yr, pivottable2.Yr) Yr,
[1ORD],[2ORD],[3ORD],[4ORD],[5ORD],
[1TAG],[2TAG],[3TAG],[4TAG],[5TAG]
FROM (
select TOP 100 PERCENT
YEAR(shipdate) AS Yr,
Convert(varchar(3),DATEPART(dy,shipdate)) + 'ORD' AS Day,
#Orders
from #tagData
) AS sourcetable1
PIVOT
(
Max(#Orders) FOR Day IN ([1ORD],[2ORD],[3ORD],[4ORD],[5ORD])
) as pivottable1
full join (
select TOP 100 PERCENT
YEAR(shipdate) AS Yr,
Convert(varchar(3),DATEPART(dy,shipdate)) + 'TAG' AS Day,
#Tags
from #tagData
) AS sourcetable2
PIVOT
(
Max(#Tags) FOR Day IN ([1TAG],[2TAG],[3TAG],[4TAG],[5TAG])
) as pivottable2
on pivottable1.Yr = pivottable2.Yr
ORDER BY isnull(pivottable1.Yr, pivottable2.Yr)
As for all that typing... a simple script can cover you:
declare #number as int
declare #numbers as varchar(max)
set #number=1
while #number <= 365
begin
set #numbers = isnull(#numbers+',','') + '[' + convert(varchar(3),#number) + ']'
set #number=#number+1
end
print #numbers

How to get column wise data in SQL Server?

How to get column wise data in SQL Server?
Format:
Name Date
---- -----
xxx 10/15/2015
xxx 12/15/2015
xxx 15/15/2015
yyy 20/15/2015
yyy 25/15/2015
Desired output:
Name Date Date Date
--------------------------------------------
xxx 10/15/2015 12/15/2015 15/15/2015
yyy 20/15/2015 25/15/2015
You can use this code for example which will pivot the data:
On MySQL:
SELECT data.name,
if(data.row_number=1,date,null) as date1,
if(data.row_number=2,date,null) as date2,
if(data.row_number=3,date,null) as date3,
if(data.row_number=4,date,null) as date4,
if(data.row_number=5,date,null) as date5
FROM (
SELECT #row_number:=#row_number+1 AS row_number, name, date
FROM yourTable, (SELECT #row_number:=0) AS t
ORDER BY date
) as data
GROUP BY data.name;
On SQL Server:
-- Generate demo data
CREATE TABLE #yourTable(name nvarchar(20), date date)
INSERT INTO #yourTable(name,date)
VALUES(N'xxx',GETDATE()), (N'xxx', DATEADD(day,-1,GETDATE())), (N'yyy',GETDATE()), (N'yyy', DATEADD(day,1,GETDATE()))
-- this is your part
SELECT pvt.*
FROM (
SELECT ROW_NUMBER() OVER(PARTITION BY name ORDER BY date) as rn, name, date
FROM #yourTable
) as data
PIVOT(
MIN(date)
FOR rn IN([1],[2],[3],[4],[5],[6])
) as pvt
-- cleanup
DROP TABLE #yourTable
This will be a dynamic pivot which will adapt if your date list will grow:
-- Generate demo data
CREATE TABLE #yourTable(name nvarchar(20), date date)
INSERT INTO #yourTable(name,date)
VALUES(N'xxx',GETDATE()), (N'xxx',DATEADD(day,1,GETDATE())), (N'xxx', DATEADD(day,-1,GETDATE())), (N'yyy',GETDATE()), (N'yyy', DATEADD(day,1,GETDATE()))
DECLARE #sql nvarchar(max), #columnlist nvarchar(max)
SELECT #columnlist =
COALESCE(#columnlist + N',['+CONVERT(nvarchar(max),ROW_NUMBER() OVER(ORDER BY date))+']',
N'['+CONVERT(nvarchar(max),ROW_NUMBER() OVER(ORDER BY date))+']'
)
FROM #yourTable
WHERE name = (
SELECT TOP (1) name
FROM #yourTable
GROUP BY name
ORDER BY COUNT(*) DESC
)
SELECT #columnlist
-- this is your part
SET #sql = N'
SELECT pvt.*
FROM (
SELECT ROW_NUMBER() OVER(PARTITION BY name ORDER BY date) as rn, name, date
FROM #yourTable
) as data
PIVOT(
MIN(date)
FOR rn IN('+#columnlist+')
) as pvt'
EXEC(#sql)
-- cleanup
DROP TABLE #yourTable
may be this will suits your requirement
declare #t table (name varchar(5),dated varchar(10))
insert into #t (name,dated)values
('xxx','10/15/2015'),('xxx','12/15/2015')
,('yyy','15/15/2015'),('yyy','20/15/2015'),('yyy','25/15/2015')
Select name,[1]As [Date],[2]As [Date],[3]As [Date] from (
select name,dated,ROW_NUMBER()OVER(PARTITION BY name ORDER BY dated)RN from #t
)T
PIVOT(MIN(dated) FOR RN IN ([1],[2],[3]))P