Search code examples
mysqlsqljpql

SQL and/or JPQL Statement to retrieve single record sorted by date desc


Require SQL and/or JPQL statement to retrieve latest record for a given account sorted by date (descending). It should return a single record (latest record) for all users e.g. if 'user a' sends/receives multiple message from 'user b', 'user c', and 'user d', it would return the latest record for each user (3 in total).

My Attempt at JPQL:

SELECT DISTINCT dm FROM DirectMessage dm WHERE (dm.messageFrom.id=1 OR dm.messageTo.id=1)  GROUP BY dm.id, dm.messageFrom.id, dm.messageTo.id ORDER BY dm.date DESC

Concrete Example with schema:

if I wished to return the latest record for 'user a' then correct number of record returned would be 3 (insert statement for each of these 3 records below):

1.INSERT INTO direct_message VALUE(2, 2, 1, 'hello back', now());
2.INSERT INTO direct_message VALUE(4, 3, 1, 'hello back', now());
3.INSERT INTO direct_message VALUE(6, 4, 1, 'hello back', now());

Script

CREATE TABLE account(
    id bigint UNSIGNED NOT NULL auto_increment,
    username varchar(75) NOT NULL,
    name varchar(70) NOT NULL,
    date datetime NOT NULL,
    CONSTRAINT pkey_account_id PRIMARY KEY(id)
);

INSERT INTO account VALUES (1,'user_a','User A', now());
INSERT INTO account VALUES (2,'user_b','User B', now());
INSERT INTO account VALUES (3,'user_c','User C', now());
INSERT INTO account VALUES (4,'user_d','User D', now());



    CREATE TABLE direct_message(
        id bigint UNSIGNED NOT NULL auto_increment,
        message_from_id bigint UNSIGNED NOT NULL,
        message_to_id bigint UNSIGNED NOT NULL,
        message_text varchar(200) NOT NULL,
        date datetime NOT NULL,
        CONSTRAINT pkey_dm_id PRIMARY KEY(id),
        CONSTRAINT fk_direct_message_to FOREIGN KEY (message_to_id) REFERENCES account(id),
        CONSTRAINT fk_message_from FOREIGN KEY (message_from_id) REFERENCES account(id)
    );

    INSERT INTO direct_message VALUE(1, 1, 2, 'hello', now());
    INSERT INTO direct_message VALUE(2, 2, 1, 'hello back', now());
    INSERT INTO direct_message VALUE(3, 1, 3, 'hello', now());
    INSERT INTO direct_message VALUE(4, 3, 1, 'hello back', now());
    INSERT INTO direct_message VALUE(5, 1, 4, 'hello', now());
    INSERT INTO direct_message VALUE(6, 4, 1, 'hello back', now());
    INSERT INTO direct_message VALUE(7, 2, 4, 'hello', now());
    INSERT INTO direct_message VALUE(8, 4, 2, 'hello back', now());

Solution

  • I think you want:

    select dm.*
    from direct_message dm
    where @userid in (dm.message_from_id, dm.message_to_id) and
          dm.date = (select max(dm2.date)
                     from direct_message dm2
                     where (dm2.message_from_id, dm2.message_to_id) in ( (dm.message_from_id, dm.message_to_id), (dm.message_to_id, dm.message_from_id) )
                    );
    

    EDIT:

    It seems strange to have different messages with exactly the same date/time stamp, but you can distinguish them using the id:

    select dm.*
    from direct_message dm
    where 1 in (dm.message_from_id, dm.message_to_id) and
          dm.id = (select dm2.id
                   from direct_message dm2
                   where (dm2.message_from_id, dm2.message_to_id) in ( (dm.message_from_id, dm.message_to_id), (dm.message_to_id, dm.message_from_id) )
                   order by dm2.date desc
                   limit 1
                  );