SQL query tested in SQL 4 CDS to fetch all emails removed by Microsoft in the issue 5352440 (Reference DV1121532).
WITH AuditData AS (
SELECT
audit.objectid,
audit.objectidname,
-- We get the prevEmail from the shredded JSON data below
json_attributes.oldValue AS 'prevEmail',
contact.emailaddress1,
audit.useridname,
audit.createdon
FROM
audit
INNER JOIN
contact ON audit.objectid = contact.contactid
-- Use CROSS APPLY to parse the JSON array for each row in the audit table
CROSS APPLY OPENJSON(audit.changedata, '$.changedAttributes')
WITH (
logicalName NVARCHAR(128) '$.logicalName',
oldValue NVARCHAR(MAX) '$.oldValue'
) AS json_attributes
WHERE
audit.createdon >= '2025-07-20'
AND audit.attributemask LIKE '%42%'
AND audit.objectidtype = 'contact'
AND (contact.emailaddress1 = '' OR contact.emailaddress1 IS NULL)
-- Filter the shredded JSON to find only the email address change
AND json_attributes.logicalName = 'emailaddress1'
),
RankedAudits AS (
SELECT
objectid AS 'contactId',
objectidname AS 'contactName',
prevEmail,
emailaddress1 AS 'Email',
useridname AS 'modifiedBy',
createdon,
ROW_NUMBER() OVER(PARTITION BY objectid ORDER BY createdon DESC) as 'rowNum'
FROM
AuditData
)
SELECT
contactId,
contactName,
prevEmail,
Email,
modifiedBy,
createdon
FROM
RankedAudits
WHERE
prevEmail IS NOT NULL
AND prevEmail != '' -- Also good to exclude empty strings
AND rowNum = 1
ORDER BY
createdon DESC;This query can also be used after MS rolls out the mitigation solution, so any emails let behind will be retrieved.
It can takes a while to finish running, but it's a one-time job. So make sure that Stop query execution after is set to 0 in the Settings:

[Edit] Based on the previous query, I developed a new generic one to recover the emails erased from the contacts affected.
-- Step 1: Create a temporary table to store the results of your audit query.
-- This isolates the data we need to work with.
CREATE TABLE #EmailUpdates (
contactId UNIQUEIDENTIFIER,
contactName NVARCHAR(255),
prevEmail NVARCHAR(255),
Email NVARCHAR(255),
modifiedBy NVARCHAR(255),
createdon DATETIME
);
-- Step 2: Insert the data from your query into the temporary table.
WITH AuditData AS (
SELECT
audit.objectid,
audit.objectidname,
-- We get the prevEmail from the shredded JSON data below
json_attributes.oldValue AS 'prevEmail',
contact.emailaddress1,
audit.useridname,
audit.createdon
FROM
audit
INNER JOIN
contact ON audit.objectid = contact.contactid
-- Use CROSS APPLY to parse the JSON array for each row in the audit table
CROSS APPLY OPENJSON(audit.changedata, '$.changedAttributes')
WITH (
logicalName NVARCHAR(128) '$.logicalName',
oldValue NVARCHAR(MAX) '$.oldValue'
) AS json_attributes
WHERE
audit.createdon >= '2025-07-20'
AND audit.attributemask LIKE '%42%'
AND audit.objectidtype = 'contact'
AND (contact.emailaddress1 = '' OR contact.emailaddress1 IS NULL)
-- Filter the shredded JSON to find only the email address change
AND json_attributes.logicalName = 'emailaddress1'
),
RankedAudits AS (
SELECT
objectid AS 'contactId',
objectidname AS 'contactName',
prevEmail,
emailaddress1 AS 'Email',
useridname AS 'modifiedBy',
createdon,
ROW_NUMBER() OVER(PARTITION BY objectid ORDER BY createdon DESC) as 'rowNum'
FROM
AuditData
)
INSERT INTO #EmailUpdates (contactId, contactName, prevEmail, Email, modifiedBy, createdon)
SELECT
contactId,
contactName,
prevEmail,
Email,
modifiedBy,
createdon
FROM
RankedAudits
WHERE
prevEmail IS NOT NULL
AND prevEmail != '' -- Also good to exclude empty strings
AND rowNum = 1;
-- Step 3: Preview the changes.
-- Run the SELECT statement below to see the current data versus the proposed new data.
SELECT
c.contactid,
c.emailaddress1 AS 'CurrentEmail',
upd.prevEmail AS 'NewEmail'
FROM
contact c
INNER JOIN
#EmailUpdates upd ON c.contactid = upd.contactId;
-- Step 4: If you are satisfied with the preview, run the UPDATE statement below.
/* -- Uncomment the following lines to execute the update
UPDATE
c
SET
emailaddress1 = upd.prevEmail
FROM
contact c
INNER JOIN
#EmailUpdates upd ON c.contactid = upd.contactId;
-- Check the number of rows affected to confirm the update was successful.
-- We must convert the number @@ROWCOUNT to a string to print it.
PRINT CONVERT(VARCHAR, @@ROWCOUNT) + ' rows updated.';
-- Step 5: Clean up the temporary table.
DROP TABLE #EmailUpdates;
*/To run this query, you'll need to uncheck the Prevent UPDATE without WHERE option in the Settings.
If you're not used to working with SQL (or SQL 4 CDS), know that you can select the portion of the code you want to run. So after executing the first steps of the code, when you uncomment the last part, you can just select the remaining part before hitting Execute (or F5), just like this:

Don't stop learning!
