top 1 first_value() based on two criteria - sql-server

I'm still new at mssqlserver, i need to get the first order for the same customer made order in certain period based on the brand name and customer_id I used first_value() as sub query with top 1 however i got only the first value based on the brand regardless the customer id
what i got
Data
Customer_ID
Brand
first_order
2022-01-01
1649104
PH
2020-09-26
2022-01-01
1306498
PH
2020-09-26
2022-01-01
1290371
PH
2020-09-26
2022-01-01
456756
TB
2020-11-09
2022-01-01
1823713
TB
2020-11-09
2022-01-01
2178025
BK
2020-08-12
2022-01-01
216435
BK
2020-08-12
2022-01-01
19031
BK
2020-08-12
2022-01-01
438095
BK
2020-08-12
the code I used
SELECT Data , Brand , Customer_ID ,
(select top 1 first_value(H.Data) over (partition by Customer_ID , Brand order by H.Data ASC)
from ORDER H
where h.Customer_ID = Customer_ID and H.Brand = Brand
) as firts_order
from ORDER
LEFT OUTER JOIN ORDER_SOURCE SRC ON SRC.SRC_ID = ORDR_SOURCE
WHERE SRC_NAME IN ('SR 1' , 'SR 2') and DATE = '2022-01-01'

I've not used that first_value() window function before, but my bet is that you don't need a subquery. Instead:
SELECT Data,
Brand,
Customer_ID,
FIRST_VALUE(Data) OVER (PARTITION BY Customer_ID, Brand ORDER BY Data) as first_order
FROM ORDER
LEFT OUTER JOIN ORDER_SOURCE SRC ON SRC.SRC_ID = ORDR_SOURCE
WHERE SRC_NAME IN ('SR 1' , 'SR 2') AND DATE = '2022-01-01'
Alternatively you could use a correlated subquery INSTEAD. You don't need to combine this with a window function. Although, I wouldn't recommend it since the window function route would be quicker.
SELECT Data , Brand , Customer_ID ,
(
select top 1 H.data
from ORDER H
where H.Customer_ID = O.Customer_ID
and H.Brand = O.Brand
ORDER BY H.Data ASC
) as first_order
from ORDER O
LEFT OUTER JOIN ORDER_SOURCE SRC ON SRC.SRC_ID = ORDR_SOURCE
WHERE SRC_NAME IN ('SR 1' , 'SR 2') and DATE = '2022-01-01'

after working around I have found a more efficient solution for me using min() Instead of the first_value()
SELECT H.Data , H.Brand , H.Customer_ID , O.Data
from ORDER H
LEFT OUTER JOIN ORDER_SOURCE SRC ON SRC.SRC_ID = H.ORDR_SOURCE
LEFT OUTER JOIN (
select min(Data) AS Data, Brand, Customer_ID
from ORDER
LEFT OUTER JOIN ORDER_SOURCE ON SRC_ID = ORDR_SOURCE
WHERE SRC_NAME IN ('SR 1' , 'SR 2')
GROUP by Brand, Customer_ID )
AS O
ON H.Brand = O.Brand AND H.Customer_ID = O.Customer_ID
WHERE SRC_NAME IN ('SR 1' , 'SR 2') and DATE = '2022-01-01'

Related

How to join columns from the same table?

How to add extra column with time difference for the same SALESID and GO = SO and GO = ZW?
select SALESID, DATETIME AS Time, GO
FROM [Mer_PRD].[dbo].[TRACKINGTABLE]
WHERE GO IN('ZW', 'SO')
Result example:
****SALESID** | TIME SO | TIME ZW | DIFF**
ZS/0033428/2020 | 2020-07-16 08:37:00 | 2020-07-16 08:40:00 | 00:03:00
You could use a self join.
SELECT goSO.SALESID
, goSo.[Time] 'TIME SO'
, goZw.[Time] 'TIME ZW'
, cast(goZw.[Time] - goSo.[Time] as Time) Difference
FROM GOTRACKING goSo
INNER JOIN GOTRACKING goZw on goSo.SALESID = goZw.SALESID
WHERE goSo.[GO] = 'SO'
AND goZw.[GO] = 'ZW'
SQLFiddle
try this,
SELECT SALESID, Time FROM [Mer_PRD].[dbo].[TRACKINGTABLE]
;WITH cte AS (SELECT SALESID, Time, ROW_NUMBER()OVER(PARTITION BY SALESID ORDER BY Time) AS RN
FROM [Mer_PRD].[dbo].[TRACKINGTABLE])
SELECT a.SALESID, a.Time, b.Time AS Time2
FROM cte a
LEFT JOIN cte b
ON a.SALESID = b.SALESID
AND a.RN = b.RN -1
WHERE a.RN = 1

Nested SQL select statement in SQL Server with aggregate function

I have a query where first table Project will be fetched as a list. So every project row should have a unique ProjectID.
Now the second table Contract should get one row for each project. Although there are multiple rows for some projects in the Contract table. But I have to get only one row based on the select count condition I have applied in the below query.
Right now my code is returning two rows and I want only the second row not first row. Logic is basically only Approve 0 & 1 will be picked which cannot be more than one row together. But value 2 is multiple and destroying my result set. But for multiple projects of different ProjectId is should return multiple results.
SELECT
a.ProjectId
,a.Title
,a.CreationDate
,a.Status
,DATEDIFF(second, CAST(SWITCHOFFSET(SYSDATETIMEOFFSET(), '+05:30') AS DATETIME), BidEndDateTime) / 3600 As BidTimeLeft
,(SELECT COUNT(*) FROM Bidding b WHERE a.ProjectId = b.ProjectId) AS BidsCount
,(SELECT e.CompanyName FROM Bidding b inner join Partner e on b.PartnerId = e.PartnerId WHERE a.ProjectId = b.ProjectId and b.BidAccepted = 1) AS BidSelected
--,h.CompanyName
--Contact table column ApprovedbyCustomer will only have one 0 or 1 for same project, Partner can only create contract either for the first time or if it is rejected by Customer
,(SELECT COUNT(*) FROM Contract x WHERE a.ProjectId = x.ProjectId and x.ApprovedbyCustomer in (0,1) ) AS ContractCount
,g.ContractId
--,(SELECT c.ContractId FROM Contract c WHERE a.ProjectId = c.ProjectId and c.ApprovedbyCustomer in (1,2)) AS ContractId
,g.ProjectValue
, g.Duration
, (CASE g.DurationType WHEN 1 THEN 'Days' WHEN 2 THEN 'Weeks' ELSE 'Months' END) As DurationType
, g.StartDate
, g.EndDate
, g.ApprovedByCustomer
--0 - No Action, 1- Accepted, 2- Send Back
,(SELECT COUNT(*) FROM PaymentRequest e WHERE a.ProjectId = e.ProjectId) AS PaymentCount
FROM
Project a
LEFT JOIN
Contract g ON a.ProjectId = g.ProjectId
-- LEFT JOIN Partner h ON g.PartnerId = h.PartnerId
WHERE
a.CustomerId = 11111;
Try a subquery with ROW_NUMBER() OVER (PARTITION BY a.ProjectId ORDER BY g.ApprovedByCustomer) AS RowNum. In the outer query, add the criteria WHERE RowNum = 1:
SELECT *
FROM
(
SELECT
ROW_NUMBER() OVER (PARTITION BY a.ProjectId ORDER BY g.ApprovedByCustomer) AS RowNum
,a.ProjectId
,a.Title
,a.CreationDate
,a.Status
,DATEDIFF(second, CAST(SWITCHOFFSET(SYSDATETIMEOFFSET(), '+05:30') AS DATETIME), BidEndDateTime) / 3600 As BidTimeLeft
,(SELECT COUNT(*) FROM Bidding b WHERE a.ProjectId = b.ProjectId) AS BidsCount
,(SELECT e.CompanyName FROM Bidding b inner join Partner e on b.PartnerId = e.PartnerId WHERE a.ProjectId = b.ProjectId and b.BidAccepted = 1) AS BidSelected
--,h.CompanyName
--Contact table column ApprovedbyCustomer will only have one 0 or 1 for same project, Partner can only create contract either for the first time or if it is rejected by Customer
,(SELECT COUNT(*) FROM Contract x WHERE a.ProjectId = x.ProjectId and x.ApprovedbyCustomer in (0,1) ) AS ContractCount
,g.ContractId
--,(SELECT c.ContractId FROM Contract c WHERE a.ProjectId = c.ProjectId and c.ApprovedbyCustomer in (1,2)) AS ContractId
,g.ProjectValue
, g.Duration
, (CASE g.DurationType WHEN 1 THEN 'Days' WHEN 2 THEN 'Weeks' ELSE 'Months' END) As DurationType
, g.StartDate
, g.EndDate
, g.ApprovedByCustomer
--0 - No Action, 1- Accepted, 2- Send Back
,(SELECT COUNT(*) FROM PaymentRequest e WHERE a.ProjectId = e.ProjectId) AS
PaymentCount
FROM
Project a
LEFT JOIN
Contract g ON a.ProjectId = g.ProjectId
-- LEFT JOIN Partner h ON g.PartnerId = h.PartnerId
WHERE
a.CustomerId = 11111
) OrderedProjects
WHERE RowNum = 1;
You need to add the filter to the main query:
and g.ApprovedbyCustomer in (0,1)

How to get first and last related event without having to do extra subqueries?

I have two tables: Customers and CustomerEvents. Each customer can have 0 or more events.
I'm working on a report that's supposed to look something like this:
CustomerName | FirstEventDate | FirstEventMessage | LastEventDate | LastEventText
---------------------------------------------------------------------------------------
Customers 'R' US | 2018-01-01 | Customer registered | 2018-04-06 | Customer Quit
The actual query is going to be a lot larger than that, but this is the general gist of it.
If I only needed the dates, I could easily do it using aggregates:
SELECT
c.Name AS ContactName
, MAX(e.DateTime) AS FirstEventDate
, '???' AS FirstEventMessage
, MIN(e.DateTime) AS FirstEventDate
, '???' AS FirstEventMessage
FROM Contacts c
LEFT JOIN CustomerEvents e ON e.ContactId = c.Id
GROUP BY c.Name
Unfortunately, that leaves out the corresponding messages.
I've managed to solve it using a subquery for each message, but that gets super expensive, so I was wondering if there's another approach you'd suggest. The full query is more complex than the example will have joins on different kinds of events, each displaying the date and message of the first and last event.
As you asked me, to create an answer out of my comment, here you are:
With version 2012 Microsoft introduced some new windowing functions. Usefull for you are FIRST_VALUE() and LAST_VALUE(). Both need an OVER() clause to specify the sort order and - if needed - a partitioning rule.
This should work (but I do not know your tables and data):
SELECT
c.Name AS ContactName
, MIN(e.DateTime) AS FirstEventDate
, FIRST_VALUE(e.EventMessage) OVER(ORDER BY e.DateTime) AS FirstEventMessage
, MAX(e.DateTime) AS LastEventDate
, LAST_VALUE(e.EventMessage) OVER(ORDER BY e.DateTime) AS LastventMessage
FROM Contacts c
LEFT JOIN CustomerEvents e ON e.ContactId = c.Id
GROUP BY c.Id,c.Name;
But be warned: If your e.DateTime is not unique (per Contact), you will get a random "first" value...
Hints
Make sure to have indexes on DateTime and ContactId and
add the c.Id to your GROUP BY
An alternative was to replace the LEFT JOIN CustomerEvents with a row-wise executed correlated sub-query. This has the advantage, that you can be sure, that both (and more) values are taken from the same row.
OUTER APPLY (SELECT TOP 1 e.[DateTime],e.[EventMessage]
FROM CustomerEvents AS e
WHERE e.ContactId=c.Id
ORDER BY e.EventMessage ASC) AS FirstEvent
--same with DESC
OUTER APPLY (SELECT TOP 1 e.[DateTime],e.[EventMessage]
FROM CustomerEvents AS e
WHERE e.ContactId=c.Id
ORDER BY e.EventMessage DESC) AS LastEvent
Then use the columns in your query like
FirstEvent.DateTime AS FirstDateTime
,FirstEvent.EventMessage AS FirstMessage
,LastEvent.DateTime AS LastDateTime
,LastEvent.EventMessage AS LastMessage
Try this CTE approach, adjust the columns to SELECT clause according to you need.
;WITH CTE
AS(
SELECT
*,
ROW_NUMBER() OVER(PARTITION BY c.Name ORDER BY e.DateTime) AS RowAsc,
ROW_NUMBER() OVER(PARTITION BY c.Name ORDER BY e.DateTime DESC) AS RowDesc
FROM Contacts c
LEFT JOIN CustomerEvents e ON e.ContactId = c.Id
)
SELECT
c1.*
, c2.*
FROM CTE c1
INNER JOIN CTE c2 ON c1.Name = c2.Name
AND c1.RowAsc = 1
AND c2.RowDesc = 1
Try Below Approach
SELECT
c.Name AS CustomerName
, MAX(e.DateTime) AS LastEventDate
, B.Message AS FirstEventMessage
, MIN(e.DateTime) AS FirstEventDate
, D.Message AS FirstEventMessage
FROM #Customers c
LEFT JOIN #CustomerEvents e ON e.CustomerId = c.Id
LEFT JOIN
(
SELECT A.CustomerId, A.Message
FROM
(
SELECT
CustomerId, Message, Row_Number()over(Partition By CustomerId order by DATETIME DESC) as No
FROM
#CustomerEvents
) A
WHERE
A.No = 1
) B ON B.CustomerId = C.Id
LEFT JOIN
(
SELECT A.CustomerId, A.Message
FROM
(
SELECT
CustomerId, Message, Row_Number()over(Partition By CustomerId order by DATETIME) as No
FROM
#CustomerEvents
) A
WHERE
A.No = 1
) d ON d.CustomerId = C.Id
GROUP BY
c.Name, B.Message, D.Message

query is not returning distinct record

Hi can you please take a look why my query is not returning distinct record. i want result with following condition OE1='SCHEDCHNG', need only recent records per orderid or ordernum means only one record per ordernum or orderid and also dropdate is null. My query is as below
select DISTINCT TOP 100 OE.ORDERID,OE.ID,OE.ORDERNUM,OE.OE4 from OrderExports OE
inner join (
select ORDERNUM, max(OE4) as MaxDate
from OrderExports
group by ORDERNUM
) tm
on OE.ORDERNUM = tm.ORDERNUM and OE.OE4 = tm.MaxDate
inner join orde_ O on OE.ORDERID = O.ORDERID
WHERE OE1='SCHEDCHNG' AND O.DROPDATE is null
Pretty sparse on details here but I think you are wanting something along these lines.
with SortedResults as
(
select OE.ORDERID
, OE.ID
, OE.ORDERNUM
, OE.OE4
, ROW_NUMBER() over(partition by OE.ORDERID, OE.ORDERNUM order by OE.OE4 desc) as RowNum
from OrderExports OE
inner join
(
select ORDERNUM
, max(OE4) as MaxDate
from OrderExports
group by ORDERNUM
) tm on OE.ORDERNUM = tm.ORDERNUM and OE.OE4 = tm.MaxDate
inner join orde_ O on OE.ORDERID = O.ORDERID
WHERE OE1='SCHEDCHNG'
AND O.DROPDATE is null
)
select ORDERID
, ID
, ORDERNUM
, OE4
from SortedResults
where RowNum = 1
You can try using max and group by as below :
SELECT a.ID, max(a.ORDERID) as OrderID, max(a.ORDERNUM) as OrderNum,MAX(OE.OE4) as OE4 FROM
(
--your query
) a
group by a.ID

Getting most recent date from multiple SQL columns

The suggested answer, in this post, works great for two columns.
I have about 50 different date columns, where I need to be able to report on the most recent interaction, regardless of table.
In this case, I am bringing the columns in to a view, since they are coming from different tables in two different databases...
CREATE VIEW vMyView
AS
SELECT
comp_name AS Customer
, Comp_UpdatedDate AS Last_Change
, CmLi_UpdatedDate AS Last_Communication
, Case_UpdatedDate AS Last_Case
, AdLi_UpdatedDate AS Address_Change
FROM Company
LEFT JOIN Comm_Link on Comp_CompanyId = CmLi_Comm_CompanyId
LEFT JOIN Cases ON Comp_CompanyId = Case_PrimaryCompanyId
LEFT JOIN Address_Link on Comp_CompanyId = AdLi_CompanyID
...
My question is, how I would easily account for the many possibilities of one column being greater than the others?
Using only the two first columns, as per the example above, works great. But considering that one row could have column 3 as the highest value, another row could have column 14 etc...
SELECT Customer, MAX(CASE WHEN (Last_Change IS NULL OR Last_Communication> Last_Change)
THEN Last_Communication ELSE Last_Change
END) AS MaxDate
FROM vMyView
GROUP BY Customer
So, how can I easily grab the highest value for each row in any of the 50(ish) columns?
I am using SQL Server 2008 R2, but I also need this to work in versions 2012 and 2014.
Any help would be greatly appreciated.
EDIT:
I just discovered that the second database is storing the dates in NUMERIC fields, rather than DATETIME. (Stupid! I know!)
So I get the error:
The type of column "ARCUS" conflicts with the type of other columns specified in the UNPIVOT list.
I tried to resolve this with a CAST to make it DATETIME, but that only resulted in more errors.
;WITH X AS
(
SELECT Customer
,Value [Date]
,ColumnName [Entity]
,BusinessEmail
,ROW_NUMBER() OVER (PARTITION BY Customer ORDER BY Value DESC) rn
FROM (
SELECT comp_name AS Customer
, Pers_EmailAddress AS BusinessEmail
, Comp_UpdatedDate AS Company
, CmLi_UpdatedDate AS Communication
, Case_UpdatedDate AS [Case]
, AdLi_UpdatedDate AS [Address]
, PLink_UpdatedDate AS Phone
, ELink_UpdatedDate AS Email
, Pers_UpdatedDate AS Person
, oppo_updateddate as Opportunity
, samdat.dbo.ARCUS.AUDTDATE AS ARCUS
FROM vCompanyPE
LEFT JOIN Comm_Link on Comp_CompanyId = CmLi_Comm_CompanyId
LEFT JOIN Cases ON Comp_CompanyId = Case_PrimaryCompanyId
LEFT JOIN Address_Link on Comp_CompanyId = AdLi_CompanyID
LEFT JOIN PhoneLink on Comp_CompanyId = PLink_RecordID
LEFT JOIN EmailLink on Comp_CompanyId = ELink_RecordID
LEFT JOIN vPersonPE on Comp_CompanyId = Pers_CompanyId
LEFT JOIN Opportunity on Comp_CompanyId = Oppo_PrimaryCompanyId
LEFT JOIN Orders on Oppo_OpportunityId = Orde_opportunityid
LEFT JOIN SAMDAT.DBO.ARCUS on IDCUST = Comp_IdCust
COLLATE Latin1_General_CI_AS
WHERE Comp_IdCust IS NOT NULL
AND Comp_deleted IS NULL
) t
UNPIVOT (Value FOR ColumnName IN
(
Company
,Communication
,[Case]
,[Address]
,Phone
,Email
,Person
,Opportunity
,ARCUS
)
)up
)
SELECT Customer
, BusinessEmail
,[Date]
,[Entity]
FROM X
WHERE rn = 1 AND [DATE] >= DATEADD(year,-2,GETDATE()) and BusinessEmail is not null
You could use CROSS APPLY to manually pivot your fields, then use MAX()
SELECT
vMyView.*,
greatest.val
FROM
vMyView
CROSS APPLY
(
SELECT
MAX(val) AS val
FROM
(
SELECT vMyView.field01 AS val
UNION ALL SELECT vMyView.field02 AS val
...
UNION ALL SELECT vMyView.field50 AS val
)
AS manual_pivot
)
AS greatest
The inner most query will pivot each field in to a new row, then the MAX() re-aggregate them back in to a single row. (Also skipping NULLs, so you don't need to explicitly cater for them.)
;WITH X AS
(
SELECT Customer
,Value [Date]
,ColumnName [CommunicationType]
,ROW_NUMBER() OVER (PARTITION BY Customer ORDER BY Value DESC) rn
FROM (
SELECT comp_name AS Customer
, Comp_UpdatedDate AS Last_Change
, CmLi_UpdatedDate AS Last_Communication
, Case_UpdatedDate AS Last_Case
, AdLi_UpdatedDate AS Address_Change
FROM Company
LEFT JOIN Comm_Link on Comp_CompanyId = CmLi_Comm_CompanyId
LEFT JOIN Cases ON Comp_CompanyId = Case_PrimaryCompanyId
LEFT JOIN Address_Link on Comp_CompanyId = AdLi_CompanyID
) t
UNPIVOT (Value FOR ColumnName IN (Last_Change,Last_Communication,
Last_Case,Address_Change))up
)
SELECT Customer
,[Date]
,[CommunicationType]
FROM X
WHERE rn = 1

Resources