A bit late to the party, But I had the same problem recently. One of my clients has iteratively upgraded from 1.4 up to 1.9 and I have found some orphaned records.
My approach was to check if there are any first.
SELECT COUNT(*) FROM `sales_flat_order_grid` WHERE `entity_id` NOT IN (SELECT `entity_id` FROM `sales_flat_order`);
Then, delete them:
DELETE FROM `sales_flat_order_grid` WHERE `entity_id` NOT IN (SELECT `entity_id` FROM `sales_flat_order`);
You can find all orphaned records like this:
// `-s` means no clutter around the result, `-N` means no header.
> mysql -s -N -umy_user -p
SELECT
CONCAT(
'SELECT COUNT(*) FROM `', TABLE_NAME, '` ',
'WHERE `', COLUMN_NAME, '` NOT IN (',
'SELECT `', REFERENCED_COLUMN_NAME, '` FROM `', REFERENCED_TABLE_NAME, '`',
');'
)
FROM
INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE
REFERENCED_TABLE_NAME IS NOT NULL
AND
CONSTRAINT_SCHEMA = '__PUT_DATABASE_NAME_HERE__'
;
Then you can generate this beauty and finish the job.
SELECT
CONCAT(
'DELETE FROM `', TABLE_NAME, '` ',
'WHERE `', COLUMN_NAME, '` NOT IN (',
'SELECT `', REFERENCED_COLUMN_NAME, '` FROM `', REFERENCED_TABLE_NAME, '`',
');'
)
FROM
INFORMATION_SCHEMA.KEY_COLUMN_USAGE
WHERE
REFERENCED_TABLE_NAME IS NOT NULL
AND
CONSTRAINT_SCHEMA = '__PUT_DATABASE_NAME_HERE__'
;
Kudos to kozie for this suggestion; and
dfelton for the orignal idea.
You could go one more step and create a batch file that iterates over the result using --execute="DELETE ..."
, but that's definitely out of scope of this answer.