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

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.

Related

What to use in place of union in above query i wrote or more optimize query then my given query without union and union all

I am counting the birthdays , sales , order in all 12 months from customers table in SQL server like these
In Customers table birth_date ,sale_date, order_date are columns of the table
select 1 as ranking,'Birthdays' as Type,[MONTH],TOTAL
from ( select DATENAME(month, birth_date) AS [MONTH],count(*) TOTAL
from customers
group by DATENAME(month, birth_date)
)x
union
select 2 as ranking,'sales' as Type,[MONTH],TOTAL
from ( select DATENAME(month, sale_date) AS [MONTH],count(*) TOTAL
from customers
group by DATENAME(month, sale_date)
)x
union
select 3 as ranking,'Orders' as Type,[MONTH],TOTAL
from ( select DATENAME(month, order_date) AS [MONTH],count(*) TOTAL
from customers
group by DATENAME(month, order_date)
)x
And the output is like these(just dummy data)
ranking
Type
MONTH
TOTAL
1
Birthdays
January
12
1
Birthdays
April
6
1
Birthdays
May
10
2
Sales
Febrary
8
2
Sales
April
14
2
Sales
May
10
3
Orders
June
4
3
Orders
July
3
3
Orders
October
6
3
Orders
December
17
I want to find count of these all these three types without using UNION and UNION ALL, means I want these data by single query statement (or more optimize version of these query)
Another approach is to create a CTE with all available ranking values ​​and use CROSS APPLY for it, as shown below.
WITH ranks(ranking) AS (
SELECT * FROM (VALUES (1), (2), (3)) v(r)
)
SELECT
r.ranking,
CASE WHEN r.ranking = 1 THEN 'Birthdays'
WHEN r.ranking = 2 THEN 'Sales'
WHEN r.ranking = 3 THEN 'Orders'
END AS Type,
DATENAME(month, CASE WHEN r.ranking = 1 THEN c.birth_date
WHEN r.ranking = 2 THEN c.sale_date
WHEN r.ranking = 3 THEN c.order_date
END) AS MONTH,
COUNT(*) AS TOTAL
FROM customers c
CROSS APPLY ranks r
GROUP BY r.ranking,
DATENAME(month, CASE WHEN r.ranking = 1 THEN c.birth_date
WHEN r.ranking = 2 THEN c.sale_date
WHEN r.ranking = 3 THEN c.order_date
END)
ORDER BY r.ranking, MONTH

My Sql PIVOT Query Is Not Working As Intended

I'm using the following SQL query to return a table with 4 columns Year, Month, Quantity Sold, Stock_Code,
SELECT yr, mon, sum(Quantity) as Quantity, STOCK_CODE
FROM [All Stock Purchased]
group by yr, mon, stock_code
order by yr, mon, stock_code
This is an example of some of the data BUT I have about 3000 Stock_Codes and approx 40 x yr/mon combinations.
yr mon Quantity STOCK_CODE
2015 4 42 100105
2015 4 220 100135
2015 4 1 100237
2015 4 2 100252
2015 4 1 100277
I want to pivot this into a table which has a row for each SKU and columns for every Year/Month combination.
I have never used Pivot before so have done some research and have created a SQL query that I believe should work.
select * from
(SELECT yr,
mon, Quantity,
STOCK_CODE
FROM [All Stock Purchased]) AS BaseData
pivot (
sum(Quantity)
For Stock_Code
in ([4 2015],[5 2015] ...........
) as PivotTable
This query returns a table with Yr as col1, Mon as col2 and then 4 2015 etc as subsequent columns. Whereas I want col1 to be Stock_Code and col2 to show the quantity of that stock code sold in 4 2015.
Would really like to understand what is wrong with my code above please.
The following query using dynamic PIVOT should do what you want:
CREATE TABLE #temp (Yr INT,Mnt INT,Quantity INT, Stock_Code INT)
INSERT INTO #temp VALUES
(2015,4,42,100105),
(2015,4,100,100105),
(2015,5,220,100135),
(2015,4,1,100237),
(2015,4,2,100252),
(2015,7,1,100277)
DECLARE #pvt NVARCHAR(MAX) = '';
SET #pvt = STUFF(
(SELECT DISTINCT N', ' + QUOTENAME(CONVERT(VARCHAR(10),Mnt) +' '+ CONVERT(VARCHAR(10),Yr)) FROM #temp FOR XML PATH('')),1,2,N'');
EXEC (N'
SELECT pvt.* FROM (
SELECT Stock_Code
,CONVERT(VARCHAR(10),Mnt) +'' ''+ CONVERT(VARCHAR(10),Yr) AS [Tag]
,Quantity
FROM #temp )a
PIVOT (SUM(Quantity) FOR [Tag] IN ('+#pvt+')) pvt');
Result is as below,
Stock_Code 4 2015 5 2015 7 2015
100105 142 NULL NULL
100135 NULL 220 NULL
100237 1 NULL NULL
100252 2 NULL NULL
100277 NULL NULL 1
You can achieve this without using pivoting.
SELECT P.`STOCK_CODE`,
SUM(
CASE
WHEN P.`yr`=2015 AND P.`mon` = '1'
THEN P.`Quantity`
ELSE 0
END
) AS '1 2015',
SUM(
CASE
WHEN P.`yr`=2015 AND P.`mon` = '2'
THEN P.`Quantity`
ELSE 0
END
) AS '2 2015',
SUM(
CASE
WHEN P.`yr`=2015 AND P.`mon` = '3'
THEN P.`Quantity`
ELSE 0
END
) AS '3 2015',
FROM [All Stock Purchased] P
GROUP BY P.`STOCK_CODE`;

Converting multiple rows to multiple columns in SQL

I have a data something like this:
Team Month Count_of_Stores
a 4 10
b 4 4
c 4 6
a 5 8
b 5 14
e 5 9
a 6 7
b 6 3
f 6 1
I working to get an output something like this converting the rows to columns:
Team Month Count_of_Stores Team Month Count_of_Stores Team Month Count_of_Stores
a 4 10 a 5 8 a 6 7
b 4 4 b 5 14 b 6 3
c 4 6 e 5 9 f 6 1
I am sure pivot should be of great help here, but confused in the appropriate usage here. any help is much appreciated.
I'm inferring team association based on the data provided. Something like the below could work for you (demo: http://rextester.com/GQHPV34978)
CREATE TABLE temp
(
[teamID] INT,
[month] INT,
[count_of_stores] INT
)
INSERT INTO temp
VALUES
(1,4,10),
(2,4,4),
(3,4,6),
(1,5,8),
(2,5,14),
(3,5,9),
(1,6,7),
(2,6,3),
(3,6,1)
SELECT [teamID],
MAX(CASE WHEN MONTH = 4 THEN MONTH END )AS Month,
MAX(CASE WHEN MONTH = 4 THEN count_of_stores END ) AS Count_of_Stores,
MAX(CASE WHEN MONTH = 5 THEN MONTH END )AS Month,
MAX(CASE WHEN MONTH = 5 THEN count_of_stores END ) AS Count_of_Stores ,
MAX(CASE WHEN MONTH = 6 THEN MONTH END )AS Month,
MAX(CASE WHEN MONTH = 6 THEN count_of_stores END ) AS Count_of_Stores
FROM temp
GROUP BY teamID
Updating with the following based on new information (demo:http://rextester.com/JIZQX61960)
create TABLE #temp
(
[teamID] varchar,
[month] INT,
[count_of_stores] INT
)
INSERT INTO #temp
VALUES
('a',4,10),
('b',4,4),
('c',4,6),
('a',5,8),
('b',5,14),
('e',5,9),
('a',6,7),
('b',6,3),
('f',6,1);
WITH monthGrouping AS
(
SELECT row_number() over (partition by month order by month) as rn, [teamID], [month],[count_of_stores] FROM #temp
)
SELECT
MAX(CASE WHEN MONTH = 4 THEN [teamID] END )AS [teamID],
MAX(CASE WHEN MONTH = 4 THEN MONTH END )AS Month,
MAX(CASE WHEN MONTH = 4 THEN count_of_stores END ) AS Count_of_Stores,
MAX(CASE WHEN MONTH = 5 THEN [teamID] END )AS [teamID],
MAX(CASE WHEN MONTH = 5 THEN MONTH END )AS Month,
MAX(CASE WHEN MONTH = 5 THEN count_of_stores END ) AS Count_of_Stores ,
MAX(CASE WHEN MONTH = 6 THEN [teamID] END )AS [teamID],
MAX(CASE WHEN MONTH = 6 THEN MONTH END )AS Month,
MAX(CASE WHEN MONTH = 6 THEN count_of_stores END ) AS Count_of_Stores
FROM monthGrouping
GROUP BY rn

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
;

SQL - Comparing and Grouping Data on multiple rows

I'm trying to query my database to find which products sold less in October than in either November or December.
I thought something like below would do it but I have a feeling the sub query will be returning the mininimum quantity for the whole database rather than for the specific product.
There must be some way of doing this using GROUP BY but I cant figure it out.
SELECT Category, Product
FROM Sales
WHERE SaleQuantity < (SELECT MIN(SaleQuantity)
FROM Sales
WHERE MonthNumber > 10)
AND MonthNumber = 10
Data looks like:
Category Product MonthNumber SaleQuantity
---------- ----------- ------------- -----------
11 14 10 210
11 14 11 200
11 14 12 390
15 12 10 55
15 12 11 24
17 12 12 129
19 10 10 12
Thanks.
try something like this
SELECT Category,
Product,
SUM( s.SaleQuantity ) AS saleOcotber,
SUM( ISNULL( son.SaleQuantity, 0 ) ) AS saleNovember,
SUM( ISNULL( sod.SaleQuantity, 0 ) ) AS saleDecember
FROM Sales s
LEFT OUTER JOIN Sales son ON son.Category = s.Category
AND son.Product = s.Product
AND son.MonthNumber = 11
LEFT OUTER JOIN Sales sod ON sod.Category = s.Category
AND sod.Product = s.Product
AND sod.MonthNumber = 11
WHERE s.MonthNumber = 10
GROUP BY Category,Product
WHERE SUM( s.SaleQuantity ) < SUM( ISNULL( son.SaleQuantity, 0 ) )
OR SUM( s.SaleQuantity ) < SUM( ISNULL( sod.SaleQuantity, 0 ) )
I have no tested this select but i think it will do the job if there is something not clear
please ask
Best Regards,
Iordan
PS. I presume you are using some version of MSSQL if not try to rewrite it by yourself int SQL you are using
Your table already appears to be summarised by Category, Product and MonthNumber, for SalesQuantity. If so, try this:
select distinct Category, Product
from Sales s11_12
where MonthNumber in (11,12) and
not exists (select null
from Sales s10
where s10.Category = s11_12.Category and
s10.Product = s11_12.Product and
s10.SalesQuantity >= s11_12.SalesQuantity)