How to override a query and display specific page by ID?

You could do this a few different ways, depending on what’s happening in the template and how you are able to identify which page you want to override.

1. Simple method with pre_get_posts:

This will mostly work, but the queried object will still contain the old page, however, using get_the_ID() in the template to load page data will return the correct ID and enable you to load data from the target page.

function wpd_switch_page( $query ){
    if( $query->is_page( 42 ) && $query->is_main_query() ) {
        $query->set( 'page_id', 23 );
    }
}
add_action( 'pre_get_posts', 'wpd_switch_page' );

2. Using simple request filter with page name:

This will correctly set the queried object, so body classes and such will contain the correct ID, get_queried_object_ID() will return the ID of the target page. In this case though, you have to know the pagename of the source and target pages, not ID:

function alter_the_query( $request ) {
    if( isset( $request['pagename'] ) && 'source-page' == $request['pagename'] ){
        $request['pagename'] = 'target-page';
    }
    return $request;
}
add_filter( 'request', 'alter_the_query' );

3. Using request filter with page ID:

If you parse the query first, you can check the source page ID instead of name. You have the option of then setting pagename as above, or you do it by page ID if you’d like:

function alter_the_query( $request ) {
    $dummy_query = new WP_Query();
    $dummy_query->parse_query( $request );

    if ( $dummy_query->is_page( 42 ) ){
        unset( $request['pagename'] );
        $request['page_id'] = 23;
    }
    return $request;
}
add_filter( 'request', 'alter_the_query' );