Skip to main content
added 320 characters in body
Source Link
shudder
  • 686
  • 7
  • 8
    // Find item ids with future booking date to ease next (slow) query.
    // If string format is known and consistent think of filtering with mysql STR_TO_DATE() function.
    // I will use php strtotime() to filter them instead (might be big chunk of data though).
    $items = $wpdb->get_results("
        SELECT m1.order_item_id AS id, m1.meta_value AS date, m2.meta_value AS time
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m1
        INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta m2 ON m2.order_item_id = m1.order_item_id
        WHERE m1.meta_key = 'Booking Date'
          AND m2.meta_key = 'Booking Time'
        ORDER BY m1.order_item_id ASC
    ");

    $now = time();
    $data = array();
    foreach ($items as $item) {
        $timestamp = strtotime($item->date . ' ' . $item->time);
        if ($timestamp > $now) {
            // $timestamp is used only for sorting and will be unset later,
            // may be used instead 'date' and 'time' fields though.
            $data[$item->id]['timestamp'] = $timestamp;
        }
    }

    // Initially sorted array with ids as key.
    // Other data will be inserted here from next query
    // thus the order will be maintained.
    asort($data);

    // list of ids that match future timestamp
    // can't simply LIMIT to 10 with array_slice() because upcoming conditions could filter them out
    // higher limit also uncertain, but maybe worth a try (loss vs. propability) 
    $item_list = implode(', ', array_keys($data));

    // helper array for fast check and key assign
    $meta_keys = array(
        'Booking Date' => 'date',
        'Booking Time' => 'time',
        'First Name - First Name' => 'fname',
        'Church Information - Church Name' => 'church',
        'Church Information - City' => 'city',
        'Church Information - State' => 'state'
    );

    // Slow query, but only one - nested loop queries are killers.
    $item_meta = $wpdb->get_results("
        SELECT m.order_item_id, m.meta_key, m.meta_value
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m
        INNER JOIN {$wpdb->prefix}woocommerce_order_items i ON m.order_item_id = i.order_item_id
        INNER JOIN {$wpdb->prefix}posts p ON p.ID = i.order_id
        INNER JOIN {$wpdb->prefix}term_relationships r ON r.object_id = p.ID
        WHERE r.term_taxonomy_id = 11
          AND p.post_type = 'shop_order'
          AND p.post_status = 'publish'
          AND m.order_item_id IN ({$item_list})
    ");

    $num = 0; //found items counter   
    foreach ($item_meta as $item) {
        if (!isset($meta_keys[$item->meta_key])) { continue; }

        $id  = $item->order_item_id;
        $key = $meta_keys[$item->meta_key];

        if (!isset($data[$id]['num']) {
            $data[$id]['num'] = $num++;
        }   
        $data[$id][$key] = $item->meta_value;
    }

    $count = 0;
    //$page (int) page number (display last page with results if too high)
    $start = ($num - 11 < $page * 10) ? $num - 11 : $page * 10;
    if ($start < 0) { $start = 0; }
    foreach ($data as $item) {
        // skip results not found by second query
        if (!isset($item['time']) || $item['num'] < $start) { continue; }

        $time = $item['time'];
        $date = $item['date'];
        unset($item['time'], $item['date'], $item['timestamp']);
        $details = implode(',<br>', $item);
?>
    <div class="wc-upcoming-booking">
        <div class="wc-upcoming-time">
            <span class="upcoming-hour"><?php echo $time; ?></span>
            <span class="upcoming-date"><?php echo $date; ?></span>
        </div>
        <div class="wc-upcoming-details">
            <?php echo $details; ?>
        </div>
    </div>
<?php
        $count++;
        if ($count == 10) { break; }

    }
    // Find item ids with future booking date to ease next (slow) query.
    // If string format is known and consistent think of filtering with mysql STR_TO_DATE() function.
    // I will use php strtotime() to filter them instead (might be big chunk of data though).
    $items = $wpdb->get_results("
        SELECT m1.order_item_id AS id, m1.meta_value AS date, m2.meta_value AS time
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m1
        INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta m2 ON m2.order_item_id = m1.order_item_id
        WHERE m1.meta_key = 'Booking Date'
          AND m2.meta_key = 'Booking Time'
        ORDER BY m1.order_item_id ASC
    ");

    $now = time();
    $data = array();
    foreach ($items as $item) {
        $timestamp = strtotime($item->date . ' ' . $item->time);
        if ($timestamp > $now) {
            // $timestamp is used only for sorting and will be unset later,
            // may be used instead 'date' and 'time' fields though.
            $data[$item->id]['timestamp'] = $timestamp;
        }
    }

    // Initially sorted array with ids as key.
    // Other data will be inserted here from next query
    // thus the order will be maintained.
    asort($data);

    // list of ids that match future timestamp
    // can't simply LIMIT to 10 with array_slice() because upcoming conditions could filter them out
    // higher limit also uncertain, but maybe worth a try (loss vs. propability) 
    $item_list = implode(', ', array_keys($data));

    // helper array for fast check and key assign
    $meta_keys = array(
        'Booking Date' => 'date',
        'Booking Time' => 'time',
        'First Name - First Name' => 'fname',
        'Church Information - Church Name' => 'church',
        'Church Information - City' => 'city',
        'Church Information - State' => 'state'
    );

    // Slow query, but only one - nested loop queries are killers.
    $item_meta = $wpdb->get_results("
        SELECT m.order_item_id, m.meta_key, m.meta_value
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m
        INNER JOIN {$wpdb->prefix}woocommerce_order_items i ON m.order_item_id = i.order_item_id
        INNER JOIN {$wpdb->prefix}posts p ON p.ID = i.order_id
        INNER JOIN {$wpdb->prefix}term_relationships r ON r.object_id = p.ID
        WHERE r.term_taxonomy_id = 11
          AND p.post_type = 'shop_order'
          AND p.post_status = 'publish'
          AND m.order_item_id IN ({$item_list})
    ");

    foreach ($item_meta as $item) {
        if (!isset($meta_keys[$item->meta_key])) { continue; }

        $id  = $item->order_item_id;
        $key = $meta_keys[$item->meta_key];

        $data[$id][$key] = $item->meta_value;
    }

    $count = 0;
    foreach ($data as $item) {
        // skip results not found by second query
        if (!isset($item['time'])) { continue; }

        $time = $item['time'];
        $date = $item['date'];
        unset($item['time'], $item['date'], $item['timestamp']);
        $details = implode(',<br>', $item);
?>
    <div class="wc-upcoming-booking">
        <div class="wc-upcoming-time">
            <span class="upcoming-hour"><?php echo $time; ?></span>
            <span class="upcoming-date"><?php echo $date; ?></span>
        </div>
        <div class="wc-upcoming-details">
            <?php echo $details; ?>
        </div>
    </div>
<?php
        $count++;
        if ($count == 10) { break; }

    }
    // Find item ids with future booking date to ease next (slow) query.
    // If string format is known and consistent think of filtering with mysql STR_TO_DATE() function.
    // I will use php strtotime() to filter them instead (might be big chunk of data though).
    $items = $wpdb->get_results("
        SELECT m1.order_item_id AS id, m1.meta_value AS date, m2.meta_value AS time
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m1
        INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta m2 ON m2.order_item_id = m1.order_item_id
        WHERE m1.meta_key = 'Booking Date'
          AND m2.meta_key = 'Booking Time'
        ORDER BY m1.order_item_id ASC
    ");

    $now = time();
    $data = array();
    foreach ($items as $item) {
        $timestamp = strtotime($item->date . ' ' . $item->time);
        if ($timestamp > $now) {
            // $timestamp is used only for sorting and will be unset later,
            // may be used instead 'date' and 'time' fields though.
            $data[$item->id]['timestamp'] = $timestamp;
        }
    }

    // Initially sorted array with ids as key.
    // Other data will be inserted here from next query
    // thus the order will be maintained.
    asort($data);

    // list of ids that match future timestamp
    // can't simply LIMIT to 10 with array_slice() because upcoming conditions could filter them out
    // higher limit also uncertain, but maybe worth a try (loss vs. propability) 
    $item_list = implode(', ', array_keys($data));

    // helper array for fast check and key assign
    $meta_keys = array(
        'Booking Date' => 'date',
        'Booking Time' => 'time',
        'First Name - First Name' => 'fname',
        'Church Information - Church Name' => 'church',
        'Church Information - City' => 'city',
        'Church Information - State' => 'state'
    );

    // Slow query, but only one - nested loop queries are killers.
    $item_meta = $wpdb->get_results("
        SELECT m.order_item_id, m.meta_key, m.meta_value
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m
        INNER JOIN {$wpdb->prefix}woocommerce_order_items i ON m.order_item_id = i.order_item_id
        INNER JOIN {$wpdb->prefix}posts p ON p.ID = i.order_id
        INNER JOIN {$wpdb->prefix}term_relationships r ON r.object_id = p.ID
        WHERE r.term_taxonomy_id = 11
          AND p.post_type = 'shop_order'
          AND p.post_status = 'publish'
          AND m.order_item_id IN ({$item_list})
    ");

    $num = 0; //found items counter   
    foreach ($item_meta as $item) {
        if (!isset($meta_keys[$item->meta_key])) { continue; }

        $id  = $item->order_item_id;
        $key = $meta_keys[$item->meta_key];

        if (!isset($data[$id]['num']) {
            $data[$id]['num'] = $num++;
        }   
        $data[$id][$key] = $item->meta_value;
    }

    $count = 0;
    //$page (int) page number (display last page with results if too high)
    $start = ($num - 11 < $page * 10) ? $num - 11 : $page * 10;
    if ($start < 0) { $start = 0; }
    foreach ($data as $item) {
        // skip results not found by second query
        if (!isset($item['time']) || $item['num'] < $start) { continue; }

        $time = $item['time'];
        $date = $item['date'];
        unset($item['time'], $item['date'], $item['timestamp']);
        $details = implode(',<br>', $item);
?>
    <div class="wc-upcoming-booking">
        <div class="wc-upcoming-time">
            <span class="upcoming-hour"><?php echo $time; ?></span>
            <span class="upcoming-date"><?php echo $date; ?></span>
        </div>
        <div class="wc-upcoming-details">
            <?php echo $details; ?>
        </div>
    </div>
<?php
        $count++;
        if ($count == 10) { break; }

    }
Notice removed Needs detailed answers by 200_success
added 1777 characters in body
Source Link
shudder
  • 686
  • 7
  • 8
    // Find first item idids with future booking date to ease next (slow) query.
    // If string format is known and consistent think of filtering with mysql STR_TO_DATE() function.
    // I will use php strtotime() to filter them instead (might be big chunk of data though).
    $items = $wpdb->get_results("
        SELECT m1.order_item_id AS id, m1.meta_value AS date, m2.meta_value AS time
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m1
        INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta m2 ON m2.order_item_id = m1.order_item_id
        WHERE m1.meta_key = 'Booking Date'
          AND m2.meta_key = 'Booking Time'
        ORDER BY m1.order_item_id ASC
    ");

    $now = time();
    $data = array();
    foreach ($items as $item) {
        $timestamp = strtotime($item->date . ' ' . $item->time);
        if ($timestamp > $now) {
            // $timestamp is used only for sorting and will be unset later,
            // may be used instead 'date' and 'time' fields though.
            $data[$item->id]['timestamp'] = $timestamp;
        }
    }

    // Initially sorted array with ids as key.
    // Other data will be inserted here from next query
    // thus the order will be maintained.
    asort($data);

    // list of ids that match future timestamp
    // (can't simply LIMIT to 10 with array_slice() because upcoming conditions could filter them out
    // higher limit also uncertain, but maybe worth a try (loss vs. propability) 
    $item_list = implode(', ', array_keys($data));

    // helper array for fast check and key assign
    $meta_keys = array(
        'Booking Date' => 'date',
        'Booking Time' => 'time',
        'First Name - First Name' => 'fname',
        'Church Information - Church Name' => 'church',
        'Church Information - City' => 'city',
        'Church Information - State' => 'state'
    );

    // Slow query, but only one - nested loop queries are killers.
    $item_meta = $wpdb->get_results("
        SELECT m.order_item_id, m.meta_key, m.meta_value
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m
        INNER JOIN {$wpdb->prefix}woocommerce_order_items i ON m.order_item_id = i.order_item_id
        INNER JOIN {$wpdb->prefix}posts p ON p.ID = i.order_id
        INNER JOIN {$wpdb->prefix}term_relationships r ON r.object_id = p.ID
        WHERE r.term_taxonomy_id = 11
          AND p.post_type = 'shop_order'
          AND p.post_status = 'publish'
          AND m.order_item_id IN ({$item_list})
    ");

    foreach ($item_meta as $item) {
        if (!isset($meta_keys[$item->meta_key])) { continue; }

        $id  = $item->order_item_id;
        $key = $meta_keys[$item->meta_key];

        $data[$id][$key] = $item->meta_value;
    }

    $count = 0;
    foreach ($data as $item) {
        // skip results not found by second query
        if (!isset($item['time'])) { continue; }

        $time = $item['time'];
        $date = $item['date'];
        unset($item['time'], $item['date'], $item['timestamp']);
        $details = implode(',<br>', $item);
?>
    <div class="wc-upcoming-booking">
        <div class="wc-upcoming-time">
            <span class="upcoming-hour"><?php echo $time; ?></span>
            <span class="upcoming-date"><?php echo $date; ?></span>
        </div>
        <div class="wc-upcoming-details">
            <?php echo $details; ?>
        </div>
    </div>
<?php
        $count++;
        if ($count == 10) { break; }

    }
    // Find first item id with future booking date to ease next (slow) query.
    // If string format is known and consistent think of filtering with mysql STR_TO_DATE() function.
    // I will use php strtotime() to filter them instead.
    $items = $wpdb->get_results("
        SELECT m1.order_item_id AS id, m1.meta_value AS date, m2.meta_value AS time
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m1
        INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta m2 ON m2.order_item_id = m1.order_item_id
        WHERE m1.meta_key = 'Booking Date'
          AND m2.meta_key = 'Booking Time'
        ORDER BY m1.order_item_id ASC
    ");

    $now = time();
    $data = array();
    foreach ($items as $item) {
        $timestamp = strtotime($item->date . ' ' . $item->time);
        if ($timestamp > $now) {
            // $timestamp is used only for sorting and will be unset later,
            // may be used instead 'date' and 'time' fields though.
            $data[$item->id]['timestamp'] = $timestamp;
        }
    }

    // Initially sorted array with ids as key.
    // Other data will be inserted here from next query
    // thus the order will be maintained.
    asort($data);

    // list of ids that match future timestamp
    // (can't simply LIMIT to 10 because upcoming conditions could filter them out)
    $item_list = implode(', ', array_keys($data));

    // helper array for fast check and key assign
    $meta_keys = array(
        'Booking Date' => 'date',
        'Booking Time' => 'time',
        'First Name - First Name' => 'fname',
        'Church Information - Church Name' => 'church',
        'Church Information - City' => 'city',
        'Church Information - State' => 'state'
    );

    // Slow query, but only one - nested loop queries are killers.
    $item_meta = $wpdb->get_results("
        SELECT m.order_item_id, m.meta_key, m.meta_value
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m
        INNER JOIN {$wpdb->prefix}woocommerce_order_items i ON m.order_item_id = i.order_item_id
        INNER JOIN {$wpdb->prefix}posts p ON p.ID = i.order_id
        INNER JOIN {$wpdb->prefix}term_relationships r ON r.object_id = p.ID
        WHERE r.term_taxonomy_id = 11
          AND p.post_type = 'shop_order'
          AND p.post_status = 'publish'
          AND m.order_item_id IN ({$item_list})
    ");

    foreach ($item_meta as $item) {
        if (!isset($meta_keys[$item->meta_key])) { continue; }

        $id  = $item->order_item_id;
        $key = $meta_keys[$item->meta_key];

        $data[$id][$key] = $item->meta_value;
    }

    $count = 0;
    foreach ($data as $item) {
        // skip results not found by second query
        if (!isset($item['time'])) { continue; }

        $time = $item['time'];
        $date = $item['date'];
        unset($item['time'], $item['date'], $item['timestamp']);
        $details = implode(',<br>', $item);
?>
    <div class="wc-upcoming-booking">
        <div class="wc-upcoming-time">
            <span class="upcoming-hour"><?php echo $time; ?></span>
            <span class="upcoming-date"><?php echo $date; ?></span>
        </div>
        <div class="wc-upcoming-details">
            <?php echo $details; ?>
        </div>
    </div>
<?php
        $count++;
        if ($count == 10) { break; }

    }
    // Find item ids with future booking date to ease next (slow) query.
    // If string format is known and consistent think of filtering with mysql STR_TO_DATE() function.
    // I will use php strtotime() to filter them instead (might be big chunk of data though).
    $items = $wpdb->get_results("
        SELECT m1.order_item_id AS id, m1.meta_value AS date, m2.meta_value AS time
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m1
        INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta m2 ON m2.order_item_id = m1.order_item_id
        WHERE m1.meta_key = 'Booking Date'
          AND m2.meta_key = 'Booking Time'
        ORDER BY m1.order_item_id ASC
    ");

    $now = time();
    $data = array();
    foreach ($items as $item) {
        $timestamp = strtotime($item->date . ' ' . $item->time);
        if ($timestamp > $now) {
            // $timestamp is used only for sorting and will be unset later,
            // may be used instead 'date' and 'time' fields though.
            $data[$item->id]['timestamp'] = $timestamp;
        }
    }

    // Initially sorted array with ids as key.
    // Other data will be inserted here from next query
    // thus the order will be maintained.
    asort($data);

    // list of ids that match future timestamp
    // can't simply LIMIT to 10 with array_slice() because upcoming conditions could filter them out
    // higher limit also uncertain, but maybe worth a try (loss vs. propability) 
    $item_list = implode(', ', array_keys($data));

    // helper array for fast check and key assign
    $meta_keys = array(
        'Booking Date' => 'date',
        'Booking Time' => 'time',
        'First Name - First Name' => 'fname',
        'Church Information - Church Name' => 'church',
        'Church Information - City' => 'city',
        'Church Information - State' => 'state'
    );

    // Slow query, but only one - nested loop queries are killers.
    $item_meta = $wpdb->get_results("
        SELECT m.order_item_id, m.meta_key, m.meta_value
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m
        INNER JOIN {$wpdb->prefix}woocommerce_order_items i ON m.order_item_id = i.order_item_id
        INNER JOIN {$wpdb->prefix}posts p ON p.ID = i.order_id
        INNER JOIN {$wpdb->prefix}term_relationships r ON r.object_id = p.ID
        WHERE r.term_taxonomy_id = 11
          AND p.post_type = 'shop_order'
          AND p.post_status = 'publish'
          AND m.order_item_id IN ({$item_list})
    ");

    foreach ($item_meta as $item) {
        if (!isset($meta_keys[$item->meta_key])) { continue; }

        $id  = $item->order_item_id;
        $key = $meta_keys[$item->meta_key];

        $data[$id][$key] = $item->meta_value;
    }

    $count = 0;
    foreach ($data as $item) {
        // skip results not found by second query
        if (!isset($item['time'])) { continue; }

        $time = $item['time'];
        $date = $item['date'];
        unset($item['time'], $item['date'], $item['timestamp']);
        $details = implode(',<br>', $item);
?>
    <div class="wc-upcoming-booking">
        <div class="wc-upcoming-time">
            <span class="upcoming-hour"><?php echo $time; ?></span>
            <span class="upcoming-date"><?php echo $date; ?></span>
        </div>
        <div class="wc-upcoming-details">
            <?php echo $details; ?>
        </div>
    </div>
<?php
        $count++;
        if ($count == 10) { break; }

    }
added 1777 characters in body
Source Link
shudder
  • 686
  • 7
  • 8

WARNING: ANSWER NOT COMPLETE - CODE NEEDS SOME CHANGES & EXPLANATION. WILL BE UPDATED SOON

I'm not familiar with the Woocommerce plugin and couldn't have tested this code, so don't delete yours. Chceck if it works and gives you sameexpected result. I can't tell if you really use the Woocommerce data structure properly, but using the woocommerce_order_itemmeta table in this context scares the hell out of me and if there's no better access to ordered items then you're the last one to blame for unefficient code.

The tweaks are based on some ground rules of querying database (mentioned in other answers as well):

  1. Avoid queries inside loops
  2. Ask for as accurate result as you can (don't fetch sth you won't use)

First one could be achieved, but second needs some hack'ish and not fully efficient workarounds, because ability to limit and sort results is limited by given data structure (see comments within code).

    // Find first item id with future booking date so theto LIMITease couldnext be(slow) usedquery.
    // IIf amstring notformat sureis known and consistent think of thefiltering date/timewith stringmysql format,STR_TO_DATE() sofunction.
    // I can'twill compareuse usingphp STR_TO_DATEstrtotime() to filter them instead.
    $items = $wpdb->get_results("
        SELECT m1.order_item_id AS id, m1.meta_value AS date, m2.meta_value AS time
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m1
        INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta m2 ON m2.order_item_id = m1.order_item_id
        WHERE m1.meta_key = 'Booking Date'
          AND m2.meta_key = 'Booking Time'
        ORDER BY m1.order_item_id ASC
    ");

    $now = time();
    $data = array();
    foreach ($items as $item) {
        if$timestamp (= strtotime($item->date . ' ' . $item->time);
        if ($timestamp > $now) { 
 $first_item = $item         // $timestamp is used only for sorting and will be unset later,
            // may be used instead 'date' and 'time' fields though.
            $data[$item->id;>id]['timestamp'] break;= $timestamp;
        }
    } 

    // Initially sorted array with ids as key.
    // Other data will be inserted here from next query
    // thus the order will be maintained.
    asort($data);

    // list of ids that match future timestamp
    // (can't simply LIMIT to 10 because upcoming conditions could filter them out)
    $item_list = implode(', ', array_keys($data));

    // helper array for fast check and key assign
    $meta_keys = array(
        'Booking Date' => 'date',
        'Booking Time' => 'time',
        'First Name - First Name' => 'fname',
        'Church Information - Church Name' => 'church',
        'Church Information - City' => 'city',
        'Church Information - State' => 'state'
    ); 

    // Slow query, but only one - nested loop queries are killers.
    $item_meta = $wpdb->get_results("
        SELECT m.order_item_id, m.meta_key, m.meta_value
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m
        INNER JOIN {$wpdb->prefix}woocommerce_order_items i ON m.order_item_id = i.order_item_id
        INNER JOIN {$wpdb->prefix}posts p ON p.ID = i.order_id
        INNER JOIN {$wpdb->prefix}term_relationships r ON r.object_id = p.ID
        WHERE r.term_taxonomy_id = 11
          AND p.post_type = 'shop_order'
          AND p.post_status = 'publish'
          AND m.order_item_id >=IN ({$first_item$item_list}
        ORDER BY m.order_item_id ASC
        LIMIT 10)
    ");

    foreach ($item_meta as $item) {
        if (!isset($meta_keys[$item->meta_key])) { continue; }

        $id  = $item->order_item_id;
        $key = $meta_keys[$item->meta_key];

        $data[$id][$key] = $item->meta_value;
    } 

    $count = 0;
    foreach ($data as $item) {
        // skip results not found by second query
        if (!isset($item['time'])) { continue; }

        $time = $item['time'];
        $date = $item['date'];
        unset($item['time'], $item['date'], $item['timestamp']);
        $details = implode(',<br>', $item);
?>
    <div class="wc-upcoming-booking">
        <div class="wc-upcoming-time">
            <span class="upcoming-hour"><?php echo $time; ?></span>
            <span class="upcoming-date"><?php echo $date; ?></span>
        </div>
        <div class="wc-upcoming-details">
            <?php $detailsecho $details; ?>
        </div>
    </div>
<?php
        $count++;
        if ($count == 10) { break; }

    }

Note: I assume that sorting by Booking Time/Date may differ from sorting by order_item_id. (Further optimization possible if assumption is wrong).

WARNING: ANSWER NOT COMPLETE - CODE NEEDS SOME CHANGES & EXPLANATION. WILL BE UPDATED SOON

I'm not familiar with the Woocommerce plugin and couldn't have tested this code, so don't delete yours. Chceck if it works and gives you same result. I can't tell if you really use the Woocommerce data structure properly, but using the woocommerce_order_itemmeta table in this context scares the hell out of me.

    // Find first item id with future booking date so the LIMIT could be used.
    // I am not sure of the date/time string format, so I can't compare using STR_TO_DATE().
    $items = $wpdb->get_results("
        SELECT m1.order_item_id AS id, m1.meta_value AS date, m2.meta_value AS time
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m1
        INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta m2 ON m2.order_item_id = m1.order_item_id
        WHERE m1.meta_key = 'Booking Date'
          AND m2.meta_key = 'Booking Time'
        ORDER BY m1.order_item_id ASC
    ");

    $now = time();
    foreach ($items as $item) {
        if (strtotime($item->date . ' ' . $item->time) > $now) { $first_item = $item->id; break; }
    }

    $meta_keys = array(
        'Booking Date' => 'date',
        'Booking Time' => 'time',
        'First Name - First Name' => 'fname',
        'Church Information - Church Name' => 'church',
        'Church Information - City' => 'city',
        'Church Information - State' => 'state'
    );

    $item_meta = $wpdb->get_results("
        SELECT m.order_item_id, m.meta_key, m.meta_value
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m
        INNER JOIN {$wpdb->prefix}woocommerce_order_items i ON m.order_item_id = i.order_item_id
        INNER JOIN {$wpdb->prefix}posts p ON p.ID = i.order_id
        INNER JOIN {$wpdb->prefix}term_relationships r ON r.object_id = p.ID
        WHERE r.term_taxonomy_id = 11
          AND p.post_type = 'shop_order'
          AND p.post_status = 'publish'
          AND m.order_item_id >= {$first_item}
        ORDER BY m.order_item_id ASC
        LIMIT 10
    ");

    foreach ($item_meta as $item) {
        if (!isset($meta_keys[$item->meta_key])) { continue; }

        $id  = $item->order_item_id;
        $key = $meta_keys[$item->meta_key];

        $data[$id][$key] = $item->meta_value;
    }

    foreach ($data as $item) {
        $time = $item['time'];
        $date = $item['date'];
        unset($item['time'], $item['date']);
        $details = implode(',<br>', $item);
?>
    <div class="wc-upcoming-booking">
        <div class="wc-upcoming-time">
            <span class="upcoming-hour"><?php echo $time; ?></span>
            <span class="upcoming-date"><?php echo $date; ?></span>
        </div>
        <div class="wc-upcoming-details">
            <?php $details ?>
        </div>
    </div>
<?php
    }

I'm not familiar with the Woocommerce plugin and couldn't have tested this code, so don't delete yours. Chceck if it works and gives you expected result. I can't tell if you really use the Woocommerce data structure properly, but using the woocommerce_order_itemmeta table in this context scares the hell out of me and if there's no better access to ordered items then you're the last one to blame for unefficient code.

The tweaks are based on some ground rules of querying database (mentioned in other answers as well):

  1. Avoid queries inside loops
  2. Ask for as accurate result as you can (don't fetch sth you won't use)

First one could be achieved, but second needs some hack'ish and not fully efficient workarounds, because ability to limit and sort results is limited by given data structure (see comments within code).

    // Find first item id with future booking date to ease next (slow) query.
    // If string format is known and consistent think of filtering with mysql STR_TO_DATE() function.
    // I will use php strtotime() to filter them instead.
    $items = $wpdb->get_results("
        SELECT m1.order_item_id AS id, m1.meta_value AS date, m2.meta_value AS time
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m1
        INNER JOIN {$wpdb->prefix}woocommerce_order_itemmeta m2 ON m2.order_item_id = m1.order_item_id
        WHERE m1.meta_key = 'Booking Date'
          AND m2.meta_key = 'Booking Time'
        ORDER BY m1.order_item_id ASC
    ");

    $now = time();
    $data = array();
    foreach ($items as $item) {
        $timestamp = strtotime($item->date . ' ' . $item->time);
        if ($timestamp > $now) { 
            // $timestamp is used only for sorting and will be unset later,
            // may be used instead 'date' and 'time' fields though.
            $data[$item->id]['timestamp'] = $timestamp;
        }
    } 

    // Initially sorted array with ids as key.
    // Other data will be inserted here from next query
    // thus the order will be maintained.
    asort($data);

    // list of ids that match future timestamp
    // (can't simply LIMIT to 10 because upcoming conditions could filter them out)
    $item_list = implode(', ', array_keys($data));

    // helper array for fast check and key assign
    $meta_keys = array(
        'Booking Date' => 'date',
        'Booking Time' => 'time',
        'First Name - First Name' => 'fname',
        'Church Information - Church Name' => 'church',
        'Church Information - City' => 'city',
        'Church Information - State' => 'state'
    ); 

    // Slow query, but only one - nested loop queries are killers.
    $item_meta = $wpdb->get_results("
        SELECT m.order_item_id, m.meta_key, m.meta_value
        FROM {$wpdb->prefix}woocommerce_order_itemmeta m
        INNER JOIN {$wpdb->prefix}woocommerce_order_items i ON m.order_item_id = i.order_item_id
        INNER JOIN {$wpdb->prefix}posts p ON p.ID = i.order_id
        INNER JOIN {$wpdb->prefix}term_relationships r ON r.object_id = p.ID
        WHERE r.term_taxonomy_id = 11
          AND p.post_type = 'shop_order'
          AND p.post_status = 'publish'
          AND m.order_item_id IN ({$item_list})
    ");

    foreach ($item_meta as $item) {
        if (!isset($meta_keys[$item->meta_key])) { continue; }

        $id  = $item->order_item_id;
        $key = $meta_keys[$item->meta_key];

        $data[$id][$key] = $item->meta_value;
    } 

    $count = 0;
    foreach ($data as $item) {
        // skip results not found by second query
        if (!isset($item['time'])) { continue; }

        $time = $item['time'];
        $date = $item['date'];
        unset($item['time'], $item['date'], $item['timestamp']);
        $details = implode(',<br>', $item);
?>
    <div class="wc-upcoming-booking">
        <div class="wc-upcoming-time">
            <span class="upcoming-hour"><?php echo $time; ?></span>
            <span class="upcoming-date"><?php echo $date; ?></span>
        </div>
        <div class="wc-upcoming-details">
            <?php echo $details; ?>
        </div>
    </div>
<?php
        $count++;
        if ($count == 10) { break; }

    }

Note: I assume that sorting by Booking Time/Date may differ from sorting by order_item_id. (Further optimization possible if assumption is wrong).

added 100 characters in body
Source Link
shudder
  • 686
  • 7
  • 8
Loading
Notice added Needs detailed answers by 200_success
deleted 1 character in body
Source Link
shudder
  • 686
  • 7
  • 8
Loading
added 27 characters in body
Source Link
shudder
  • 686
  • 7
  • 8
Loading
Source Link
shudder
  • 686
  • 7
  • 8
Loading