Related
I am not sure whether this can be done. I want to group the data based on company with consecutive date. Below is the desired result I am attempting in SQL.
EmpNo
Company
StartDt
EndDt
Desired Result
0003
C01
2021-01-01 00:00:00.000
2021-01-10 00:00:00.000
1
0003
C02
2021-01-11 00:00:00.000
2021-01-15 00:00:00.000
2
0003
C02
2021-01-16 00:00:00.000
2021-01-20 00:00:00.000
2
0003
C01
2021-01-21 00:00:00.000
2021-01-31 00:00:00.000
3
You can use lag() to detect when a company changes and then a cumulative sum:
select t.*,
sum(case when company = prev_company then 0 else 1 end) over (partition by empno order by startdt) as desired_result
from (select t.*,
lag(company) over (partition by empno order by startdt) as prev_company
from t
) t
Something like:
SELECT * FROM `<your-table>`
GROUP BY `Company`
ORDER BY `StartDt` DESC
I have the following data:
Id Week1 Week2 Date
-------------------------------------------------------------------------------
C0935336-B424-E911-8117-005056A82772 201906 201904 2019-02-02 00:00:00.000
18D809B1-8725-E911-8117-005056A82772 201907 201904 2019-02-09 00:00:00.000
C95855A0-9428-E911-8117-005056A82772 201908 201905 2019-02-16 00:00:00.000
5ABE80F6-2531-E911-8117-005056A82772 201909 201905 2019-02-23 00:00:00.000
6B520DE4-9445-E911-8118-005056A82772 201910 201906 2019-03-02 00:00:00.000
ADD0A8D0-EE2E-E911-8117-005056A82772 201911 201906 2019-03-09 00:00:00.000
As you can see, Week2 as duplicate entries and I need to return the first row of each pair of rows returned so that I end up with something similar to this.
Id Week1 Week2 Date
-------------------------------------------------------------------------------
C0935336-B424-E911-8117-005056A82772 201906 201904 2019-02-02 00:00:00.000
C95855A0-9428-E911-8117-005056A82772 201908 201905 2019-02-16 00:00:00.000
6B520DE4-9445-E911-8118-005056A82772 201910 201906 2019-03-02 00:00:00.000
I'm using the following in SQL:
SELECT DISTINCT
ROW_NUMBER() OVER (PARTITION BY Weeks.Week2 ORDER BY Weeks.Week2) AS Row#,
Data.Id, Weeks.Week1, Weeks.Week2, Weeks.Date
FROM
Data
INNER JOIN
Weeks ON Data.WeekN = Weeks.Week1
INNER JOIN
Users ON Data.UserId = Users.UserId
WHERE
Weeks.Week2 IN (SELECT DISTINCT Weeks.Week2
FROM Data
INNER JOIN Weeks ON Data.Week = Weeks.Week1
INNER JOIN Users ON Data.UserId = Users.UserId
WHERE Data.UserId = 1234 AND Weeks.Week1 >= 201907)
ORDER BY
Weeks.Week2
Which introduces a row number for each set or rows returned:
Row# Id Week1 Week2 Date
-----------------------------------------------------------------------------------
1 C0935336-B424-E911-8117-005056A82772 201906 201904 2019-02-02 00:00:00.000
2 18D809B1-8725-E911-8117-005056A82772 201907 201904 2019-02-09 00:00:00.000
1 C95855A0-9428-E911-8117-005056A82772 201908 201905 2019-02-16 00:00:00.000
2 5ABE80F6-2531-E911-8117-005056A82772 201909 201905 2019-02-23 00:00:00.000
1 6B520DE4-9445-E911-8118-005056A82772 201910 201906 2019-03-02 00:00:00.000
2 ADD0A8D0-EE2E-E911-8117-005056A82772 201911 201906 2019-03-09 00:00:00.000
My question is how do I select all the rows where the Row# is 1 ?
As #stickybit mentioned, you can use:
SELECT
Id
, Week1
, Week2
, Date
FROM
(
SELECT
ROW_NUMBER() OVER (PARTITION BY Weeks.Week2 ORDER BY Weeks.Week2) AS Row#
, Data.Id
, Weeks.Week1
, Weeks.Week2
, Weeks.Date
FROM
Data
INNER JOIN Weeks ON Data.WeekN = Weeks.Week1
INNER JOIN Users ON Data.UserId = Users.UserId
WHERE Weeks.Week2 IN
(
SELECT DISTINCT Weeks.Week2
FROM
Data
INNER JOIN Weeks ON Data.Week = Weeks.Week1
INNER JOIN Users ON Data.UserId = Users.UserId
WHERE
Data.UserId = 1234
AND Weeks.Week1 >= 201907
)
) Q
WHERE Row# = 1
You don't need to worry about the ORDER BY, as the ROW_NUMBER() function is taking care of that for you in its OVER() clause.
You also don't need DISTINCT, as the ROW_NUMBER() function will prevent it from having any effect anyway.
I don't even know what's a good title for this question.
But I'm having a table:
create table trans
(
[transid] INT IDENTITY (1, 1) NOT NULL,
[customerid] int not null,
[points] decimal(10,2) not null,
[date] datetime not null
)
and records:
--cus1
INSERT INTO trans ( customerid , points , date )
VALUES ( 1, 10, '2016-01-01' ) , ( 1, 20, '2017-02-01' ) , ( 1, 22, '2017-03-01' ) ,
( 1, 24, '2018-02-01' ) , ( 1, 50, '2018-02-25' ) , ( 2, 44, '2016-02-01' ) ,
( 2, 20, '2017-02-01' ) , ( 2, 32, '2017-03-01' ) , ( 2, 15, '2018-02-01' ) ,
( 2, 10, '2018-02-25' ) , ( 3, 10, '2018-02-25' ) , ( 4, 44, '2015-02-01' ) ,
( 4, 20, '2015-03-01' ) , ( 4, 32, '2016-04-01' ) , ( 4, 15, '2016-05-01' ) ,
( 4, 10, '2017-02-25' ) , ( 4, 10, '2018-02-27' ) ,( 4, 20, '2018-02-28' ) ,
( 5, 44, '2015-02-01' ) , ( 5, 20, '2015-03-01' ) , ( 5, 32, '2016-04-01' ) ,
( 5, 15, '2016-05-01' ) ,( 5, 10, '2017-02-25' );
-- selecting the data
select * from trans
Produces:
transid customerid points date
----------- ----------- --------------------------------------- -----------------------
1 1 10.00 2016-01-01 00:00:00.000
2 1 20.00 2017-02-01 00:00:00.000
3 1 22.00 2017-03-01 00:00:00.000
4 1 24.00 2018-02-01 00:00:00.000
5 1 50.00 2018-02-25 00:00:00.000
6 2 44.00 2016-02-01 00:00:00.000
7 2 20.00 2017-02-01 00:00:00.000
8 2 32.00 2017-03-01 00:00:00.000
9 2 15.00 2018-02-01 00:00:00.000
10 2 10.00 2018-02-25 00:00:00.000
11 3 10.00 2018-02-25 00:00:00.000
12 4 44.00 2015-02-01 00:00:00.000
13 4 20.00 2015-03-01 00:00:00.000
14 4 32.00 2016-04-01 00:00:00.000
15 4 15.00 2016-05-01 00:00:00.000
16 4 10.00 2017-02-25 00:00:00.000
17 4 10.00 2018-02-27 00:00:00.000
18 4 20.00 2018-02-28 00:00:00.000
19 5 44.00 2015-02-01 00:00:00.000
20 5 20.00 2015-03-01 00:00:00.000
21 5 32.00 2016-04-01 00:00:00.000
22 5 15.00 2016-05-01 00:00:00.000
23 5 10.00 2017-02-25 00:00:00.000
I'm trying to group all the customerid and sum their points. But here's the catch, If the trans is not active for 1 year(the next tran is 1 year and above), the points will be expired.
For this case:
Points for each customers should be:
Customer1 20+22+24+50
Customer2 20+32+15+10
Customer3 10
Customer4 10+20
Customer5 0
Here's what I have so far:
select
t1.transid as transid1,
t1.customerid as customerid1,
t1.date as date1,
t1.points as points1,
t1.rank1 as rank1,
t2.transid as transid2,
t2.customerid as customerid2,
t2.points as points2,
isnull(t2.date,getUTCDate()) as date2,
isnull(t2.rank2,t1.rank1+1) as rank2,
cast(case when(t1.date > dateadd(year,-1,isnull(t2.date,getUTCDate()))) Then 0 ELSE 1 END as bit) as ShouldExpire
from
(
select transid,CustomerID,Date,points,
RANK() OVER(PARTITION BY CustomerID ORDER BY date ASC) AS RANK1
from trans
)t1
left join
(
select transid,CustomerID,Date,points,
RANK() OVER(PARTITION BY CustomerID ORDER BY date ASC) AS RANK2
from trans
)t2 on t1.RANK1=t2.RANK2-1
and t1.customerid=t2.customerid
which gives
from the above table,how do I check for ShouldExpire field having max(rank1) for customer, if it's 1, then totalpoints will be 0, otherwise,sum all the consecutive 0's until there are no more records or a 1 is met?
Or is there a better approach to this problem?
The following query uses LEAD to get the date of the next record withing the same CustomerID slice:
;WITH CTE AS (
SELECT transid, CustomerID, [Date], points,
LEAD([Date]) OVER (PARTITION BY CustomerID
ORDER BY date ASC) AS nextDate,
CASE
WHEN [date] > DATEADD(YEAR,
-1,
-- same LEAD() here as above
ISNULL(LEAD([Date]) OVER (PARTITION BY CustomerID
ORDER BY date ASC),
getUTCDate()))
THEN 0
ELSE 1
END AS ShouldExpire
FROM trans
)
SELECT transid, CustomerID, [Date], points, nextDate, ShouldExpire
FROM CTE
ORDER BY CustomerID, [Date]
Output:
transid CustomerID Date points nextDate ShouldExpire
-------------------------------------------------------------
1 1 2016-01-01 10.00 2017-02-01 1 <-- last exp. for 1
2 1 2017-02-01 20.00 2017-03-01 0
3 1 2017-03-01 22.00 2018-02-01 0
4 1 2018-02-01 24.00 2018-02-25 0
5 1 2018-02-25 50.00 NULL 0
6 2 2016-02-01 44.00 2017-02-01 1 <-- last exp. for 2
7 2 2017-02-01 20.00 2017-03-01 0
8 2 2017-03-01 32.00 2018-02-01 0
9 2 2018-02-01 15.00 2018-02-25 0
10 2 2018-02-25 10.00 NULL 0
11 3 2018-02-25 10.00 NULL 0 <-- no exp. for 3
12 4 2015-02-01 44.00 2015-03-01 0
13 4 2015-03-01 20.00 2016-04-01 1
14 4 2016-04-01 32.00 2016-05-01 0
15 4 2016-05-01 15.00 2017-02-25 0
16 4 2017-02-25 10.00 2018-02-27 1 <-- last exp. for 4
17 4 2018-02-27 10.00 2018-02-28 0
18 4 2018-02-28 20.00 NULL 0
19 5 2015-02-01 44.00 2015-03-01 0
20 5 2015-03-01 20.00 2016-04-01 1
21 5 2016-04-01 32.00 2016-05-01 0
22 5 2016-05-01 15.00 2017-02-25 0
23 5 2017-02-25 10.00 NULL 1 <-- last exp. for 5
Now, you seem to want to calculate the sum of points after the last expiration.
Using the above CTE as a basis you can achieve the required result with:
;WITH CTE AS (
... above query here ...
)
SELECT CustomerID,
SUM(CASE WHEN rnk = 0 THEN points ELSE 0 END) AS sumOfPoints
FROM (
SELECT transid, CustomerID, [Date], points, nextDate, ShouldExpire,
SUM(ShouldExpire) OVER (PARTITION BY CustomerID ORDER BY [Date] DESC) AS rnk
FROM CTE
) AS t
GROUP BY CustomerID
Output:
CustomerID sumOfPoints
-----------------------
1 116.00
2 77.00
3 10.00
4 30.00
5 0.00
Demo here
The tricky part here is to dump all points when they expire, and start accumulating them again. I assumed that if there was only one transaction that we don't expire the points until there's a new transaction, even if that first transaction was over a year ago now?
I also get a different answer for customer #5, as they do appear to have a "transaction chain" that hasn't expired?
Here's my query:
WITH ordered AS (
SELECT
*,
ROW_NUMBER() OVER (PARTITION BY customerid ORDER BY [date]) AS order_id
FROM
trans),
max_transid AS (
SELECT
customerid,
MAX(transid) AS max_transid
FROM
trans
GROUP BY
customerid),
not_expired AS (
SELECT
t1.customerid,
t1.points,
t1.[date] AS t1_date,
CASE
WHEN m.customerid IS NOT NULL THEN GETDATE()
ELSE t2.[date]
END AS t2_date
FROM
ordered t1
LEFT JOIN ordered t2 ON t2.customerid = t1.customerid AND t1.transid != t2.transid AND t2.order_id = t1.order_id + 1 AND t1.[date] > DATEADD(YEAR, -1, t2.[date])
LEFT JOIN max_transid m ON m.customerid = t1.customerid AND m.max_transid = t1.transid
),
max_not_expired AS (
SELECT
customerid,
MAX(t1_date) AS max_expired
FROM
not_expired
WHERE
t2_date IS NULL
GROUP BY
customerid)
SELECT
n.customerid,
SUM(n.points) AS points
FROM
not_expired n
LEFT JOIN max_not_expired m ON m.customerid = n.customerid
WHERE
ISNULL(m.max_expired, '19000101') < n.t1_date
GROUP BY
n.customerid;
It could be refactored to be simpler, but I wanted to show the steps to get to the final answer:
customerid points
1 116.00
2 77.00
3 10.00
4 30.00
5 57.00
can you try this:
SELECT customerid,
Sum(t1.points)
FROM trans t1
WHERE NOT EXISTS (SELECT 1
FROM trans t2
WHERE Datediff(year, t1.date, t2.date) >= 1)
GROUP BY t1.customerid
Hope it helps!
try this:
select customerid,Sum(points)
from trans where Datediff(year, date, GETDATE()) < 1
group by customerid
output:
customerid Points
1 - 74.00
2 - 25.00
3 - 10.00
4 - 30.00
I have this query that gives me a given date for each of the past 15 years. When my starting date is February 29 it does not return the 29 for year 2012, 2008 and 2004. How can I have this query to return the 29 for those years?
DECLARE #TempDate1 TABLE (Entry_Date Date)
INSERT INTO #TempDate1 values ('2016-02-29')
;WITH
a AS(SELECT DATEADD(yy,-1,Entry_Date) d, DATEADD(yy,-1,Entry_Date) d2,0 i
FROM #TempDate1
UNION all
SELECT DATEADD(yy,-1,d),DATEADD(yy,-1,d2),i+1 FROM a WHERE i<14),
b AS(SELECT d,d2, DATEDIFF(dd,0,d)%7 dd,i FROM a)
SELECT
d AS Entry_Date
FROM b
It returns this:
Entry_Date
2015-02-28
2014-02-28
2013-02-28
2012-02-28
2011-02-28
2010-02-28
2009-02-28
2008-02-28
2007-02-28
2006-02-28
2005-02-28
2004-02-28
2003-02-28
2002-02-28
2001-02-28
While I would like to have this:
Entry_Date
2015-02-28
2014-02-28
2013-02-28
2012-02-29
2011-02-28
2010-02-28
2009-02-28
2008-02-29
2007-02-28
2006-02-28
2005-02-28
2004-02-29
2003-02-28
2002-02-28
2001-02-28
Perhaps DateAdd in concert with an ad-hoc tally table
Example
Declare #YourTable Table ([Entry_Date] date)
Insert Into #YourTable Values
('2016-02-29')
,('2015-07-22')
Select YearNr = N
,Anniv = dateadd(YEAR,N*-1,Entry_Date)
From #YourTable A
Cross Apply (
Select Top 15 N=Row_Number() Over (Order By (Select NULL)) From master..spt_values n1
) B
Returns
Simply by using EOMONTH function (SQL Server 2012 and above):
DECLARE #TempDate1 TABLE (Entry_Date Date)
INSERT INTO #TempDate1 values ('2016-02-29')
;WITH
a AS(SELECT DATEADD(yy,-1,Entry_Date) d, DATEADD(yy,-1,Entry_Date) d2,0 i
FROM #TempDate1
UNION all
SELECT DATEADD(yy,-1,d),DATEADD(yy,-1,d2),i+1 FROM a WHERE i<14),
b AS(SELECT d,d2, DATEDIFF(dd,0,d)%7 dd,i FROM a)
SELECT EOMONTH(d) AS Entry_Date
FROM b;
Rextester Demo
Rewrite tour query like this... Not only will handle leap years without jumping through hoops, it's orders of magnitude more efficient than what you currently have.
DECLARE #BaseDate DATE = '2016-02-29';
SELECT
Entry_Date = DATEADD(YEAR, t.n, #BaseDate)
FROM
(VALUES (-1),(-2),(-3),(-4),(-5),
(-6),(-7),(-8),(-9),(-10),
(-11),(-12),(-13),(-14),(-15) ) t (n);
Results...
Entry_Date
----------
2015-02-28
2014-02-28
2013-02-28
2012-02-29
2011-02-28
2010-02-28
2009-02-28
2008-02-29
2007-02-28
2006-02-28
2005-02-28
2004-02-29
2003-02-28
2002-02-28
2001-02-28
EDIT: Same functionality when used with a table of dates (I stole John's table)
DECLARE #YourTable TABLE (id INT, Entry_Date DATE);
INSERT INTO #YourTable VALUES (1, '2016-02-29'), (2, '2015-07-22');
SELECT
yt.id,
Entry_Date = DATEADD(YEAR, t.n, yt.Entry_Date)
FROM
#YourTable yt
CROSS APPLY (VALUES (-1),(-2),(-3),(-4),(-5),
(-6),(-7),(-8),(-9),(-10),
(-11),(-12),(-13),(-14),(-15) ) t (n);
GO
Results...
id Entry_Date
----------- ----------
1 2015-02-28
1 2014-02-28
1 2013-02-28
1 2012-02-29
1 2011-02-28
1 2010-02-28
1 2009-02-28
1 2008-02-29
1 2007-02-28
1 2006-02-28
1 2005-02-28
1 2004-02-29
1 2003-02-28
1 2002-02-28
1 2001-02-28
2 2014-07-22
2 2013-07-22
2 2012-07-22
2 2011-07-22
2 2010-07-22
2 2009-07-22
2 2008-07-22
2 2007-07-22
2 2006-07-22
2 2005-07-22
2 2004-07-22
2 2003-07-22
2 2002-07-22
2 2001-07-22
2 2000-07-22
I am using SQL-Server and have a table of my Purchase orders (stock). But stuck in a query while I was trying to get my All Available stock with its Latest Cost Price and Latest Selling Price.
I made a query it run successfully, but i need some better and optimized way to do this, because it will get slow when table have n number of records.
Query Sample:
SELECT
po.ProductID, sum(po.AvailableQty) as AvailableQty,
(select top 1 po2.CostPrice from Sales_PurchaseOrders po2 where po2.PurchasedAt=max(po.PurchasedAt)) as CostPrice,
(select top 1 po2.SellingPrice from Sales_PurchaseOrders po2 where po2.PurchasedAt=max(po.PurchasedAt)) as SellingPrice
FROM
Sales_PurchaseOrders po
INNER JOIN Sales_Products p on p.ProductID=po.ProductID
GROUP BY po.ProductID
Table Data:
PurchaseOrderID ProductID CostPrice SellingPrice AvailableQty PurchasedAt
--------------- ----------- --------------------------------------- --------------------------------------- --------------------------------------- -----------------------
1 1 90.000000 100.000000 2.000000 2016-07-28 00:00:00.000
2 1 33.580000 50.000000 0.000000 2016-06-28 00:00:00.000
3 2 200.000000 240.000000 15.000000 2016-07-30 00:00:00.000
4 1 50.000000 60.000000 0.000000 2016-08-02 00:00:00.000
5 1 50.000000 60.000000 1.000000 2016-08-03 00:00:00.000
6 1 100.000000 110.000000 6.000000 2016-08-04 00:00:00.000
7 1 25.000000 30.000000 3.000000 2016-08-03 00:00:00.000
8 1 20.000000 30.000000 0.000000 2016-07-30 00:00:00.000
1007 1 100.000000 200.000000 2.000000 2016-09-24 00:00:00.000
Query Result:
ProductID AvailableQty CostPrice SellingPrice
----------- --------------------------------------- --------------------------------------- ---------------------------------------
1 14.000000 100.000000 200.000000
2 15.000000 200.000000 240.000000
May be via some kind of aggregate function, or any other better optimized way to do this.
Thanks,
I think this does what you want:
SELECT po.ProductID, sum(po.AvailableQty) as AvailableQty,
MAX(last_CostPrice), MAX(last_SellingPrice)
FROM (SELECT po.*,
FIRST_VALUE(CostPrice) OVER (PARTITION BY ProductId ORDER BY PurchasedAt DESC) as last_CostPrice,
FIRST_VALUE(SellingPrice) OVER (PARTITION BY ProductId ORDER BY PurchasedAt DESC) as last_SellingPrice
FROM Sales_PurchaseOrders po
) po
GROUP BY po.ProductID;
Notes:
The table Sales_Products seems totally unnecessary for the query.
You probably want the most recent cost and selling price for the product, not for all products.
You can use FIRST_VALUE() in the subquery to get this information.
Dear Mehmood Try this.
;with wt_table
as
(
select ROW_NUMBER() over(partition by po.ProductID order by PurchasedAt desc) as Num,
AvailableQty=sum(po.AvailableQty) over(partition by po.ProductID),
po.ProductID,
po.CostPrice,
po.SellingPrice,
po.PurchasedAt
From #Sales_PurchaseOrders po)
select * from wt_table where Num=1
try this:
with Sales_PurchaseOrders(PurchaseOrderID,ProductID,CostPrice,SellingPrice,AvailableQty,PurchasedAt)AS(
select 1,1,90.000000,100.000000,2.000000,'2016-07-28 00:00:00.000' union all
select 2,1,33.580000,50.000000,0.000000,'2016-06-28 00:00:00.000' union all
select 3,2,200.000000,240.000000,15.000000,'2016-07-30 00:00:00.000' union all
select 4,1,50.000000,60.000000,0.000000,'2016-08-02 00:00:00.000' union all
select 5,1,50.000000,60.000000,1.000000,'2016-08-03 00:00:00.000' union all
select 6,1,100.000000,110.000000,6.000000,'2016-08-04 00:00:00.000' union all
select 7,1,25.000000,30.000000,3.000000,'2016-08-03 00:00:00.000' union all
select 8,1,20.000000,30.000000,0.000000,'2016-07-30 00:00:00.000' union all
select 1007,1,100.000000,200.000000,2.000000,'2016-09-24 00:00:00.000'
)
select * from (
SELECT
po.ProductID, sum(po.AvailableQty)over(partition by po.ProductID) as AvailableQty,CostPrice,SellingPrice,
row_number()over(partition by po.ProductID order by po.PurchasedAt desc) as seq
FROM Sales_PurchaseOrders po
) as t where t.seq=1
ProductID AvailableQty CostPrice SellingPrice seq
1 1 14,000000 100,000000 200,000000 1
2 2 15,000000 200,000000 240,000000 1