CSharpNewBee
CSharpNewBee

Reputation: 1981

Get Last message loaded based on message type

I have a table which contains different messages, each with a messageType field. I need to fill a ASP.NET list control of the last loaded message for each messageType. My sql statement to return everything is:

SELECT MessageTypes.MessageType MessageType
    ,Messages.MessageDate
    ,Messages.ValueDate
    ,Messages.MessageReference
    ,Messages.Beneficiary
    ,Messages.StatusId
    ,MessageStatus.STATUS
    ,BICProfile.BIC
FROM Messages
INNER JOIN MessageStatus
    ON Messages.StatusId = MessageStatus.Id
INNER JOIN MessageTypes
    ON Messages.MessageTypeId = MessageTypes.MessageTypeId
INNER JOIN BICProfile
    ON Messages.SenderId = dbo.BICProfile.BicId
WHERE (BICProfile.BIC = 'someValue')
    AND Messages.StatusId IN (4, 5, 6)

So i need to pull back the last message for each message type.

EDIT Some clarification on what the statusId is. These are basically used in conjunction with a users role, what messages that can see. So, i need to return the last loaded message for a given message type. Effectively, only looking to return 1 message for each message type. The messageId is unique, where the message date could be the same.

Upvotes: 1

Views: 135

Answers (3)

GarethD
GarethD

Reputation: 69789

You can use the ROW_NUMBER() Function to assign each of your messages a rank by Message date (starting at 1 again for each message type), then just limit the results to the top ranked message:

WITH AllMessages AS
(   SELECT  MessageTypes.MessageType, 
            Messages.MessageDate, 
            Messages.ValueDate, 
            Messages.MessageReference, 
            Messages.Beneficiary, 
            Messages.StatusId,
            MessageStatus.Status, 
            BICProfile.BIC,
            RowNumber = ROW_NUMBER() OVER(PARTITION BY Messages.MessageTypeId 
                                            ORDER BY Messages.MessageDate DESC)
    FROM    Messages 
            INNER JOIN MessageStatus 
                ON Messages.StatusId = MessageStatus.Id 
            INNER JOIN MessageTypes 
                ON Messages.MessageTypeId = MessageTypes.MessageTypeId 
            INNER JOIN BICProfile  
                ON Messages.SenderId = dbo.BICProfile.BicId 
    WHERE   BICProfile.BIC = 'someValue'
    AND     Messages.StatusId IN (4, 5, 6)
)
SELECT  MessageType, 
        MessageDate, 
        ValueDate, 
        MessageReference, 
        Beneficiary, 
        StatusId,
        Status, 
        BIC 
FROM    AllMessages
WHERE   RowNumber = 1;

If you can't use ROW_NUMBER then you can use a subquery to get the latest message date per type:

SELECT  Messages.MessageTypeID, MessageDate = MAX(Messages.MessageDate)
FROM    Messages
        INNER JOIN BICProfile  
            ON Messages.SenderId = dbo.BICProfile.BicId 
WHERE   BICProfile.BIC = 'someValue'
AND     Messages.StatusId IN (4, 5, 6)
GROUP BY Messages.MessageTypeID

Then inner join the results of this back to your main query to filter the results:

SELECT  MessageTypes.MessageType, 
        Messages.MessageDate, 
        Messages.ValueDate, 
        Messages.MessageReference, 
        Messages.Beneficiary, 
        Messages.StatusId,
        MessageStatus.Status, 
        BICProfile.BIC
FROM    Messages 
        INNER JOIN MessageStatus 
            ON Messages.StatusId = MessageStatus.Id 
        INNER JOIN MessageTypes 
            ON Messages.MessageTypeId = MessageTypes.MessageTypeId 
        INNER JOIN BICProfile  
            ON Messages.SenderId = dbo.BICProfile.BicId 
        INNER JOIN 
        (   SELECT  Messages.MessageTypeID, 
                    MessageDate = MAX(Messages.MessageDate)
            FROM    Messages
                    INNER JOIN BICProfile  
                        ON Messages.SenderId = dbo.BICProfile.BicId 
            WHERE   BICProfile.BIC = 'someValue'
            AND     Messages.StatusId IN (4, 5, 6)
            GROUP BY Messages.MessageTypeID
        ) AS MaxMessage
            ON MaxMessage.MessageTypeID = Messages.MessageTypeID
            AND MaxMessage.MessageDate = Messages.MessageDate
WHERE   BICProfile.BIC = 'someValue'
AND     Messages.StatusId IN (4, 5, 6);

N.B This second method will return multiple rows per message type if the latest message date is common among more than one message. This behaviour can be replicated in the first query by replacing ROW_NUMBER with RANK


EDIT

If you will have multiple messages with the same date and only want to return one of them you need to expand the ordering within the row_number function, i.e. if you wanted to pick the message with the maximum id when there were ties you could make it:

RowNumber = ROW_NUMBER() OVER(PARTITION BY Messages.MessageTypeId 
                                ORDER BY Messages.MessageDate DESC,
                                        Messages.MessageID DESC)

So the full query would be:

WITH AllMessages AS
(   SELECT  MessageTypes.MessageType, 
            Messages.MessageDate, 
            Messages.ValueDate, 
            Messages.MessageReference, 
            Messages.Beneficiary, 
            Messages.StatusId,
            MessageStatus.Status, 
            BICProfile.BIC,
            RowNumber = ROW_NUMBER() OVER(PARTITION BY Messages.MessageTypeId 
                                            ORDER BY Messages.MessageDate DESC,
                                                    Messages.MessageID DESC)
    FROM    Messages 
            INNER JOIN MessageStatus 
                ON Messages.StatusId = MessageStatus.Id 
            INNER JOIN MessageTypes 
                ON Messages.MessageTypeId = MessageTypes.MessageTypeId 
            INNER JOIN BICProfile  
                ON Messages.SenderId = dbo.BICProfile.BicId 
    WHERE   BICProfile.BIC = 'someValue'
    AND     Messages.StatusId IN (4, 5, 6)
)
SELECT  MessageType, 
        MessageDate, 
        ValueDate, 
        MessageReference, 
        Beneficiary, 
        StatusId,
        Status, 
        BIC 
FROM    AllMessages
WHERE   RowNumber = 1;

Upvotes: 2

Brian DeMilia
Brian DeMilia

Reputation: 13248

You can use a simple subquery to get the laste messagedate for each message type and use that to filter in on the last message. See below:

If you want the last message for each category, only where the status of that message is 4, 5, or 6: (no result for that category if the status of the message is something else)

SELECT MessageTypes.MessageType MessageType,
       Messages.MessageDate,
       Messages.ValueDate,
       Messages.MessageReference,
       Messages.Beneficiary,
       Messages.StatusId,
       MessageStatus.STATUS,
       BICProfile.BIC
  FROM Messages
 INNER JOIN MessageStatus
    ON Messages.StatusId = MessageStatus.Id
 INNER JOIN MessageTypes
    ON Messages.MessageTypeId = MessageTypes.MessageTypeId
 INNER JOIN BICProfile
    ON Messages.SenderId = dbo.BICProfile.BicId
 WHERE BICProfile.BIC = 'someValue'
   AND Messages.StatusId IN (4, 5, 6)
   and messages.messagedate =
       (select max(x.messagedate)
          from messages x
         where x.messagetypeid = messages.messagetypeid)

If you want the last message for each category, only where the status of that message is 4, 5, or 6: (no result for that category if the status of the message is something else) AND IN THE EVENT OF A TIE YOU WANT THE MESSAGE WITH THE HIGHEST MESSAGE ID

SELECT MessageTypes.MessageType MessageType,
       Messages.MessageDate,
       Messages.ValueDate,
       Messages.MessageReference,
       Messages.Beneficiary,
       Messages.StatusId,
       MessageStatus.STATUS,
       BICProfile.BIC
  FROM Messages
 INNER JOIN MessageStatus
    ON Messages.StatusId = MessageStatus.Id
 INNER JOIN MessageTypes
    ON Messages.MessageTypeId = MessageTypes.MessageTypeId
 INNER JOIN BICProfile
    ON Messages.SenderId = dbo.BICProfile.BicId
 WHERE BICProfile.BIC = 'someValue'
   AND Messages.StatusId IN (4, 5, 6)
   and messages.messagedate =
       (select max(x.messagedate)
          from messages x
         where x.messagetypeid = messages.messagetypeid)
   and messages.messageid =
       (select max(x.messageid)
          from messages x
         where x.messagetypeid = messages.messagetypeid
           and x.messagedate = messages.messagedate)

^^ CHANGE "messageid" to whatever field on table MESSAGES indicates the ID.

If you want the last message for each category and for each status of 4, 5, and 6 (if any/all exist within that category)

SELECT MessageTypes.MessageType MessageType,
       Messages.MessageDate,
       Messages.ValueDate,
       Messages.MessageReference,
       Messages.Beneficiary,
       Messages.StatusId,
       MessageStatus.STATUS,
       BICProfile.BIC
  FROM Messages
 INNER JOIN MessageStatus
    ON Messages.StatusId = MessageStatus.Id
 INNER JOIN MessageTypes
    ON Messages.MessageTypeId = MessageTypes.MessageTypeId
 INNER JOIN BICProfile
    ON Messages.SenderId = dbo.BICProfile.BicId
 WHERE BICProfile.BIC = 'someValue'
   AND Messages.StatusId IN (4, 5, 6)
   and messages.messagedate =
       (select max(x.messagedate)
          from messages x
         where x.messagetypeid = messages.messagetypeid
           and x.statusid = messages.statusid)

If you want the last message for each category where the status is 4, 5, or 6: (messages of other statuses not considered)

SELECT MessageTypes.MessageType MessageType,
       Messages.MessageDate,
       Messages.ValueDate,
       Messages.MessageReference,
       Messages.Beneficiary,
       Messages.StatusId,
       MessageStatus.STATUS,
       BICProfile.BIC
  FROM Messages
 INNER JOIN MessageStatus
    ON Messages.StatusId = MessageStatus.Id
 INNER JOIN MessageTypes
    ON Messages.MessageTypeId = MessageTypes.MessageTypeId
 INNER JOIN BICProfile
    ON Messages.SenderId = dbo.BICProfile.BicId
 WHERE BICProfile.BIC = 'someValue'
   AND Messages.StatusId IN (4, 5, 6)
   and messages.messagedate =
       (select max(x.messagedate)
          from messages x
         where x.messagetypeid = messages.messagetypeid
           and x.statusid in (4, 5, 6))

Upvotes: 0

t-clausen.dk
t-clausen.dk

Reputation: 44336

Try this:

SELECT MT.MessageType
    ,X.MessageDate
    ,X.ValueDate
    ,X.MessageReference
    ,X.Beneficiary
    ,X.StatusId
    ,X.STATUS
    ,X.BIC
FROM MessageTypes MT
OUTER APPLY -- or CROSS APPLY
(
SELECT TOP 1
    M.MessageDate
    ,M.ValueDate
    ,M.MessageReference
    ,M.Beneficiary
    ,M.StatusId
    ,MS.STATUS
    ,B.BIC
FROM Messages M
INNER JOIN MessageStatus MS
    ON M.StatusId = MS.Id
INNER JOIN BICProfile B
    ON M.SenderId = B.BicId
WHERE (B.BIC = 'someValue')
    AND M.StatusId IN (4, 5, 6)
    AND M.MessageTypeId = MT.MessageTypeId
ORDER BY M.MessageDate DESC
) X

Upvotes: 2

Related Questions