Question interested me, so I took a shot. Not tested, so recommend backing up the wp_posts
table before running (though you should do so regardless!)
As mentioned in the comments, don't forget to replace "domain.com" in the regular expression with your own.
require 'wp-load.php'; // Only needed if *not* a plugin.
function out_with_the_old( $match ) {
global $links;
if ( ! isset( $links[ $match[1] ] ) ) { // Cache to save subsequent hits.
$links[ $match[1] ] = get_permalink( $match[1] );
clean_post_cache( $match[1] );
}
return $links[ $match[1] ] ? $links[ $match[1] ] : $match[0]; // Just fallback to original URL if new permalink void (for whatever reason).
}
$links = array();
$posts = get_posts(
array(
'posts_per_page' => -1,
'post_status' => 'any',
'post_type' => 'any',
'fields' => 'ids', // Getting *everything* will most likely hit the memory limit.
)
);
// Loop over each post & replace all old instances of the permalink with the new one.
foreach ( $posts as $post_id ) {
$save = array();
$post = get_post( $post_id );
foreach ( array( 'post_content', 'post_excerpt' ) as $field ) {
// Replace "domain\.com" with your domain name, minus "www." (escape dots with a backslash).
// The regex accommodates for various forms of the same URL, including SSL, no "www" & no index.php.
$text = preg_replace_callback( "!https?://(?:www\.)?domain\.com/(?:index\.php)?\?(?:p|post_id)=([0-9]+)!", 'out_with_the_old', $post->$field );
if ( $text !== $post->$field )
$save[ $field ] = $text;
}
if ( $save )
$wpdb->update(
$wpdb->posts,
$save,
array(
'ID' => $post_id,
),
'%s',
'%d'
);
clean_post_cache( $post ); // Otherwise we might soon hit a memory limit.
}
You could turn this into a plugin, but the cheap-n-easy way would be to dump into a script, FTP to the same directory as WordPress, then run in a browser.