Searching a custom WP table and displaying results in an HTML table

There’s a lot going on in the code, so I rewrote your main function with some comments. Overview of notes:

$address_table is superfluous, and also probably not what you wanted

You only use this variable once, so it’s safe to just in-line it in your query. Also, your use of $wpdb->prefix expands $address_table to the value “wp_wp_products“, probably not what you want. I left it in the code with some notes, but you don’t need any of that in your final method, just use what’s in the $query.

Early Return

Returning when your conditions fail, instead of doing logic when they pass (the is_search() && isset( $_GET['s'] ) block), makes your code more readable.

Escaping input

I know you were using $wpdb->prepare eventually, but it doesn’t hurt to sanitize input as soon as you plan to use it. This way, if you pass $search somewhere else later, it will hopefully be safer than it was when it came to your function. Also note the use of $wpdb->esc_like() to prepare your search for use in a LIKE clause.

function search_it() {

    // Sanitize user input early, especially if assigning to a variable like later.
    $search = filter_input( INPUT_GET, 's', FILTER_SANITIZE_STRING );

    // Early returns make your code more readable.
    if ( ! is_search() || empty( $search ) ) {
        return array();
    }

    global $wpdb;

    /**
     * Some things about `$address_table`:
     * - You don't need double quotes if you're concatenating
     * - You only use it in your query, see below where I incorporate it inline.
     * - $wpdb->prefix is usually "wp_", so you're table comes out to "wp_wp_products" - 
     * I don't think that's what you want.
     */
    $address_table = $wpdb->prefix . 'products';

    // Double quote would look like this: 
    // $address_table = "{$wpdb->prefix}products";

    // https://codex.wordpress.org/Class_Reference/wpdb/esc_like
    $search = $wpdb->esc_like( $search );
    $search = "%{$search}%";

    // Why not prepare the whole query"?
    // Also, you don't even need $address_table
    $query = "SELECT * FROM {$wpdb->prefix}products WHERE SKU LIKE %s OR Details LIKE %s OR Page Like %s";
    $query = $wpdb->prepare( $query, $search, $search, $search );
    return $wpdb->get_results();
}

Please let me know if you have any questions.

Edit: I accidentally typed filter_validate instead of filter_input

To address the second part of your code, it looks like the inner for loop is using $start and $end, which aren’t defined. You also don’t need another loop since you’re already looping your results. Try this instead:

$cities = search_it();
if ( ! empty( $cities ) ) {
    echo '<h1>Catalogue Search Results:</h1>';
    echo "<table width="100%" align='center' border="3px solid grey">";
    echo "<tr>";
    echo "<th style="background: #B9C9FE;">Part Number</th>";
    echo "<th style="background: #B9C9FE;">Description</th>";
    echo "<th style="background: #B9C9FE;">Page</th>";
    echo "</tr>";
    echo "<tbody>";


    foreach( $cities AS $city ) {
        echo "<tr>";
        echo "<td>{$city->SKU}</td>";
        echo "<td>{$city->description}</td>";
        echo "<td>{$city->Page}</td>";
        echo "</tr>";
    }

    echo "</tbody>";
    echo "</table>"; 
}