Split CALC_FOUND_ROW query into seperate count query for better performance (#35468)

Co-authored-by: Néstor Soriano <konamiman@konamiman.com>
This commit is contained in:
Vedanshu Jain 2022-12-12 13:39:44 +05:30 committed by GitHub
parent e66d335542
commit 7187c8dff0
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
3 changed files with 72 additions and 6 deletions

View File

@ -0,0 +1,4 @@
Significance: patch
Type: performance
Split CALC_FOUND_ROW query into seperate count query for better performance.

View File

@ -115,6 +115,13 @@ class OrdersTableQuery {
*/
private $sql = '';
/**
* Final SQL query to count results after processing of args.
*
* @var string
*/
private $count_sql = '';
/**
* The number of pages (when pagination is enabled).
*
@ -586,11 +593,7 @@ class OrdersTableQuery {
$fields = $this->fields;
// SQL_CALC_FOUND_ROWS.
if ( ( ! $this->arg_isset( 'no_found_rows' ) || ! $this->args['no_found_rows'] ) && $this->limits ) {
$found_rows = 'SQL_CALC_FOUND_ROWS';
} else {
$found_rows = '';
}
// JOIN.
$join = implode( ' ', array_unique( array_filter( array_map( 'trim', $this->join ) ) ) );
@ -617,6 +620,23 @@ class OrdersTableQuery {
$groupby = $this->groupby ? 'GROUP BY ' . implode( ', ', (array) $this->groupby ) : '';
$this->sql = "SELECT $found_rows DISTINCT $fields FROM $orders_table $join WHERE $where $groupby $orderby $limits";
$this->build_count_query( $fields, $join, $where, $groupby );
}
/**
* Build SQL query for counting total number of results.
*
* @param string $fields Prepared fields for SELECT clause.
* @param string $join Prepared JOIN clause.
* @param string $where Prepared WHERE clause.
* @param string $groupby Prepared GROUP BY clause.
*/
private function build_count_query( $fields, $join, $where, $groupby ) {
if ( ! isset( $this->sql ) || '' === $this->sql ) {
wc_doing_it_wrong( __FUNCTION__, 'Count query can only be build after main query is built.', '7.3.0' );
}
$orders_table = $this->tables['orders'];
$this->count_sql = "SELECT COUNT(DISTINCT $fields) FROM $orders_table $join WHERE $where $groupby";
}
/**
@ -1014,7 +1034,7 @@ class OrdersTableQuery {
}
if ( $this->limits ) {
$this->found_orders = absint( $wpdb->get_var( 'SELECT FOUND_ROWS()' ) );
$this->found_orders = absint( $wpdb->get_var( $this->count_sql ) ); // phpcs:ignore WordPress.DB.PreparedSQL.NotPrepared
$this->max_num_pages = (int) ceil( $this->found_orders / $this->args['limit'] );
} else {
$this->found_orders = count( $this->orders );

View File

@ -1003,6 +1003,48 @@ class OrdersTableDataStoreTests extends WC_Unit_Test_Case {
$this->assertEquals( array_slice( $test_orders, 5, 5 ), $query->orders, 'The expected dataset is supplied when paginating through orders.' );
}
/**
* @testdox Test that the query counts works as expected.
*
* @return void
*/
public function test_cot_query_count() {
$this->assertEquals( 0, ( new OrdersTableQuery() )->found_orders, 'We initially have zero orders within our custom order tables.' );
for ( $i = 0; $i < 30; $i ++ ) {
$order = new WC_Order();
$this->switch_data_store( $order, $this->sut );
if ( 0 === $i % 2 ) {
$order->set_billing_address_2( 'Test' );
}
$order->save();
}
$query = new OrdersTableQuery( array( 'limit' => 5 ) );
$this->assertEquals( 30, $query->found_orders, 'Specifying limits still calculate all found orders.' );
// Count does not change based on the fields that we are fetching.
$query = new OrdersTableQuery(
array(
'fields' => 'ids',
'limit' => 5,
)
);
$this->assertEquals( 30, $query->found_orders, 'Fetching specific field does not change query count.' );
$query = new OrdersTableQuery(
array(
'field_query' => array(
array(
'field' => 'billing_address_2',
'value' => 'Test',
),
),
)
);
$this->assertEquals( 15, $query->found_orders, 'Counting orders with a field query works.' );
}
/**
* @testDox Test the `get_order_count()` method.
*/