My Sql PIVOT Query Is Not Working As Intended - sql

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`;

Related

How to get every nth row value in sql

I have a select statement in sql:
SELECT DISTINCT [Year] FROM [data_list] ORDER BY [YEAR] ASC;
Result:
Year
----
2019
2020
2021
2022
2023
2024
2025
2026
2027
2028
I want list of year in 3 increments. For example:
Year
----
2019
2022
2025
2028
One way to do it is to use a common table expression with dense_rank, and then filter the results by it.
First, create and populate sample table (Please save us this step in your future questions):
DECLARE #T AS TABLE
(
[Year] int
)
INSERT INTO #T ([Year]) VALUES
(2019),
(2019),
(2019),
(2020),
(2020),
(2021),
(2022),
(2022),
(2023),
(2024),
(2024),
(2025),
(2025),
(2026),
(2027),
(2027),
(2028);
The cte and query:
WITH CTE AS
(
SELECT [Year],
DENSE_RANK() OVER(ORDER BY [Year]) - 1 As dr
FROM #T
)
SELECT DISTINCT [Year]
FROM CTE
WHERE dr % 3 = 0
ORDER BY [Year]
results:
Year
2019
2022
2025
2028
You can use ROW_NUMBER:
SELECT [Year]
FROM (
SELECT [Year], ROW_NUMBER() OVER (ORDER BY [Year] ASC) rn
FROM table_name
GROUP BY [YEAR]
) t WHERE (t.rn - 1) % 3 = 0
demo on dbfiddle.uk
Try it,
;WITH CTEs AS
(
SELECT *,(CASE WHEN [Year] %3 =0 THEN [Year] END) AS [Year] FROM yourTable
)
SELECT * FROM CTEs WHERE [Year] IS NOT NULL

Fill up date gap by month

I have table of products and their sales quantity in months.
Product Month Qty
A 2018-01-01 5
A 2018-02-01 3
A 2018-05-01 5
B 2018-08-01 10
B 2018-10-01 12
...
I'd like to first fill in the data gap between each product's min and max dates like below:
Product Month Qty
A 2018-01-01 5
A 2018-02-01 3
A 2018-03-01 0
A 2018-04-01 0
A 2018-05-01 5
B 2018-08-01 10
B 2018-09-01 0
B 2018-10-01 12
...
Then I would need to perform an accumulation of each product's sales quantity by month.
Product Month total_Qty
A 2018-01-01 5
A 2018-02-01 8
A 2018-03-01 8
A 2018-04-01 8
A 2018-05-01 13
B 2018-08-01 10
B 2018-09-01 10
B 2018-10-01 22
...
I fumbled over the "cross join" clause, however it seems to generate some unexpected results for me. Could someone help to give a hint how I can achieve this in SQL?
Thanks a lot in advance.
I think a recursive CTE is a simple way to do this. The code is just:
with cte as (
select product, min(mon) as mon, max(mon) as end_mon
from t
group by product
union all
select product, dateadd(month, 1, mon), end_mon
from cte
where mon < end_mon
)
select cte.product, cte.mon, coalesce(qty, 0) as qty
from cte left join
t
on t.product = cte.product and t.mon = cte.mon;
Here is a db<>fiddle.
Hi i think this example can help you and perform what you excepted :
CREATE TABLE #MyTable
(Product varchar(10),
ProductMonth DATETIME,
Qty int
);
GO
CREATE TABLE #MyTableTempDate
(
FullMonth DATETIME
);
GO
INSERT INTO #MyTable
SELECT 'A', '2019-01-01', 214
UNION
SELECT 'A', '2019-02-01', 4
UNION
SELECT 'A', '2019-03-01', 50
UNION
SELECT 'B', '2019-01-01', 214
UNION
SELECT 'B', '2019-02-01', 10
UNION
SELECT 'C', '2019-04-01', 150
INSERT INTO #MyTableTempDate
SELECT '2019-01-01'
UNION
SELECT '2019-02-01'
UNION
SELECT '2019-03-01'
UNION
SELECT '2019-04-01'
UNION
SELECT '2019-05-01'
UNION
SELECT '2019-06-01'
UNION
SELECT '2019-07-01';
------------- FOR NEWER SQL SERVER VERSION > 2005
WITH MyCTE AS
(
SELECT T.Product, T.ProductMonth AS 'MMonth', T.Qty
FROM #MyTable T
UNION
SELECT T.Product, TD.FullMonth AS 'MMonth', 0 AS 'Qty'
FROM #MyTable T, #MyTableTempDate TD
WHERE NOT EXISTS (SELECT 1 FROM #MyTable TT WHERE TT.Product = T.Product AND TD.FullMonth = TT.ProductMonth)
)
-- SELECT * FROM MyCTE;
SELECT Product, MMonth, Qty, SUM( Qty) OVER(PARTITION BY Product ORDER BY Product
ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW) as 'TotalQty'
FROM MyCTE
ORDER BY Product, MMonth ASC;
DROP TABLE #MyTable
DROP TABLE #MyTableTempDate
I have other way to perform this in lower SQL Server Version (like 2005 and lower)
It's a SELECT on SELECT if it's your case let me know and i provide some other example.
You can create the months with a recursive CTE
DECLARE #MyTable TABLE
(
ProductID CHAR(1),
Date DATE,
Amount INT
)
INSERT INTO #MyTable
VALUES
('A','2018-01-01', 5),
('A','2018-02-01', 3),
('A','2018-05-01', 5),
('B','2018-08-01', 10),
('B','2018-10-01', 12)
DECLARE #StartDate DATE
DECLARE #EndDate DATE
SELECT #StartDate = MIN(Date), #EndDate = MAX(Date) FROM #MyTable
;WITH dates AS (
SELECT #StartDate AS Date
UNION ALL
SELECT DATEADD(Month, 1, Date)
FROM dates
WHERE Date < #EndDate
)
SELECT A.ProductID, d.Date, COALESCE(Amount,0) AS Amount, COALESCE(SUM(Amount) OVER(PARTITION BY A.ProductID ORDER BY A.ProductID, d.Date ROWS BETWEEN UNBOUNDED PRECEDING AND CURRENT ROW),0) AS Total
FROM
(
SELECT ProductID, MIN(date) as DateStart, MAX(date) as DateEnd
FROM #MyTable
GROUP BY ProductID -- As I read in your comments that you need different min and max dates per product
) A
JOIN dates d ON d.Date >= A.DateStart AND d.Date <= A.DateEnd
LEFT JOIN #MyTable T ON A.ProductID = T.ProductID AND T.Date = d.Date
ORDER BY A.ProductID, d.Date
Try this below
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
DROP TABLE #Temp
;WITH CTE(Product,[Month],Qty)
AS
(
SELECT 'A','2018-01-01', 5 UNION ALL
SELECT 'A','2018-02-01', 3 UNION ALL
SELECT 'A','2018-05-01', 5 UNION ALL
SELECT 'B','2018-08-01', 10 UNION ALL
SELECT 'D','2018-10-01', 12
)
SELECT ct.Product,[MonthDays],ct.Qty
INTO #Temp
FROM
(
SELECT c.Product,[Month],
ISNULL(Qty,0) AS Qty
FROM CTE c
)ct
RIGHT JOIN
(
SELECT -- This code is to get month data
CONVERT(VARCHAR(10),'2018-'+ RIGHT('00'+CAST(MONTH(DATEADD(MM, s.number, CONVERT(DATETIME, 0)))AS VARCHAR),2) +'-01',120) AS [MonthDays]
FROM master.dbo.spt_values s
WHERE [type] = 'P' AND s.number BETWEEN 0 AND 11
)DT
ON dt.[MonthDays] = ct.[Month]
SELECT
MAX(Product)OVER(ORDER BY [MonthDays])AS Product,
[MonthDays],
ISNULL(Qty,0) Qty,
SUM(ISNULL(Qty,0))OVER(ORDER BY [MonthDays]) As SumQty
FROM #Temp
Result
Product MonthDays Qty SumQty
------------------------------
A 2018-01-01 5 5
A 2018-02-01 3 8
A 2018-03-01 0 8
A 2018-04-01 0 8
A 2018-05-01 5 13
A 2018-06-01 0 13
A 2018-07-01 0 13
B 2018-08-01 10 23
B 2018-09-01 0 23
D 2018-10-01 12 35
D 2018-11-01 0 35
D 2018-12-01 0 35
First of all, i would divide month and year to get easier with statistics.
I will give you an example query, not based on your table but still helpful.
--here i create the table that will be used as calendar
Create Table MA_MonthYears (
Month int not null ,
year int not null
PRIMARY KEY ( month, year) )
--/////////////////
-- here i'm creating a procedure to fill the ma_monthyears table
declare #month as int
declare #year as int
set #month = 1
set #year = 2015
while ( #year != 2099 )
begin
insert into MA_MonthYears(Month, year)
select #month, #year
if #month < 12
set #month=#month+1
else
set #month=1
if #month = 1
set #year = #year + 1
end
--/////////////////
--here you are the possible result you are looking for
select SUM(Ma_saledocdetail.taxableamount) as Sold, MA_MonthYears.month , MA_MonthYears.year , item
from MA_MonthYears left outer join MA_SaleDocDetail on year(MA_SaleDocDetail.DocumentDate) = MA_MonthYears.year
and Month(ma_saledocdetail.documentdate) = MA_MonthYears.Month
group by MA_SaleDocDetail.Item, MA_MonthYears.year , MA_MonthYears.month
order by MA_MonthYears.year , MA_MonthYears.month

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.

in sql how to arrange rows of data into colum using Pivot

Here i have a simple table i want to display all rows respected that year display in single column
Year Month Amt
1999 Jan 520
1999 Feb 100
199 Mar 200
2000 Jan 500
2000 Feb 200
I want to display these table as
Year Jan Feb Mar
1999 520 100 200
2000 500 200 null
I had Written query as invoice its my table name
select
[Jan] as January,
[Feb] as Feburary,
[March] as Feburary,
from(
select Year,month,amount from invoice)x
PIVOT(
sum(amount)
for month in([jan],[Feb],[March])
)p
Please Try This Query
create table #Invoice
(
ID int identity(1,1),
Year varchar(4),
Month varchar(3),
Amount int
)
insert into #Invoice (Year, Month, Amount) values ('1999','Jan',520),('1999','Feb',100),('1999','Mar',200),
('2000','Jan',500),('2000','Feb',200)
select Year, [Jan], [Feb],[Mar],[Grand Total]
from (
select Year, Month, Amount
from #Invoice
Union all
select Year, 'Grand Total', SUM(Amount)
from #Invoice
group by year
)dd
pivot (
sum(Amount) for Month in ([Jan], [Feb],[Mar],[Grand Total])
) piv
drop table #Invoice
You can achieve it using CTE. You can't GROUP BY YEAR within the PIVOT table operator, the PIVOT operator infers the grouped columns automatically. This seems to work
WITH Pivoted
AS
(
SELECT *
FROM table1
PIVOT
(
sum([Amt]) FOR [month] IN ( [jan],[Feb],[Mar])
) AS p
)
SELECT
Year,
sum([Jan]) as January,
sum([Feb]) as Feburary,
sum([Mar]) as March
FROM Pivoted
GROUP BY Year;
REXTESTER DEMO

Dynamic PIVOT in SQL Server 2005

i rare use PIVOT in sql server but now requirement is something that i have to use PIVOT.
my table structure is something like
CurDate Warranty_Info
------- -------------
01/01/2009 50
01/05/2009 30
01/03/2009 220
01/01/2010 40
01/06/2010 10
01/02/2010 0
01/01/2011 10
01/05/2012 420
01/05/2013 130
now i have to show the data in this way
Month 2009 2010 2011 2012 2013
----- ---- ---- ---- ---- -----
JAN 10 0 11 32 98
FEB 20 10 21 11 44
MAR 0 224 33 77 31
UPTO
DEC
1) data should display month wise order....so jan first
2) if no data exist in any month then month name will come with 0 as value for that month.
i tried but fail. here is my sql by which i tried.
SELECT *
FROM (SELECT DateName(month,DateAdd(month,Month(CurDate),-1)) as [Month],
YEAR(CurDate) AS WarrantyYear,
Warranty_Info
FROM eod_main) AS D
PIVOT (
SUM(Warranty_Info)
FOR WarrantyYear IN (
[2009],[2010],[2011],[2012],[2013]
)
) AS P
ORDER BY DATENAME(MONTH,DATEADD(MONTH, [Month] - 1, 0))
and i tried to generate sql dynamically this way.
DECLARE #cols AS NVARCHAR(MAX)
DECLARE #PivotTableSQL NVARCHAR(MAX)
DECLARE #StartYear AS INT,
#EndYear AS INT
SET #StartYear=2009
SET #EndYear=2013
select #cols = STUFF((SELECT ',' + QUOTENAME(Year(CurDate))
from eod_main
WHERE Year(CurDate)>=#StartYear AND Year(CurDate) <=#EndYear
group by Year(CurDate)
order by Year(CurDate)
FOR XML PATH(''), TYPE
).value('.', 'NVARCHAR(MAX)')
,1,1,'')
SET #PivotTableSQL = N'
SELECT * FROM (
SELECT Month(CurDate), YEAR(CurDate) AS WarrantyYear,Warranty_Info FROM eod_main) AS D
PIVOT (
SUM(Warranty_Info)
FOR WarrantyYear IN (
' + #cols + '
)
) AS PivotTable
'
print #PivotTableSQL
but some where i am facing problem like
1) display month name
2) order by month no
3) null value show show 0 instead of NULL
4) if no data exist for any month then month name should display with 0 value.
please guide me how to achieve it. thanks
UPDATE
DECLARE #query varchar(max)
DECLARE #StartYr INT
DECLARE #ENDYr INT
declare #years varchar(max), #yearsColumns varchar(max)
SET #StartYr=2011
SET #EndYr=2013
SELECT 1 mID, 'January' as month into #tempMonths UNION ALL
SELECT 2,'February' as month UNION ALL
SELECT 3,'March' as month UNION ALL
SELECT 4,'April' as month UNION ALL
SELECT 5,'May' as month UNION ALL
SELECT 6,'June' as month UNION ALL
SELECT 7,'July' as month UNION ALL
SELECT 8,'August' as month UNION ALL
SELECT 9,'September' as month UNION ALL
SELECT 10,'October' as month UNION ALL
SELECT 11,'November' as month UNION ALL
SELECT 12,'December' as month
SELECT #years=COALESCE(#years+',','') +'['+ cast(years as varchar(4))+']',
#yearsColumns=COALESCE(#yearsColumns+',','') +'isnull(['+ cast(years as varchar(4))+'],0)
as ['+cast(years as varchar(4))+']'
from (select distinct YEAR(CurDate) years from EOD_Main
WHERE YEAR(CurDate)>=#StartYr AND YEAR(CurDate)<=#EndYr
) as x
SET #query = 'Select months,'+#yearsColumns+' from (
select distinct mID, YEAR(CurDate) years,[MONTH] months,
isnull(Warranty_Info,0) as Warranty_Info from EOD_Main
right join #tempMonths on datename(month,CurDate ) =[month]
) as xx
PIVOT
(
SUM(xx.Warranty_Info) FOR years IN ('+#years+')
)
as pvt ORDER BY mID'
--PRINT #query
EXEC(#query)
drop table #tempMonths
Hi Find the Below Solution, i hope that it is help full to you
SELECT CAST('01/01/2009' AS date) CurDate , 50 Warranty_Info INto #temp UNION all SELECT
CAST('05/01/2009' AS date) , 30 UNION all SELECT
CAST('03/01/2009' AS date) , 220 UNION all SELECT
CAST('01/01/2010' AS date) , 40 UNION all SELECT
CAST('06/01/2010' AS date) , 10 UNION all SELECT
CAST('02/01/2010' AS date) , 0 UNION all SELECT
CAST('01/01/2011' AS date) , 10 UNION all SELECT
CAST('05/01/2012' AS date) , 420 UNION all SELECT
CAST('05/01/2013' AS date) , 130
SELECT 1 mID, 'January' as month into #tempMonths UNION ALL
SELECT 2,'February' as month UNION ALL
SELECT 3,'March' as month UNION ALL
SELECT 4,'April' as month UNION ALL
SELECT 5,'May' as month UNION ALL
SELECT 6,'June' as month UNION ALL
SELECT 7,'July' as month UNION ALL
SELECT 8,'August' as month UNION ALL
SELECT 9,'September' as month UNION ALL
SELECT 10,'October' as month UNION ALL
SELECT 11,'November' as month UNION ALL
SELECT 12,'December' as month
declare #years varchar(max), #yearsColumns varchar(max)
SELECT #years=COALESCE(#years+',','') +'['+ cast(years as varchar(4))+']',
#yearsColumns=COALESCE(#yearsColumns+',','') +'isnull(['+ cast(years as varchar(4))+'],0) as ['+cast(years as varchar(4))+']'
from (select distinct YEAR(CurDate) years from #temp) as x
print #years
DECLARE #query varchar(max)= '
select months,'+#yearsColumns+' from (
select distinct mID, YEAR(CurDate) years,[MONTH] months, isnull(Warranty_Info,0) as Warranty_Info from #temp
right join #tempMonths on datename(month,CurDate ) =[month]
) as xx
PIVOT
(
SUM(xx.Warranty_Info) FOR years IN ('+#years+')
)
as pvt ORDER BY mID'
PRINT #query
EXEC(#query)
output islike below
months 2009 2010 2011 2012 2013
--------- ----------- ----------- ----------- ----------- -----------
January 50 40 10 0 0
February 0 0 0 0 0
March 220 0 0 0 0
April 0 0 0 0 0
May 30 0 0 420 130
June 0 10 0 0 0
July 0 0 0 0 0
August 0 0 0 0 0
September 0 0 0 0 0
October 0 0 0 0 0
November 0 0 0 0 0
December 0 0 0 0 0
if it is give accurate result don't forget to make a vote.