0

I have 2 tables in postgres.

users

auth0_id email
123-A a@a
123-B b@b
123-C c@c

auth0_logs

id date user_id client_name
abc-1 021-10-16T00:18:41.381Z 123-A example_client
abc-2 ... 123-A example_client
abc-3 ... 123-B example_client
abc-4 ... 123-A example_client
abc-5 ... 123-B example_client
abc-6 ... 123-C example_client

I am trying to get the last login information (a single row in the auth0_logs table based on MAX(auth0_logs.date) ) for for each unique user (auth0_logs.user_id) joined to the users table on user.auth0_id.

[
    {
        // auth0_logs information
        user_id: "123-A",
        last_login: "021-10-16T00:18:41.381Z",
        client_name: "example_client",

        // users information
        email: "a@a"
    },
    {
        user_id: "123-B",
        last_login: "...",
        client_name: "example_client",
        email: "b@b"
    },
    {
        user_id: "123-C",
        last_login: "...",
        client_name: "example_client",
        email: "c@c"
    }
]

I know this is a problem with "bare" columns not being allowed in queries that use aggregators (without being added to the GROUP BY -- but adding to the GROUP BY returned > 1 row) but I cannot get a solution that works from other SO posts (best post I've found: SQL select only rows with max value on a column). I promise you I have been on this for many hours over the past few days ....

-- EDIT: start --

I have removed my incorrect attempts as to not confuse / misdirect future readers. Please see @MichaelRobellard answer using the WITH clause based on the above information.

-- EDIT: end --

Any help or further research direction would be greatly appreciated!

Reed Turgeon
  • 261
  • 5
  • 13
  • Is the table login_logs or auth0_logs, doesn't matter other than it is confusing while trying to understand your question. Trying to make sure I understand what you are asking, You want all logs of all users who have ever logged in and their last login date? – Michael Robellard Oct 17 '21 at 18:39
  • Please supply the actual columnar results expected, with the specific values. Not a description as 'All columns'. – Belayer Oct 17 '21 at 18:49
  • yes, @MichaelRobellard I would like the most recent auth0_logs information (and have updated the question so that the table names do not conflict) – Reed Turgeon Oct 17 '21 at 18:55
  • Which row of a group do you want a bare column to return? If you don't care, use MAX. This is a faq. Please before considering posting read the manual & google any error message & many clear, concise & precise phrasings of your question/problem/goal, with & without your particular names/strings/numbers, 'site:stackoverflow.com' & tags; read many answers. Reflect your research. [ask] [Help] This post does not even contain a clear statement of what you want. ( Putting words in scare quotes does not clarify the idiosyncratic meaning that you don't make clear by actually saying what you mean) – philipxy Oct 17 '21 at 19:05
  • Please in code questions give a [mre]--cut & paste & runnable code; example input with desired & actual output (including verbatim error messages); tags & versions; clear specification & explanation. For errors that includes the least code you can give that is code that you show is OK extended by code that you show is not OK. (Debugging fundamental.) For SQL include DDL & tabular initialization code. When you get a result you don't expect, pause your overall goal, chop to the 1st subexpression with unexpected result & say what you expected & why, justified by documentation. – philipxy Oct 17 '21 at 19:05
  • 1
    WITH clause _is_ a definition of a CTE. It allows for step-by-step declararions like `format to JSON and aggregate (the final select) the set of latest login logs (t) with additional fields (tt)` among other nice things. – Stefanov.sm Oct 17 '21 at 19:26
  • You should leave your original attempts in the question always. It will help others having similar issues look for the answer in the future. To add to what Stefanov said, a CTE allows two things, in your case, it makes for easier to read sql by cleaning up what would otherwise be very obtuse hard to read subqueries that can be hard to read. The other main use of CTEs is to allow for recursive queries. – Michael Robellard Oct 17 '21 at 20:10

2 Answers2

1
with
t as 
(
 select distinct on (user_id) * 
 from login_logs 
 order by user_id, ldate desc
),
tt as 
(
 select auth0_id user_id, ldate last_login, client_name, email
 from t join users on auth0_id = user_id
) 
select json_agg(to_json(tt.*)) from tt;

SQL fiddle here.

Stefanov.sm
  • 11,215
  • 2
  • 21
  • 21
1
with user_data as (
    select user_id, max(date) from auth0_logs group by user_id
)
select * from user_data
join auth0_logs on user_data.user_id = auth0_logs.user_id 
    and user_data.date = auth0_logs.date
join users on user_data.user_id = users.auth0_id
Michael Robellard
  • 2,268
  • 15
  • 25
  • THANK YOU MICHAEL :) I am interested that both you and @stefanov-sm used the WITH Clause. I am not very familiar with this but will keep in mind going forward! for others who have not seen this and want further research I am reading it is considered "subquery refactoring" and is similar to CTE (Common Table Expression) – Reed Turgeon Oct 17 '21 at 19:22
  • JSON array result as expected by OP? – Stefanov.sm Oct 17 '21 at 19:33
  • @Stefanov.sm using this query through `results = knex.raw(sql)` I am getting results.rows in the requested array of objects format. – Reed Turgeon Oct 17 '21 at 19:46