FIFO Balance at every month end with sql - sql

I am trying to get the balance quantity at the end of every month i.e. running total at every month end with FIFO only but its not showing any result. Pls help
Here is my query.
declare #Stock table (Item char(3) not null,Date date not null,TxnType varchar(3) not null,Qty int not null,Price decimal(10,2) null)
insert into #Stock(Item , [Date] , TxnType, Qty, Price) values
('ABC','20120401','IN', 200, 750.00),
('ABC','20120402','OUT', 100 ,null ),
('ABC','20120403','IN', 50, 700.00),
('ABC','20120404','IN', 75, 800.00),
('ABC','20120405','OUT', 175, null ),
('XYZ','20120406','IN', 150, 350.00),
('XYZ','20120407','OUT', 120 ,null ),
('XYZ','20120408','OUT', 10 ,null ),
('XYZ','20120409','IN', 90, 340.00),
('ABC','20120510','IN', 200, 750.00),
('ABC','20120511','OUT', 100 ,null ),
('ABC','20120512','IN', 50, 700.00),
('ABC','20120513','IN', 75, 800.00),
('ABC','20120514','OUT', 175, null ),
('XYZ','20120515','IN', 150, 350.00),
('XYZ','20120516','OUT', 120 ,null ),
('XYZ','20120517','OUT', 10 ,null ),
('XYZ','20120518','IN', 90, 340.00);
;WITH OrderedIn as (
select *,ROW_NUMBER() OVER (PARTITION BY month(date) ORDER BY DATE) as rn
from #Stock
where TxnType = 'IN'
), RunningTotals as (
select Item,Qty,Price,Qty as Total,0 as PrevTotal,rn from OrderedIn where rn = 1
union all
select rt.Item,oi.Qty,oi.Price,rt.Total + oi.Qty,rt.Total,oi.rn
from
RunningTotals rt
inner join
OrderedIn oi
on
rt.Item = oi.Item and
rt.rn = oi.rn - 1
), TotalOut as (
select Item,SUM(Qty) as Qty from #Stock where TxnType='OUT' group by Item
)
select
rt.Item,SUM(CASE WHEN PrevTotal > out.Qty THEN rt.Qty ELSE rt.Total - out.Qty END * Price)
from
RunningTotals rt
inner join
TotalOut out
on
rt.Item = out.Item
where
rt.Total > out.Qty
group by rt.Item
Output
Month Item (No column name(qty*price))
4 ABC 40000
4 XYZ 37600
5 ABC 77500
5 XYZ 76100

With you sample data.Are you looking for this,else clearly sketch your desired output .
;WITH CTE AS
(
select Item ,[Date] ,TxnType ,s.Qty
,Price ,ROW_NUMBER() OVER (PARTITION BY month(date),TxnType ORDER BY DATE) as rn
from #Stock S
)
,
CTE1 AS
(
SELECT ITEM ,[DATE] ,TXNTYPE ,QTY,MONTH(DATE) MONTHDT,RN FROM CTE WHERE RN=1
UNION ALL
SELECT S.ITEM ,S.[DATE] ,S.TXNTYPE ,S.QTY+A.QTY,A.MONTHDT ,S.RN
FROM CTE S INNER JOIN CTE1 A
ON S.TXNTYPE=A.TXNTYPE AND MONTH(S.[DATE])=A.MONTHDT AND S.RN-A.RN=1
)
,CTE2 AS
(
SELECT *
,ROW_NUMBER() OVER (PARTITION BY month(date),TxnType ORDER BY QTY DESC )RN1
FROM CTE1 S
)
SELECT A.DATE,A.QTY-(SELECT B.Qty from CTE2 B
where a.MONTHDT=b.MONTHDT and b.RN1=1 AND b.TxnType='OUT')
from CTE2 A
WHERE A.RN1=1 AND a.TxnType='IN'

Try this.
;WITH cte
AS (SELECT Row_number()OVER(partition BY item, Datepart(mm, date) ORDER BY date DESC) rn,
item,
Month(date) AS [month],
TxnType,
Price,
date
FROM #Stock
WHERE TxnType = 'IN'),
cte1
AS (SELECT Row_number() OVER(partition BY item, Datepart(mm, date) ORDER BY date DESC) rn,
item,
Month(date) [month],
(SELECT Sum(CASE
WHEN TxnType = 'out' THEN -1 * qty
ELSE qty
END) AS qty
FROM #Stock b
WHERE a.item = b.item
AND a.Date >= b.Date) qty
FROM #Stock a)
SELECT a.[month],
a.Item,
( a.Price * b.qty ) running_tot
FROM cte a
JOIN cte1 b
ON a.Item = b.Item
AND a.[month] = b.[month]
WHERE a.rn = 1
AND b.rn = 1

Related

Problem with Recursive CTE very long query plan

When I execute below query SQL run this plan and it took a long time to run it and it will not be over.
QueryPlanLink
I have 3 million records in #T table.
myCode:
;WITH cte1 AS (
SELECT NationalId,len(NationalId) as LenNationalId,CustomerType,FullDateInt,time,
SUM(Price) as SUMPrice
,AVG(Price) as Price
,SUM(Volume) as Volume
,SUM (sum([Volume])) OVER (PARTITION BY NationalId,len(NationalId) ORDER BY FullDateInt,[Time]) as SumVol
,ROW_NUMBER() OVER (PARTITION BY NationalId,len(NationalId) ORDER BY FullDateInt,[Time]) AS rn
from #T as T1
group by NationalId,len(NationalId),CustomerType,FullDateInt,time
), rcte AS (
SELECT *, Price AS Cost , cast(0 as decimal) as Profit
FROM cte1 AS base
WHERE base.rn = 1
UNION ALL
SELECT curr.*, Case when curr.Volume>0 Then ((curr.Volume *curr.Price) + (prev.Cost*prev.SumVol))/nullif(curr.SumVol,0)
when curr.Volume<0 Then prev.Cost
End
as Cost
,ISNULL(Cast (Case when curr.Volume<0 Then -1*(curr.Price-Cost)*curr.Volume End as decimal),0) as Profit
FROM cte1 AS curr
INNER JOIN rcte AS prev
ON curr.NationalId = prev.NationalId AND curr.rn = prev.rn + 1
)
Select * from rcte
option (maxrecursion 0)
Is there any way to make it better?
Thanks
I Change My Query like below And Everything is Done. Thanks For All.
SELECT NationalId,len(NationalId) as LenNationalId,CustomerType,FullDateInt,time,
SUM(Price) as SUMPrice
,AVG(Price) as Price
,SUM(Volume) as Volume
,SUM (sum([Volume])) OVER (PARTITION BY NationalId,len(NationalId) ORDER BY FullDateInt,[Time]) as SumVol
,ROW_NUMBER() OVER (PARTITION BY NationalId,len(NationalId) ORDER BY FullDateInt,[Time]) AS rn
into #TCTE from #T as T1
group by NationalId,len(NationalId),CustomerType,FullDateInt,time
;With rcte AS (
SELECT *, Price AS Cost , cast(0 as decimal) as Profit
FROM #TCTE AS base
WHERE base.rn = 1
UNION ALL
SELECT curr.*, Case when curr.Volume>0 Then ((curr.Volume *curr.Price) + (prev.Cost*prev.SumVol))/nullif(curr.SumVol,0)
when curr.Volume<0 Then prev.Cost
End
as Cost
,ISNULL(Cast (Case when curr.Volume<0 Then -1*(curr.Price-Cost)*curr.Volume End as decimal),0) as Profit
FROM #TCTE AS curr
INNER JOIN rcte AS prev
ON curr.NationalId = prev.NationalId AND curr.rn = prev.rn + 1
)
Select *
into #TFinal from rcte
option (maxrecursion 0)

Bring next value after condition

I am trying to fetch the next value after the condition is found. In this case, it is a row from 13/05/2021 the result I want to see is the row from 19/05/2021 Cte and CTE1 bring correct results.
I can't figure out what is wrong with my query.
<with cte as
(
select
customerid
,max(timestamp) as [Case Submitted]
,row_number() over (partition by [CustomerId] order by [CustomerId] ,max([timestamp]) desc) as rownum
from Table1
where substatus = 'Case Submitted'
and timestamp > '2021-01-01'
Group by
customerid
,timestamp
)
,CTE2 as
(
Select *
from cte
Where rownum = 1
),
CTE3 as
(
select
PS.customerid
,(PS.timestamp) as [Customer Support]
,row_number() over (partition by PS.customerid order by PS.customerid ) as rownum
from Table1 PS
left join CTE2 C2 on C2.customerid = PS.customerid and C2.[Case Submitted] > PS.timestamp and C2.rownum =1
where status = 'Customer Support'
and timestamp > '2021-01-01'
Group by
PS.customerid
,ps.timestamp
)
Select*
from CTE3>
untested notepad scribble
with CTE1 as
(
select
customerid
, [timestamp] as [Case Submitted]
, rownum = row_number() over (partition by CustomerId order by [timestamp] desc)
from Table1
where substatus = 'Case Submitted'
and [timestamp] > cast('2021-01-01' as date)
),
CTE2 AS
(
select
PS.customerid
, PS.timestamp as [Customer Support]
, rownum = row_number() over (partition by PS.customerid order by PS.timestamp)
from Table1 as PS
join CTE1 as C1
on C1.customerid = PS.customerid
and C1.[Case Submitted] > PS.[timestamp]
and C1.rownum = 1
where PS.status = 'Customer Support'
and [timestamp] > cast('2021-01-01' as date)
)
select *
from CTE2
where rownum = 1

How to find the highest value in a year and in all months

I want to return a year in which was the most contracts made and a month throughout all years - in which month the highest number of contracts is made.
I've tried:
SELECT
cal.CalendarYear
,cal.MonthOfYear
,COUNT(*) AS Cnt
FROM dim.Application app
JOIN dim.Calendar cal ON app.ApplicationDateID = cal.DateId
--WHERE (CalendarYear IS NULL) OR (MonthOfYear IS NULL)
GROUP BY
cal.CalendarYear
,cal.MonthOfYear
WITH CUBE
ORDER BY COUNT(*) DESC
and...
--;WITH maxYear AS (
SELECT TOP 1
cal.CalendarYear AS [Year]
,0 AS [Month]
,COUNT(*) AS Cnt
FROM dim.Application app
JOIN dim.Calendar cal ON app.ApplicationDateID = cal.DateId
GROUP BY cal.CalendarYear
-- ORDER BY COUNT(*) DESC
--)
UNION ALL
--,maxMonth AS (
SELECT TOP 1
0 AS [Year]
,cal.MonthOfYear AS [Month]
,COUNT(*) AS Cnt
FROM dim.Application app
JOIN dim.Calendar cal ON app.ApplicationDateID = cal.DateID
GROUP BY cal.MonthOfYear
ORDER BY COUNT(*) DESC
--)
Any help would be appreciated. Thanks.
This will ORDER BY each portion of the UNION independently, and still have the results joined in one SELECT...
SELECT x.* FROM (
SELECT TOP 1
cal.CalendarYear AS [Year]
,0 AS [Month]
,COUNT(*) AS Cnt
FROM dim.Application app
JOIN dim.Calendar cal ON app.ApplicationDateID = cal.DateId
GROUP BY cal.CalendarYear
ORDER BY COUNT(*) DESC
) x
UNION ALL
SELECT x.* FROM (
SELECT TOP 1
0 AS [Year]
,cal.MonthOfYear AS [Month]
,COUNT(*) AS Cnt
FROM dim.Application app
JOIN dim.Calendar cal ON app.ApplicationDateID = cal.DateID
GROUP BY cal.MonthOfYear
ORDER BY COUNT(*) DESC
) x
Get the counts per year and month and use row_number to get the year and month with the highest contracts.
SELECT
MAX(CASE WHEN year_rank=1 then Year END) as Highest_contracts_year,
MAX(CASE WHEN year_rank=1 then Year_count END) as Year_Contracts_count
MAX(CASE WHEN month_year_rank=1 then Month END) as Highest_contracts_Month,
MAX(CASE WHEN month_year_rank=1 then Month_Year_count END) as MonthYear_Contracts_count
FROM (SELECT T.*
,ROW_NUMBER() OVER(ORDER BY Year_Cnt DESC) as Year_rank
,ROW_NUMBER() OVER(ORDER BY Month_Year_Cnt DESC) as Month_Year_rank
FROM (SELECT
cal.CalendarYear AS [Year]
,cal.MonthOfYear AS [Month]
,COUNT(*) OVER(PARTITION BY cal.CalendarYear) AS Year_Cnt
,COUNT(*) OVER(PARTITION BY cal.MonthOfYear) AS Month_Year_Cnt
FROM dim.Application app
JOIN dim.Calendar cal ON app.ApplicationDateID = cal.DateId
) T
) T
You should specify what needs to be done when there are ties for highest counts. Assuming you need all highest count rows when there are ties, use
With ranks as
(SELECT T.*
,RANK() OVER(ORDER BY Year_Cnt DESC) as Year_rank
,RANK() OVER(ORDER BY Month_Year_Cnt DESC) as Month_Year_rank
FROM (SELECT
cal.CalendarYear AS [Year]
,cal.MonthOfYear AS [Month]
,COUNT(*) OVER(PARTITION BY cal.CalendarYear) AS Year_Cnt
,COUNT(*) OVER(PARTITION BY cal.MonthOfYear) AS Month_Year_Cnt
FROM dim.Application app
JOIN dim.Calendar cal ON app.ApplicationDateID = cal.DateId
) T
)
SELECT *
FROM (SELECT DISTINCT Year,Year_Cnt FROM RANKS WHERE Year_rank=1) ry
CROSS JOIN (SELECT DISTINCT Month,Month_Year_Cnt FROM RANKS WHERE Month_Year_rank=1) rm
EDIT: This might be what you want, unless you want it on single line:
select calendaryear AS 'year/month', cnt from (
SELECT TOP 1
cal.CalendarYear
,COUNT(*) AS Cnt
FROM dim.Application AS app
JOIN dim.Calendar AS cal ON app.ApplicationDateID = cal.DateId
GROUP BY
cal.CalendarYear
ORDER BY COUNT(*) DESC
) as year
UNION ALL
select MonthOfYear, Cnt FROM (
SELECT TOP 1
cal.CalendarYear
,cal.MonthOfYear
,COUNT(*) AS Cnt
FROM dim.Application AS app
JOIN dim.Calendar AS cal ON app.ApplicationDateID = cal.DateId
GROUP BY
cal.CalendarYear
,cal.MonthOfYear
ORDER BY COUNT(*) DESC
) AS month
It returns following result where month 3 is in fact 2016:
year/month cnt
2017 4
3 2
I have used following data as input
create table #calendar (DateId int, calendaryear int, monthofyear int)
create table #application (applicationdateId int)
insert into #calendar values (1,2017,01)
insert into #calendar values (2,2017,02)
insert into #calendar values (3,2017,03)
insert into #calendar values (4,2016,01)
insert into #calendar values (5,2016,03)
insert into #application values (1)
insert into #application values (1)
insert into #application values (2)
insert into #application values (3)
insert into #application values (4)
insert into #application values (5)
insert into #application values (5)

SQL server to do like Group By task

I have a table with SQL server as below,
Date Value
---------------------------------------------------
08-01-2016 1
08-02-2016 1
08-03-2016 1
08-04-2016 1
08-05-2016 1
08-06-2016 2
08-07-2016 2
08-08-2016 2
08-09-2016 2.5
08-10-2016 1
08-11-2016 1
Since the original table is too large, even I used 'Results to file', it still raise the exception 'System.OutOfMemoryException'. That's why I want to organize the table into this kind.
But I don't have a good logic to deal with. Therefore, I want to change the table into this kind as below.
Date_from Date_to Value
-------------------------------------------------
08-01-2016 08-05-2016 1
08-06-2016 08-08-2016 2
08-09-2016 08-09-2016 2.5
08-10-2016 08-11-2016 1
I appreciate your ideas!
Commonly called as Groups and Island problem. Here is one trick to do this
;WITH data
AS (SELECT *,Lag(Value, 1)OVER(ORDER BY Dates) [pVal]
FROM (VALUES ('08-01-2016',1 ),
('08-02-2016',1 ),
('08-03-2016',1 ),
('08-04-2016',1 ),
('08-05-2016',1 ),
('08-06-2016',2 ),
('08-07-2016',2 ),
('08-08-2016',2 ),
('08-09-2016',2.5 ),
('08-10-2016',1 ),
('08-11-2016',1 )) tc (Dates, Value)),
intr
AS (SELECT Dates,
Value,
Sum(Iif(pVal = Value, 0, 1)) OVER(ORDER BY Dates) AS [Counter]
FROM data)
SELECT Min(Dates) AS Dates_from,
Max(Dates) AS Dates_to,
Value
FROM intr
GROUP BY [Counter],
Value
The cumulative sum/lag approach is one method. In this case, a simpler method is:
select min(date) as date_from, max(date) as date_to, value
from (select t.*,
dateadd(day, - row_number() over (partition by value order by date),date) as grp
from t
) t
group by value, grp;
This uses the observation that the dates are consecutive with no gaps. Hence, subtracting a sequence from the date will yield a constant -- when the values are the same.
Here is an example:
DECLARE #T TABLE (
[Date] DATE,
[Value] DECIMAL(9,2)
)
INSERT #T VALUES
( '08-01-2016', 1 ),
( '08-02-2016', 1 ),
( '08-03-2016', 1 ),
( '08-04-2016', 1 ),
( '08-05-2016', 1 ),
( '08-06-2016', 2 ),
( '08-07-2016', 2 ),
( '08-08-2016', 2 ),
( '08-09-2016', 2.5 ),
( '08-10-2016', 1 ),
( '08-11-2016', 1 )
SELECT * FROM #T
SELECT A.[Date] StartDate, B.[Date] EndDate, A.[Value] FROM (
SELECT A.*, ROW_NUMBER() OVER (ORDER BY A.[Date], A.[Value]) O FROM #T A
LEFT JOIN #T B ON B.[Value] = A.[Value] AND B.[Date] = DATEADD(d, -1, A.[Date])
WHERE B.[Date] IS NULL
) A
JOIN (
SELECT A.*, ROW_NUMBER() OVER (ORDER BY A.[Date], A.[Value]) O FROM #T A
LEFT JOIN #T B ON B.[Value] = A.[Value] AND B.[Date] = DATEADD(d, 1, A.[Date])
WHERE B.[Date] IS NULL
) B ON B.O = A.O
Prdp's solution is great but just in case if anyone is still using SQL Server 2008 where LAG() and The Parallel Data Warehouse (PDW) features are not available here is an alternative:
SAMPLE DATA:
IF OBJECT_ID('tempdb..#Temp') IS NOT NULL
DROP TABLE #Temp;
CREATE TABLE #Temp([Dates] DATE
, [Value] FLOAT);
INSERT INTO #Temp([Dates]
, [Value])
VALUES
('08-01-2016'
, 1),
('08-02-2016'
, 1),
('08-03-2016'
, 1),
('08-04-2016'
, 1),
('08-05-2016'
, 1),
('08-06-2016'
, 2),
('08-07-2016'
, 2),
('08-08-2016'
, 2),
('08-09-2016'
, 2.5),
('08-10-2016'
, 1),
('08-11-2016'
, 1);
QUERY:
;WITH Seq
AS (SELECT SeqNo = ROW_NUMBER() OVER(ORDER BY [Dates]
, [Value])
, t.Dates
, t.[Value]
FROM #Temp t)
SELECT StartDate = MIN([Dates])
, EndDate = MAX([Dates])
, [Value]
FROM
(SELECT [Value]
, [Dates]
, SeqNo
, rn = SeqNo - ROW_NUMBER() OVER(PARTITION BY [Value] ORDER BY SeqNo)
FROM Seq s) a
GROUP BY [Value]
, rn
ORDER BY StartDate;
RESULTS:

SQL Server 2008 calculating data difference when we have only one date column

I have a date column Order_date and I am looking for ways to calculate the date difference between customer last order date and his recent previous ( previous form last) order_date ....
Example
Customer : 1, 2 , 1 , 1
Order_date: 01/02/2007, 02/01/2015, 06/02/2014, 04/02/2015
As you can see customer # 1 has three orders.
I want to know the date difference between his recent order date (04/02/2015) and his recent previous (06/02/2014).
For SQL Server 2012 & 2014 you could use LAG with a DATEDIFF to see the number of days between them.
For older versions, a CTE would probably be your best bet:
;WITH CTE AS
(
SELECT CustomerID,
Order_Date,
rn = ROW_NUMBER() OVER (PARTITION BY CustomerID ORDER BY Order_Date DESC)
)
SELECT c1.CustomerID,
DATEDIFF(d, c1.Order_Date, c2.Order_Date)
FROM CTE c1
INNER JOIN CTE c2 ON c2.rn = c1.rn + 1
In SQL Server 2012+, you can use lag() to get the difference between any two dates:
select t.*,
datediff(day, lag(order_date) over (partition by customer order by order_date),
order_date) as days_dff
from table t;
If you have an older version, you can do something similar with correlated subqueries or outer apply.
EDIT:
If you just want the difference between the two most recent dates, use conditional aggregation instead:
select customer,
datediff(day, max(case when seqnum = 2 then order_date end),
max(case when seqnum = 1 then order_date end)
) as MostRecentDiff
from (select t.*,
row_number() over (partition by customer order by order_date desc) as seqnum
from table t
) t
group by customer;
If you're using SQL Server 2008 or later, you can try CROSS APPLY.
SELECT [customers].[customer_id], DATEDIFF(DAY, MIN([recent_orders].[order_date]), MAX([recent_orders].[order_date])) AS [elapsed]
FROM [customers]
CROSS APPLY (
SELECT TOP 2 [order_date]
FROM [orders]
WHERE ([orders].[customer_id] = [customers].[customer_id])
) [recent_orders]
GROUP BY [customers].[customer_id]
SELECT DATEDIFF(DAY, Y.PrevLastOrderDate, Y.LastOrderDate) AS PreviousDays
FROM
(
SELECT X.LastOrderDate
, (SELECT MAX(OrderDate) FROM dbo.Orders SO WHERE SO.CustomerID=1 AND SO.OrderDate < X.LastOrderDate) AS PrevLastOrderDate
FROM
(
select MAX(OrderDate) AS LastOrderDate
FROM dbo.Orders O
WHERE O.CustomerID=1
)X
)Y
drop table #Invoices
create table #Invoices ( OrderId int , OrderDate datetime )
insert into #Invoices (OrderId , OrderDate )
select 101, '01/01/2001' UNION ALL Select 202, '02/02/2002' UNION ALL Select 303, '03/03/2003'
UNION ALL Select 808, '08/08/2008' UNION ALL Select 909, '09/09/2009'
;
WITH
MyCTE /* http://technet.microsoft.com/en-us/library/ms175972.aspx */
( OrderId,OrderDate,ROWID) AS
(
SELECT
OrderId,OrderDate
, ROW_NUMBER() OVER ( ORDER BY OrderDate ) as ROWID
FROM
#Invoices inv
)
SELECT
OrderId,OrderDate
,(Select Max(OrderDate) from MyCTE innerAlias where innerAlias.ROWID = (outerAlias.ROWID-1) ) as PreviousOrderDate
,
[MyDiff] =
CASE
WHEN (Select Max(OrderDate) from MyCTE innerAlias where innerAlias.ROWID = (outerAlias.ROWID-1) ) iS NULL then 0
ELSE DATEDIFF (mm, OrderDate , (Select Max(OrderDate) from MyCTE innerAlias where innerAlias.ROWID = (outerAlias.ROWID-1) ) )
END
, ROWIDMINUSONE = (ROWID-1)
, ROWID as ROWID_SHOWN_FOR_KICKS , OrderDate as OrderDateASecondTimeForConvenience
FROM
MyCTE outerAlias
ORDER BY outerAlias.OrderDate Desc , OrderId