I need to get the initial state as well as the latest state from a MySQL database. This is over two tables:
customer
id | name | surname | dob | telephone | |
---|---|---|---|---|---|
10 | Steve | Bobbly | 01-01-1970 | uvw@xyz.com | 0123456789 |
15 | James | Bond | 01-01-1950 | 007@bond.com | 0101010999 |
audit_log
id | entity_id | property | old_value | new_value |
---|---|---|---|---|
1 | 10 | name | John | Steve |
2 | 10 | abc@def.com | uvw@xyz.com | |
3 | 10 | telephone | 0123456789 |
What I expect is output like this:
id | name | surname | dob | telephone | |
---|---|---|---|---|---|
10 | Steve | Bobbly | 01-01-1970 | uvw@xyz.com | 0123456789 |
10_1 | John | Bobbly | 01-01-1970 | abc@def.com | |
15 | James | Bond | 01-01-1950 | 007@bond.com | 0101010999 |
I initially had a PHP script that runs through all the customer
rows, and then matches them to the audit_log
rows and generate output from there, but the speed is EXTREMELY slow and resource intensive.
Would something like this be possible directly in MySQL, and how would I do it?
I've added additional rows to the customer
and the output tables. The output table needs to contain all rows in customer
, as well as a copy of the initial row, built from audit_log
.
Try the following:
SET @rn=0;
SET @cust=null;
SET @pr=null;
SELECT id, name, surname, dob, email, telephone
FROM customer
UNION ALL
(
SELECT CONCAT(T.id, '_', D.rownum) id,
COALESCE(MAX(CASE D.property WHEN 'name' THEN D.old_value END), MAX(T.name)) name,
COALESCE(MAX(CASE D.property WHEN 'surname' THEN D.old_value END), MAX(T.surname)) surname,
COALESCE(MAX(CASE D.property WHEN 'dob' THEN D.old_value END), MAX(T.dob)) dob,
COALESCE(MAX(CASE D.property WHEN 'email' THEN D.old_value END), MAX(T.email)) email,
COALESCE(MAX(CASE D.property WHEN 'telephone' THEN D.old_value END), MAX(T.telephone)) telephone
FROM customer T
JOIN
(
SELECT id, entity_id, property, old_value, new_value,
IF(@cust <> entity_id OR @pr <> property, @rn:=1, @rn:=@rn+1) rownum,
@cust:=entity_id, @pr:=property
FROM audit_log
ORDER BY entity_id, property, id
) D
ON T.ID = D.entity_id
WHERE D.rownum=1
GROUP BY T.id, D.rownum
)
ORDER BY id
See a demo.
This query simulates ROW_NUMBER() OVER (PARTITION BY entity_id, property ORDER BY id)
for the audit_log table to get the initial value for each customer/ property (where rownum = 1) .
The COALESCE
is used to get the value of a property from the customers table, if this property is not changed, i.e. the name is changed but the email is not, then for email get the lastest value (in this case the latest=initial, which is T.email in this query).