Transpose data in SQL - sql

Can someone assist me on this?
As you can see from the first picture (Original data) I have date in format "Mar-12" and data for 2014,2015,2016 and 2017 year.
Now, I need to insert new column "year" where I need to put the year from Jan-14, Jan-15, Jan-16, Feb-16 etc.
Basically, I need some kind of data transpose, I think.
In the second picture "Final Order" I show in which order I need the data.
I don't know what is dbms.
So, this is how my data (original) looks like:
Customer|Section|Data|Jan-14|Feb-14|Jan-15|Feb-15
Total Fore SR 10 20 30 35
Total Fore TK 5 4 12 10
===================================================
And I need to put the data in this form:
Customer|Section|Data| Year |Jan|Feb|
Total Fore SR 2014 10 20
Total Fore TK 2014 5 4
Total Fore SR 2015 30 35
Total Fore TK 2015 12 10

Given your sample of
create table t (Customer varchar(5),Section varchar(4), Data varchar(2), [Jan-14] int , [Feb-14] int, [Jan-15] int, [Feb-15] int)
insert into #t values
('Total' , 'Fore' , 'SR' , 10 , 20, 30, 35),
('Total' , 'Fore' , 'TK' , 5 , 4, 12, 10)
you can solve this if your sql dialect is ms sql server by unpivoting and then grouping by like so
select customer,section,data,yyyy,
sum(case when mm='Jan' then dts else 0 end) as 'Jan',
sum(case when mm='Feb' then dts else 0 end) as 'Feb'
from
(
select customer,section,data,
dummy,
substring(dummy,1,3) as mm,
concat('20',substring(dummy,5,2)) as yyyy,
dts
from
(
select customer,section,data,
[Jan-14] , [Feb-14] , [Jan-15] , [Feb-15]
from t
) pvt
UNPIVOT
(dts FOR dummy IN
([Jan-14] , [Feb-14] , [Jan-15] , [Feb-15])
)AS unpvt
) x
group by customer,section,yyyy,data
result
customer section data yyyy Jan Feb
-------- ------- ---- ---- ----------- -----------
Total Fore SR 2014 10 20
Total Fore TK 2014 5 4
Total Fore SR 2015 30 35
Total Fore TK 2015 12 10
If your sql dialect does not have unpivot you can
select customer,section,data,yyyy,
sum(case when mm='Jan' then dts else 0 end) as 'Jan',
sum(case when mm='Feb' then dts else 0 end) as 'Feb'
from
(
select customer,section,data,2014 as yyyy,'Jan' as mm,[Jan-14] as dts from t
union all
select customer,section,data,2014 as yyyy,'Feb' as mm,[Feb-14] as dts from t
union all
select customer,section,data,2015 as yyyy,'Jan' as mm,[Jan-15] as dts from t
union all
select customer,section,data,2015 as yyyy,'Feb' as mm,[Feb-15] as dts from t
) x
group by customer,section,yyyy,data
Clearly either method is a pain if you have an unknown/variable number/lots of columns in which case you would need write a script to generate a sql statement for submission to dynamic sql.

Related

Selecting rows with specific text to count and sum

I would need to determine the percentage of specific values by year.
The dataset has values as follows:
Year Col Value
2012 -20 p, 12
2012 -20 points, d 20
2012 -20 points, by 24
2012 -20 p, new 32
2012 -30 p, 1256
2012 -30 points, d 32
2012 -30 points, by 42
2012 -30 p, new 164
There are other years but for the example I selected only 2012.
For each year, I would like to determine the percentage as:
Count of values having points word in the text
Divided by values starting with - 20
Same for case with 30.
Expected output for -20 in 2012:
(20+24)/(12+20+24+32)
I have tried as follows
Select year,
Col,
Count(0) as Value
, 100*count(0)/sum(count(case when Col like ‘-20%points%’ then 1 end) over (partition by year, substr(Col, 1,2))) as pct_20
/* Same for 40 poin
ts */
From table1
Where /* conditions */
Group by 1,2
But I got the error Ordered analytical functions can not be nested.
I think you want conditional aggregation:
select year, substr(col, 1, 2),
sum(case when col like '%points%' then value end) / sum(value)
from t
group by 1, 2;
Based on your comment:
select year, substr(col, 1, 2),
sum(case when col like '%points%' then 1.0 end) / count(*)
from t
group by 1, 2;
You can only nest an aggregate within an OLAP function, but not vice versa:
, 100*count(*)/NULLIF(sum(count(case when Col like ‘-20%points%’ then 1 end))
over (partition by year, substr(Col, 1,2)), 0) as pct_20

How to compare two date values in SQL Server?

I have a table with the date 2018 and date 2019 and here is my data.
select amount, date
from TABLE1
where date in (2018, 2019) and empid = 21120
It didn't work for me as the query says case when date =2019 then amount as my data above when date=2019 it will just show me the amount. This is what my result is:
Amount Data
9.67 2019
21 2019
6 2019
9.56 2018
42 2018
7 2018
What is want it: the difference between the Amount for the two dates 2018 and 2019.
The above query gives me only one row of the amount for those dates.
My expected result is to find the difference in the amount between 2018 and 2019. Any ideas on how I could get the difference. I am trying self joining but any help is appreciated.
Ex: 2019 amount 9.67 - 2018 amount 9.56.
You seem to be looking for conditional aggregation:
select
sum(case when date = 2019 then amount else 0 end)
- sum(case when date = 2018 then amount else 0 end) diff
from mytable
where empid = 21 and date in (2018, 2019)
The query gives you the difference between the total amount of 2019 and that of 2018 for the given employee.
This can be shortened a little:
select sum(case when date = 2019 then amount else -amount end) diff
from mytable
where empid = 21 and date in (2018, 2019)
You can do as
SELECT ABS(SUM(CASE WHEN Data = '2019' THEN Amount ELSE -Amount END)) Diff
FROM
(
VALUES
(9.67 , '2019'),
(21 , '2019'),
(6 , '2019'),
(9.56 , '2018'),
(42 , '2018'),
(7 , '2018')
) T(Amount, Data)
WHERE Data IN ('2018', '2019')
-- AND empid = 21120; Uncomment this when you run it against your table

How to make pivot table in SQL Server in my case?

I wants to display the record by same column. I don't know how describe the question also.
I have a table called SoldQtyTable
ItemNo Weeks Years QtySold AsOfWeekOnHand
----------------------------------------------------
1 1 2017 5 3
2 1 2017 2 5
3 1 2017 66 70
1 2 2017 4 33
I wants to display like below
ItemNo Years [1QtySold] [1_OnHand] [2QtySold] [2_OnHand]
-----------------------------------------------------------------------
1 2017 5 3 4 33
2 2017 2 5
3 2017 66 70
I tried in this way. But It doesn't work
select
PVT1.ItemID,
PVT1.StoreID,
PVT1.Years,
isnull([1],0) as [1QtySold], isnull([2],0) as [2QtySold],
isnull([1_OnHand],0) as [1_OnHand], isnull([2_OnHand],0) as [2_OnHand]
from
(
SELECT
ItemID,
StoreID,
Years,
Weeks,
AsOfWeekOnHand
FROM
SoldQtyTable
) L
PIVOT
(
SUM(AsOfWeekOnHand)
FOR Weeks IN ( [1_OnHand], [2_OnHand])
) AS PVT1
LEFT JOIN
(
SELECT
ItemID,
StoreID,
Years,
Weeks,
QtySold
FROM
SoldQtyTable
) L
PIVOT
(
SUM(QtySold)
FOR Weeks IN ( [1soldQty], [2soldQty]
) AS PVT2 on PVT2.ItemID = PVT1.ItemID and PVT1.Years = PVT2.Years
where
PVT1.years = 2017
I find conditional aggregation so much simpler:
SELECT ItemID, Years,
SUM(CASE WHEN weeks = 1 THEN QtySold END) as QtySold_1,
SUM(CASE WHEN weeks = 1 THEN AsOfWeekOnHand END) as AsOfWeekOnHand_1,
SUM(CASE WHEN weeks = 2 THEN QtySold END) as QtySold_2,
SUM(CASE WHEN weeks = 3 THEN AsOfWeekOnHand END) as AsOfWeekOnHand_2
FROM SoldQtyTable
GROUP BY ItemID, Years
ORDER BY ItemID, Years;
If you want to PIVOT multiple columns, you can achieve that by doing UNPIVOT first and then doing PIVOT on just one value column as described in this answer.
SELECT ItemID,
StoreID,
Years,
[1_QtySold],
[1_AsOfWeekOnHand] AS [1_OnHand],
[2_QtySold],
[2_AsOfWeekOnHand] AS [2_OnHand]
FROM
(
SELECT
ItemID,
StoreID,
Years,
Weeks + '_' + col AS col,
[value]
FROM
(
SELECT
ItemID,
StoreID,
Years,
CAST(Weeks as varchar) Weeks,
AsOfWeekOnHand,
QtySold
FROM
SoldQtyTable
WHERE Years = 2017 -- your original filter PVT1.years = 2017
) src
UNPIVOT
(
VALUE
FOR col in (AsOfWeekOnHand, QtySold)
) unpiv
) s
PIVOT
(
SUM([value])
FOR col IN ([1_AsOfWeekOnHand], [1_QtySold], [2_AsOfWeekOnHand], [2_QtySold])
) unpiv
ORDER BY StoreID
Here is the SQL Fiddle.

Display 12 months of data from the past 5 years

I am currently creating a script that will pull 5 years of invoice data and will summarize the invoice amounts by month of that year for a specific customer. Example
Year jan feb mar
2011 800 900 700
2012 700 800 900, and so forth
I am having issues getting my output to be like this though. My current code
select MAX(cust) as customer,year(invoicedate) as y, month(invoicedate) as m, sum(amount) as summary
from #tquery
group by year(dinvoice), month(dinvoice)
having MAX(ccustno) ='WILLAMETTE'
order by y asc,m asc
select * from #tquery
gives me this. which i just need to find a way to reformat it.
customer year month amount
WILLAMETTE 2012 11 500
WILLAMETTE 2012 12 600
WILLAMETTE 2013 1 600
No need to go through a Pivot. It is only 12 columns. A conditional aggregation would be more efficient
Select Customer = cust
,Year = year(invoicedate)
,Jan = sum(case when month(invoicedate) = 1 then amount else 0 end)
,Feb = sum(case when month(invoicedate) = 2 then amount else 0 end)
...
,Dec = sum(case when month(invoicedate) =12 then amount else 0 end)
From #tquery
Group by ccustno,year(dinvoice)
Order By 1,2
You must using PIVOT to reformat rows to column
select customer
,y
,"1","2","3","4","5","6","7","8","9","10","11","12"
from (select cust as customer,year(invoicedate) as y, month(invoicedate) as m,amount
from #tquery
where ccustno ='WILLAMETTE'
)
t
pivot (sum (amount) for m in ("1","2","3","4","5","6","7","8","9","10","11","12")) p
order by y
;

How to write where clause in pivot statement in SQL Server

I need to get records based on project and year
SELECT
*
FROM
(SELECT
Source,
val,
month
FROM
tbl_OrganicResult
CROSS APPLY
(VALUES ('visitors', visitors),
('UniqueVisitors', UniqueVisitors),
('ReturnVisitors', ReturnVisitors)) cs (Source, val)) A
PIVOT (Max(val)
FOR month IN ([jun], [jul)) pv
I need the records with where condition like
select *
from tbl_OrganicResult
where project = 'Homeocare'
Sample data
ProjectName Month Year visitors UniqueVisitors ReturnVisitors
Homeocare Jun 2015 400 33 22
Homeocare Jul 2015 100 10 8
debug test. Aug 2015 15222 122 120
debug test. Jun. 2015 1500 150 15
debug test. Jul 2015 1400 140 14
I'm getting records like this which is not correct, I mean I'm not getting project wise.
How to add where condition to get like where project - 'homeocare' ?
Getting output like this
Source jun jul
ReturnVisitors 8 120
UniqueVisitors 10 122
Visitors 100 15222
I need records like this
Source jun jul
ReturnVisitors. 22 8
UniqueVisitors. 33 10
Visitors 400 100
Sorry for my mistake of giving wrong data (earlier). Here always getting last 2 records and not used where condition to get records project and year wise.
The where clause should go after the cross apply like this:
SELECT
*
FROM
(
SELECT
Source,
val,
Year,
month
FROM
tbl_OrganicResult
CROSS APPLY
(VALUES ('visitors', visitors),
('UniqueVisitors', UniqueVisitors),
('ReturnVisitors', ReturnVisitors)) cs (Source, val)
WHERE ProjectName = 'Homeocare'
) A
PIVOT ( Max(val) FOR Month IN ([jun], [jul]) ) pv
Unless you only have data for one year in your table this query will get you the max value for a month from any year where you have data recorded for that month, for example if you had 100 visitors in June 15 and 200 visitors in June 14 then the value for June 14 would be selected. This might not be what you want.
I would consider doing something like this instead:
SELECT
Source, [Jun-2015], [Jul-2015]
FROM
(
SELECT
Source,
Val,
MonthYear = CONCAT(Month,'-',Year)
FROM
tbl_OrganicResult
CROSS APPLY
(VALUES ('Visitors', visitors),
('UniqueVisitors', UniqueVisitors),
('ReturnVisitors', ReturnVisitors)) cs (Source, Val)
WHERE ProjectName = 'Homeocare'
) A
PIVOT ( MAX(Val) FOR MonthYear IN ([Jun-2015], [Jul-2015]) ) pv ;
in your case you have three months but you are looking for july in place of june and August in place of july just add alias names for those columns
declare #table table (ProjectName varchar(20), Month varchar(20), Year varchar(20), visitors INT, UniqueVisitors INT, ReturnVisitors INT)
insert into #table (ProjectName,Month,Year,visitors,UniqueVisitors,ReturnVisitors)values
('Homeocare' , 'Jun' , 2015 , 400 , 33 , 22),
('Homeocare' , 'Jul' , 2015 , 100 , 10 , 8),
('debug test', 'Aug' , 2015 , 15222 , 122 , 120 )
SELECT source,JUN as Jul,jul as aug FROM (SELECT Source,
val,
month
FROM #table
CROSS apply (VALUES ('visitors',visitors),
('UniqueVisitors',UniqueVisitors),
('ReturnVisitors',ReturnVisitors)) cs (Source, val)) A
PIVOT (max(val)
FOR month IN ([jun],[Jul],
[Aug])) pv