1

I have a table with something like this:

User | ProfileId |      OpenDate       |      CloseDate      | ProfileValue
----------------------------------------------------------------------------
test |     1     | 2018-10-25 11:40:00 | 2018-10-25 11:40:00 |     10 
test |     3     | 2018-10-25 11:40:00 |        NULL         |     3 
test |     4     | 2018-10-25 11:45:00 | 2018-10-25 11:40:00 |     4 
test |     7     | 2018-10-18 10:00:00 |        NULL         |     5

An open account is one with a null CloseDate. I want to retrieve the ProfileId of the most recently opened account (i.e. MAX(OpenDate) where CloseDate IS NULL) , and I want the maximum ProfileValue for those open accounts. In the above example, this means I want to return a row with ProfileId 3 and ProfileValue 5. So ideally:

User | ProfileId | ProfileValue
--------------------------------
test |     3     |     5 

However, the trouble I am having is for the case when there is no open accounts, I want to return the most recently opened account (regardless of when it was closed) and whatever the max ProfileValue is, and I am not sure how to condition this.

For my example, the query I have so far looks like this:

SELECT U.User,
       MAX(P.OpenDate) AS OpenDate,
       CASE WHEN MAX(CASE WHEN P.CloseDate IS NULL THEN 1 ELSE 0 END) = 0
        THEN MAX(P.CloseDate) END AS CloseDate, -- use null CloseDate field if available
       MAX(R.ProfileValue) AS ProfileValue
FROM #UserIds U
LEFT JOIN dbo.Profiles P
    ON U.User = P.User
INNER JOIN [dbo].[ReferenceTable] R
    ON P.ProfileId = R.ProfileId
GROUP BY U.User, P.CloseDate
HAVING P.CloseDate IS NULL -- TO DO?

This will return

User |      OpenDate       |      CloseDate      | ProfileValue
----------------------------------------------------------------
test | 2018-10-25 11:40:00 |        NULL         |     5

And I can then join this table back to the Profiles table to get that the ProfileId, although this isn't particularly efficient either.

How can I fix my HAVING clause to include the scenario when there are no open accounts? I tried to do something like

HAVING ISNULL(P.CloseDate, '2079-06-06 23:59:00') = MAX(COALESCE(P.CloseDate, '2079-06-06 23:59:00'))

to try and make NULL CloseDate be considered the max value, but this returns too many rows. And is there a better way to return ProfileId in my query so that I don't have to join the resulting table to itself again?

Edit:

Adapting @Gordon Linoff's answer:

SELECT A.User, A.ProfileId, A.OpenDate, A.CloseDate, A.ProfileValue
FROM #UserIds U OUTER APPLY
     (SELECT TOP 5 P.User, P.ProfileId, P.OpenDate, P.CloseDate, R.ProfileValue
      FROM dbo.Profiles P
      INNER JOIN [dbo].[ReferenceTable] R
        ON P.ProfileId = R.ProfileId
      WHERE U.User = P.User
      ORDER BY (CASE WHEN p.CloseDate IS NULL THEN 1 ELSE 2 END),  -- put open accounts first
               P.OpenDate DESC  -- put most recent opened first
    ) A

This will return:

User | ProfileId |      OpenDate       |      CloseDate      | ProfileValue
----------------------------------------------------------------------------
test |     3     | 2018-10-25 11:40:00 |        NULL         |     3
test |     7     | 2018-10-18 10:00:00 |        NULL         |     5
test |     4     | 2018-10-25 11:45:00 | 2018-10-25 11:40:00 |     4 
test |     1     | 2018-10-25 11:40:00 | 2018-10-25 11:40:00 |     10

So if I modify the subquery to return TOP 1, it will provide the correct ProfileId, but I need to also get the MAX(ProfileValue) of the open accounts (which is 5), and in the case that there are no open accounts, simply return the max of the all the accounts. I'm trying various group by clauses, but they seem to shuffle the ProfileId order. My back up plan is to simply retrieve the max ProfileValue in a separate query, but this is inefficient. Any ideas on how I can fix this query?

2 Answers 2

1

This seems like a good situation to use lateral joins:

SELECT p.*
FROM #UserIds U OUTER APPLY
     (SELECT p.*
      FROM dbo.Profiles P
      WHERE U.User = P.User
      ORDER BY (CASE WHEN p.CloseDate IS NULL THEN 1 ELSE 2 END),  -- put open accounts first
               o.OpenDate DESC  -- put most recent opened first
    ) p;

Your query mentions other tables. I don't understand what they are being used for, because they are not part of the question.

Sign up to request clarification or add additional context in comments.

4 Comments

Thank you for the response. What other tables are you referring to? For your answer, I'm assuming o.OpenDate is a typo for p.OpenDate, and I added a TOP 1 to the inner query (to avoid the error: The ORDER BY clause is invalid in views, inline functions, derived tables, subqueries, and common table expressions, unless TOP, OFFSET or FOR XML is also specified.). This gives me the correct ProfileId so you have set me on the right track, but I am currently trying to use your answer to select the MAX(ProfileValue) of all open accounts (or the max value if there are no open accounts).
@C.Helling . . . ReferenceTable.
Ah, that's just where the ProfileValues are stored. Each ProfileId has a different ProfileValue associated with it (1-to-1), so I was just inner joining to that reference table to get those values. Right now I am trying to get the max ProfileValue of open accounts, regardless of the OpenDate (or the max value of all accounts if they all happen to be closed).
Your answer set me on the right track, so I will click to accept this answer in a bit, and I will upvote you tomorrow after your rep cap resets. Thanks for the help.
0

I adapted this from Gordon Linoff's answer. I used his suggestion to get the most recently opened ProfileId, and I used my old idea to get the ProfileValue, and I joined the two tables together. This can't possibly be the best way to do this, but I've been testing it and it appears to work.

SELECT D.User, D.ProfileId, D.ProfileValue
FROM
(
SELECT C.*, CASE WHEN (ROW_NUMBER() OVER
(PARTITION BY User ORDER BY User))=1 THEN 1 ELSE 0 END FirstTime -- We only want the first time the User appears in the result set
FROM
    (
    SELECT A.User, A.ProfileId, B.ProfileValue
    FROM #Users U OUTER APPLY
        (SELECT TOP 1 P.User, P.ProfileId, P.OpenDate, P.CloseDate, L.ProfileValue
              FROM [dbo].[Profiles] P
              INNER JOIN [dbo].[ReferenceTable] L
                ON P.ProfileId = L.ProfileId
              WHERE U.User = P.User
              ORDER BY (CASE WHEN p.CloseDate IS NULL THEN 1 ELSE 2 END),  -- put open accounts first
                       P.OpenDate DESC  -- put most recent opened first
        ) A
    LEFT OUTER JOIN
        (SELECT U.User,
                   MAX(P.OpenDate) AS OpenDate,
                   CASE WHEN MAX(CASE WHEN P.CloseDate IS NULL THEN 1 ELSE 0 END) = 0
                    THEN MAX(P.CloseDate) END AS CloseDate, -- use null CloseDate field if available
                   MAX(L.ProfileValue) AS ProfileValue
        FROM #Users U
            LEFT JOIN [dbo].[Profiles] P
                ON U.User = P.User
            INNER JOIN [dbo].[ReferenceTable] L
                ON P.ProfileId = L.ProfileId
            GROUP BY U.User, P.CloseDate
        ) B
    ON A.User = B.User
    AND A.OpenDate = B.OpenDate
    ) C
) D
WHERE D.FirstTime = 1

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.