0

I am running MySQL Community Server v5.7.40 and have 2 tables: "customers" and "events" created as follows:

create table customers (customer_id int, name varchar(100), primary key (customer_id));
create table events (event_id int, customer_id int, event_date datetime, event_type varchar(100), primary key (event_id), foreign key (customer_id) references customers(customer_id));

with data structured as below:

CUSTOMERS

customer_id name
1 Cust1
2 Cust2
3 Cust3

EVENTS

event_Id customer_Id event_date event_type
1 1 2022-11-30 10:00:00 100m
2 1 2022-11-30 12:00:00 High Jump
3 1 2022-11-30 12:00:00 Long Jump
4 2 2022-11-29 11:00:00 400m
5 2 2022-11-28 09:00:00 800m
6 3 2022-11-27 07:00:00 Triple Jump

I have a requirement to create a view where each customer record will be joined to the events table on the latest event record (ordered by event_date, for which there may be duplicates - selecting the first is acceptable here) for that customer, i.e.:

customer_id customer_name latest_event_id latest_event_date latest_event_type
1 Cust1 2 2022-11-30 12:00:00 High Jump
2 Cust2 4 2022-11-29 11:00:00 400m
3 Cust3 6 2022-11-27 07:00:00 Triple Jump

(inner or left join are both acceptable - customer records with no matching events will eventually be ignored anyway).

For newer versions of MySQL, I can use "ROW_NUMBER() OVER" + PARTITION BY (similar to Oracle) such as below to achieve this:

WITH ranked_events AS (
  SELECT e.*, ROW_NUMBER() OVER (PARTITION BY Customer_Id ORDER BY Event_Date DESC) AS rn
  FROM events AS e
)
SELECT c.customer_id,
        c.name as customer_name,
        re.event_id as latest_event_id,
        re.event_date as latest_event_date,
        re.event_type as latest_event_type
FROM customers c, ranked_events re 
WHERE c.customer_id = re.customer_id and re.rn = 1;

However, for the older version I am stuck using this will not work. I can currently achieve the same result via a select statement using a row_number variable incremented for each row as follows:

with ordered_events as (
        select e.*, (@row_number := @row_number + 1) AS rn
        from events e,
        (SELECT @row_number := 0) AS x
        order by customer_id, event_date desc
)
select c.customer_id,
        c.name as customer_name,
        b.event_id as latest_event_id,
        b.event_date as latest_event_date,
        b.event_type as latest_event_type
from customers c, (
        select customer_id, min(rn) as latest_event_rn
        from ordered_events
        group by customer_id) a, 
    ordered_events b
where a.customer_id = b.customer_id 
    and a.customer_id = c.customer_id
    and a.latest_event_rn = b.rn;

If I try to add create or replace view customer_latest_events as to the above syntax and compile, I get the error

Error Code: 1351. View's SELECT contains a variable or parameter

Is there a way to achieve the desired outcome in a view for this version of MySQL?

Update

As has been pointed out, CTE syntax is also not valid in v5.7.40 - I had wrongly tested that in a v8 environment. The query below achieves the same result without the CTE, but also fails due to the inclusion of variables in the view's SELECT statement:

create or replace view customer_latest_events as
select c.customer_id,
        c.name as customer_name,
        b.event_id as latest_event_id,
        b.event_date as latest_event_date,
        b.event_type as latest_event_type
from customers c, (
        select customer_id, min(rn) as latest_event_rn
        from (
                select e.*, (@row_number := @row_number + 1) AS rn
                from events e,
                (select @row_number := 0) AS x
                order by customer_id, event_date desc
        ) oe
        group by customer_id) a, (
        select e.*, (@row_number2 := @row_number2 + 1) AS rn
        from events e,
        (select @row_number2 := 0) AS x
        order by customer_id, event_date desc
) b
where a.customer_id = b.customer_id 
    and a.customer_id = c.customer_id
    and a.latest_event_rn = b.rn;

Update 2

This question has been marked as a duplicate of Retrieving the last record in each group - MySQL. However, there are 2 issues present in this question that are not addressed by any of the answers given there.

  1. The CTE syntax with x as ( is not valid for the version of MySQL I have specified and thus will not work
  2. I specifically require a view. The workaround use of variables to cater for v5.7 works fine in a select statement only, but fails specifically when used in a create view statement (which is not something addresses in that question or by any of the answers)
nick_j_white
  • 534
  • 6
  • 27
  • Hmm. CTEs (ie with) are also not available until version 8.. – P.Salmon Mar 06 '23 at 16:20
  • Ah you're right! I originally had a setup where the CTE was included as 2 separate subqueries... but for this to work I had to use 2 differently named variables in each subquery. In my infinite wisdom I replaced these with the CTE and tested in the v8 environment instead of the 5.7. Will update the question to reflect this. – nick_j_white Mar 06 '23 at 16:28

0 Answers0