New column which indicate min date from group of value - sql

I would like to add new column which will be indicating min value of subgroup.
Id ShopId OrderDate
12232018 12229018 2011-01-01 00:00:00.000
12232018 12229018 2012-01-01 00:00:00.000
12232018 12394018 2012-02-02 00:00:00.000
12232018 11386005 2012-03-01 00:00:00.000
12232018 14347023 2012-04-02 00:00:00.000
12232018 14026026 2014-03-16 00:00:00.000
Here is the result I want to get:
NewCol Id ShopId OrderDate
1 12232018 12229018 2011-01-01 00:00:00.000
1 12232018 12229018 2012-01-01 00:00:00.000
0 12232018 12394018 2012-02-02 00:00:00.000
0 12232018 11386005 2012-03-01 00:00:00.000
0 12232018 14347023 2012-04-02 00:00:00.000
0 12232018 14026026 2014-03-16 00:00:00.000
Because ShopId have min OrderDate for Id I would like to assign '1' to this ShopId.

You can use min with windowing function to get this as below:
select NewCol = Case when orderdate = min(orderdate) over() then 1 else 0 end,*
from yourtable
--Probably you might require to add Partition by Id or shopId depends on requirement

Try this:
SELECT Id, ShopId, OrderDate,
CASE
WHEN MIN(OrderDate) OVER (PARTITION BY Id, ShopId) =
MIN(OrderDate) OVER (PARTITION BY Id) THEN 1
ELSE 0
END AS NewCol
FROM mytable
The query uses windowed version of MAX in order to compare the minimum-per-Id OrderDate to the minimum-per- (Id, ShopId) date. If these two values are the same, then we mark the corresponding (Id, ShopId) partition with 1.
Demo here

Less elegant than the others, but is ANSI
select MyTable.*, case when a1.mindate = orderdate then 1 else 0 end as NewCol
from MyTable
inner join
(
select id, min(orderdate) as mindate
from Mytable
group by id
) a1
on a1.id = MyTable.id

Use min with orderdate on ShopId and use that in the Case When statement like this:-
Select case when (a.OrderDate=b.min_order_dt) then 1 else 0 end as NewCol, a.*
from
your_table_name a
inner join
(
SELECT ShopId, min(OrderDate) as min_order_dt
from
your_table_name
group by shop_id
) b
on a.ShopId=b.ShopId;

Try this
select case when t2.ShopId is null then 0 else 1 end as newcol,t1.id,
t1.ShopiId,t1.OrderDate
from table as t1 left join
(
select ShopId,min(OrderDate) as OrderDate from table
group by ShopId
) as t2 on t1.ShopId=t2.ShopId and t1.OrderDate=t2.OrderDate

Related

sql date difference with multiple variables

I'm trying to get the number of days difference in dates between the effdate status 0 that follows the most recent status 1
the code below yields the following results
SELECT * FROM
(SELECT FILEKEY, STATUS, EFFDATE FROM ASTATUSHIST
UNION
SELECT FILEKEY, ASTATUS, ASTATUSEFFDATE FROM USERS ) A
ORDER BY 1, 3 DESC
130 0 2019-10-25 00:00:00.000
130 0 2017-03-01 00:00:00.000
130 0 2017-01-01 00:00:00.000
130 1 2005-02-01 00:00:00.000
130 0 2001-03-03 00:00:00.000
130 0 2000-01-30 00:00:00.000
130 0 2000-01-01 00:00:00.000
this code combines 2 tables to get the complete history for a given user.
Ideally I could produce something that looks like this:
130 4352
or
125 null
where the null is filekey without a status 1 or a filekey with a status 1 but without a following status 0
Thanks
In all supported versions of SQL Server, you can use window functions:
with t as (
<your query here>
)
select t.*,
datediff(day, date, next_date) as days_diff
from (select t.*,
row_number() over (partition by filekey, status order by date desc) as seqnum,
lead(date) over (partition by filekey order by date) as next_date
from t
) t
where seqnum = 1;

Get max date for each from either of 2 columns

I have a table like below
AID BID CDate
-----------------------------------------------------
1 2 2018-11-01 00:00:00.000
8 1 2018-11-08 00:00:00.000
1 3 2018-11-09 00:00:00.000
7 1 2018-11-15 00:00:00.000
6 1 2018-12-24 00:00:00.000
2 5 2018-11-02 00:00:00.000
2 7 2018-12-15 00:00:00.000
And I am trying to get a result set as follows
ID MaxDate
-------------------
1 2018-12-24 00:00:00.000
2 2018-12-15 00:00:00.000
Each value in the id columns(AID,BID) should return the max of CDate .
ex: in the case of 1, its max CDate is 2018-12-24 00:00:00.000 (here 1 appears under BID)
in the case of 2 , max date is 2018-12-15 00:00:00.000 . (here 2 is under AID)
I tried the following.
1.
select
g.AID,g.BID,
max(g.CDate) as 'LastDate'
from dbo.TT g
inner join
(select AID,BID,max(CDate) as maxdate
from dbo.TT
group by AID,BID)a
on (a.AID=g.AID or a.BID=g.BID)
and a.maxdate=g.CDate
group by g.AID,g.BID
and 2.
SELECT
AID,
CDate
FROM (
SELECT
*,
max_date = MAX(CDate) OVER (PARTITION BY [AID])
FROM dbo.TT
) AS s
WHERE CDate= max_date
Please suggest a 3rd solution.
You can assemble the data in a table expression first, and the compute the max for each value is simple. For example:
select
id, max(cdate)
from (
select aid as id, cdate from t
union all
select bid, cdate from t
) x
group by id
You seem to only care about values that are in both columns. If this interpretation is correct, then:
select id, max(cdate)
from ((select aid as id, cdate, 1 as is_a, 0 as is_b
from t
) union all
(select bid as id, cdate, 1 as is_a, 0 as is_b
from t
)
) ab
group by id
having max(is_a) = 1 and max(is_b) = 1;

How can I get distinct data from one col

I need to get member personal data for all our members whose subscriptions have lapsed i.e. have a subscription end date before 31/03/2020, however I want to show one member record only (distinct by membership number) ideally the most recent one
I've tried a ROW_NUMBER() solution SQL - Distinct One Col, Select Multiple other? and a cross apply solution sql distinct, getting 2 columns but I can't get it to work.
SELECT membershipnumber AS Id,
subscription.enddate
FROM [dbo].[userprofile]
INNER JOIN dbo.subscription
ON userprofile.id = subscription.userprofileid
INNER JOIN dbo.subscriptiontype
ON subscriptiontype.id = subscription.subscriptiontypeid
Output is
Id Enddate
1 2006-04-01 00:00:00.000
1 2001-04-01 00:00:00.000
1 1999-04-01 00:00:00.000
1 1998-04-01 00:00:00.000
1 2008-04-01 00:00:00.000
1 2007-04-01 00:00:00.000
1 2011-04-01 00:00:00.000
1 2005-04-01 00:00:00.000
1 2000-04-01 00:00:00.000
1 1997-04-01 00:00:00.000
2 1999-04-01 00:00:00.000
2 2012-04-01 00:00:00.000
2 2004-04-01 00:00:00.000
2 2001-04-01 00:00:00.000
2 2018-04-01 00:00:00.000
2 2009-04-01 00:00:00.000
2 2005-04-01 00:00:00.000
2 1997-04-01 00:00:00.000
Desired output
Id Enddate
1 2011-04-01 00:00:00.000
2 2018-04-01 00:00:00.000
Solved sql answer
;WITH cte
AS (SELECT membershipnumber AS Id,
subscription.enddate,
Row_number()
OVER (
partition BY membershipnumber
ORDER BY subscription.enddate DESC) AS rownumber
FROM [dbo].[userprofile]
INNER JOIN dbo.subscription
ON userprofile.id = subscription.userprofileid
INNER JOIN dbo.subscriptiontype
ON subscriptiontype.id = subscription.subscriptiontypeid
)
SELECT *
FROM cte
WHERE rownumber = 1
https://stackoverflow.com/a/6841644/5859743
Not sure if I got your question right.
but you can use DISTINCT in the SELECT, that would show only one record for each member.
SELECT DISTINCT Membershipnumber as Id
,'P' as PartyType
,'A' as Status
,case
when Name = 'Standard Membership paid annually.' and EndDate > '2020-03-31' then 'Member'
when Name = 'Lapsed subscription renewal' and EndDate > '2020-03-31' then 'Member'
when Name = '3 Year Subscription (members outside of UK and Ireland, Jersey, Guernsey and the Channel Islands)' and EndDate > '2020-03-31' then 'Overseas member'
when Name = '1 Year Subscription (members outside of UK and Ireland, Jersey, Guernsey and the Channel Islands).' and EndDate > '2020-03-31' then 'Overseas member'
when Name = 'Lapsed subscription renewal' and EndDate > '2020-03-31' then 'Member'
when Name = 'Lifetime membership' then 'Lifetime member'
when Name = 'Retired membership paid annually' and EndDate > '2020-03-31' then 'Retired member'
else 'Non member'
end As MemberType
,Title as NamePrefix
,FirstName as FirstName
,Surname as LastName
,DateOfBirth as BirthDate
,'Home' as AddressPurpose
,'Default' as CommunicationReasons
,AddressLine1
,AddressLine2
,AddressLine3
,Addressline4 as CityName
,'' as CountrySubEntityName
,Country as CountryCode
,'' as CountryName
,Postcode as PostalCode
,EmailAddress as Email
FROM [dbo].[UserProfile]
inner join dbo.Subscription on
UserProfile.Id = Subscription.UserProfileId
inner join dbo.SubscriptionType on
SubscriptionType.id = Subscription.SubscriptionTypeId```
If you are getting as above mentioned output. Then from that, your desired output will easily get using distinct.
; with cte as (
----- query which gives you above mentioned output
)
select distinct id, max(Enddate) as Enddate from cte
I suspect you want something like this:
select *
from (select . . ., -- all the columns you want
row_number() over (partition by Membershipnumber as Id order by s.Enddate) as seqnum
from [dbo].[UserProfile] up inner join
dbo.Subscription s
on up.Id = s.UserProfileId inner join
dbo.SubscriptionType st
on st.id = s.SubscriptionTypeId
) x
where seqnum = 1;

Create new column based on some conditions in SQL

I have the following table. Let's call it orders. I would like to add a new column to this existing table which calculates the days apart from the first order date by the customerid. If there are 0 days apart from the minimum sold date, then it should be 0.
From this
customerid orderdate
1 1/21/2018
1 1/21/2018
1 2/21/2018
1 5/22/2018
2 3/22/2018
3 4/5/2018
3 4/5/2018
to this
customerid orderdate daysapart
1 1/21/2018 0
1 1/21/2018 0
1 2/21/2018 30
1 2/21/2018 123
2 3/22/2018 0
3 4/5/2018 0
3 4/5/2018 0
Using a Windowed Aggregate:
select customerid, orderdate,
orderdate - min(orderdate) over (partition by customerid) as daysapart
from mytab
Here is one approach, using a correlated subquery:
SELECT
t1.customerid,
t1.orderdate,
t1.orderdate - (SELECT MIN(t2.orderdate)
FROM your_table t2
WHERE t1.customerid = t2.customerid) daysapart
FROM your_table t1;

Self join with aggregate function or some better way

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