Đây là phiên bản cập nhật của câu trả lời, linh hoạt hơn so với phiên bản trước.
Đây là một ý tưởng sử dụng UNION
trong SQL :
-
Chúng tôi có thể sử dụng dữ liệu từ
posts_clauses
bộ lọc để viết lại truy vấn SQL từposts_request
lọc. -
Chúng tôi mở rộng
WP_Query
lớp để đạt được mục tiêu của chúng tôi. Chúng tôi thực sự làm điều đó hai lần:-
WP_Query_Empty
:để nhận truy vấn SQL đã tạo của từng truy vấn con nhưng không thực hiện truy vấn cơ sở dữ liệu. -
WP_Query_Combine
:để tìm nạp các bài đăng.
-
-
Việc triển khai sau đây hỗ trợ kết hợp
N
truy vấn phụ.
Đây là hai bản demo:
Bản trình diễn số 1:
Giả sử bạn có sáu bài đăng, được sắp xếp theo ngày (DESC):
CCC
AAA
BBB
CCC
YYY
ZZZ
XXX
nơi XXX
, YYY
và ZZZ
cũ hơn DT=2013-12-14 13:03:40
.
Hãy sắp xếp các bài đăng của chúng tôi để các bài đăng được xuất bản sau DT
được sắp xếp theo tiêu đề (ASC) và đăng publisehd trước DT
được sắp xếp theo tiêu đề (DESC):
AAA
BBB
CCC
ZZZ
YYY
XXX
Sau đó, chúng tôi có thể sử dụng như sau:
/**
* Demo #1 - Combine two sub queries:
*/
$args1 = array(
'post_type' => 'post',
'orderby' => 'title',
'order' => 'ASC',
'date_query' => array(
array( 'after' => '2013-12-14 13:03:40' ),
),
);
$args2 = array(
'post_type' => 'post',
'orderby' => 'title',
'order' => 'DESC',
'date_query' => array(
array( 'before' => '2013-12-14 13:03:40', 'inclusive' => TRUE ),
),
);
$args = array(
'posts_per_page' => 1,
'paged' => 1,
'sublimit' => 1000,
'args' => array( $args1, $args2 ),
);
$results = new WP_Combine_Queries( $args );
Điều này tạo ra truy vấn SQL sau:
SELECT SQL_CALC_FOUND_ROWS * FROM (
( SELECT wp_posts.*
FROM wp_posts
WHERE 1=1
AND ( ( post_date > '2013-12-14 13:03:40' ) )
AND wp_posts.post_type = 'post'
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
ORDER BY wp_posts.post_title ASC
LIMIT 1000
)
UNION
( SELECT wp_posts.*
FROM wp_posts
WHERE 1=1
AND ( ( post_date <= '2013-12-14 13:03:40' ) )
AND wp_posts.post_type = 'post'
AND (wp_posts.post_status = 'publish' OR wp_posts.post_status = 'private')
ORDER BY wp_posts.post_title DESC
LIMIT 1000
)
) as combined LIMIT 0, 10
Bản trình diễn # 2:
Đây là ví dụ của bạn:
/**
* Demo #2 - Combine two sub queries:
*/
$today = date( 'm/d/Y', strtotime( 'today' ) );
$args1 = array(
'post_type' => 'workshops',
'meta_key' => 'select_dates_0_workshop_date',
'orderby' => 'meta_value',
'order' => 'ASC',
'meta_query' => array(
array(
'key' => 'select_dates_0_workshop_date',
'value' => $today,
'compare' => '>=',
'type' => 'CHAR',
),
)
);
$args2 = array(
'post_type' => 'workshops',
'meta_key' => 'select_dates_0_workshop_date',
'orderby' => 'meta_value',
'order' => 'DESC',
'meta_query' => array(
array(
'key' => 'select_dates_0_workshop_date',
'value' => $today,
'compare' => '<',
'type' => 'CHAR',
),
)
);
$args = array(
'posts_per_page' => 5,
'paged' => 4,
'sublimit' => 1000,
'args' => array( $args1, $args2 ),
);
$results = new WP_Combine_Queries( $args );
Điều này sẽ cung cấp cho bạn một truy vấn như sau:
SELECT SQL_CALC_FOUND_ROWS * FROM (
( SELECT wp_posts.*
FROM wp_posts
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id)
WHERE 1=1
AND wp_posts.post_type = 'workshops'
AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private')
AND (wp_postmeta.meta_key = 'select_dates_0_workshop_date' AND (mt1.meta_key = 'select_dates_0_workshop_date' AND CAST(mt1.meta_value AS CHAR) >= '05/16/2014') )
GROUP BY wp_posts.ID
ORDER BY wp_postmeta.meta_value ASC
LIMIT 1000
)
UNION
( SELECT wp_posts.*
FROM wp_posts
INNER JOIN wp_postmeta ON (wp_posts.ID = wp_postmeta.post_id)
INNER JOIN wp_postmeta AS mt1 ON (wp_posts.ID = mt1.post_id)
WHERE 1=1
AND wp_posts.post_type = 'workshops'
AND (wp_posts.post_status = 'publish' OR wp_posts.post_author = 1 AND wp_posts.post_status = 'private')
AND (wp_postmeta.meta_key = 'select_dates_0_workshop_date' AND (mt1.meta_key = 'select_dates_0_workshop_date' AND CAST(mt1.meta_value AS CHAR) < '05/16/2014') )
GROUP BY wp_posts.ID
ORDER BY wp_postmeta.meta_value DESC
LIMIT 1000
)
) as combined LIMIT 15, 5
Demo # 3:
Chúng tôi cũng có thể kết hợp nhiều hơn hai truy vấn phụ:
/**
* Demo #3 - Combine four sub queries:
*/
$args = array(
'posts_per_page' => 10,
'paged' => 1,
'sublimit' => 1000,
'args' => array( $args1, $args2, $args3, $args4 ),
);
$results = new WP_Combine_Queries( $args );
Các lớp:
Đây là các lớp học demo của chúng tôi:
/**
* Class WP_Combine_Queries
*
* @uses WP_Query_Empty
* @link https://stackoverflow.com/a/23704088/2078474
*
*/
class WP_Combine_Queries extends WP_Query
{
protected $args = array();
protected $sub_sql = array();
protected $sql = '';
public function __construct( $args = array() )
{
$defaults = array(
'sublimit' => 1000,
'posts_per_page' => 10,
'paged' => 1,
'args' => array(),
);
$this->args = wp_parse_args( $args, $defaults );
add_filter( 'posts_request', array( $this, 'posts_request' ), PHP_INT_MAX );
parent::__construct( array( 'post_type' => 'post' ) );
}
public function posts_request( $request )
{
remove_filter( current_filter(), array( $this, __FUNCTION__ ), PHP_INT_MAX );
// Collect the generated SQL for each sub-query:
foreach( (array) $this->args['args'] as $a )
{
$q = new WP_Query_Empty( $a, $this->args['sublimit'] );
$this->sub_sql[] = $q->get_sql();
unset( $q );
}
// Combine all the sub-queries into a single SQL query.
// We must have at least two subqueries:
if ( count( $this->sub_sql ) > 1 )
{
$s = '(' . join( ') UNION (', $this->sub_sql ) . ' ) ';
$request = sprintf( "SELECT SQL_CALC_FOUND_ROWS * FROM ( $s ) as combined LIMIT %s,%s",
$this->args['posts_per_page'] * ( $this->args['paged']-1 ),
$this->args['posts_per_page']
);
}
return $request;
}
} // end class
/**
* Class WP_Query_Empty
*
* @link https://stackoverflow.com/a/23704088/2078474
*/
class WP_Query_Empty extends WP_Query
{
protected $args = array();
protected $sql = '';
protected $limits = '';
protected $sublimit = 0;
public function __construct( $args = array(), $sublimit = 1000 )
{
$this->args = $args;
$this->sublimit = $sublimit;
add_filter( 'posts_clauses', array( $this, 'posts_clauses' ), PHP_INT_MAX );
add_filter( 'posts_request', array( $this, 'posts_request' ), PHP_INT_MAX );
parent::__construct( $args );
}
public function posts_request( $request )
{
remove_filter( current_filter(), array( $this, __FUNCTION__ ), PHP_INT_MAX );
$this->sql = $this->modify( $request );
return '';
}
public function posts_clauses( $clauses )
{
remove_filter( current_filter(), array( $this, __FUNCTION__ ), PHP_INT_MAX );
$this->limits = $clauses['limits'];
return $clauses;
}
protected function modify( $request )
{
$request = str_ireplace( 'SQL_CALC_FOUND_ROWS', '', $request );
if( $this->sublimit > 0 )
return str_ireplace( $this->limits, sprintf( 'LIMIT %d', $this->sublimit ), $request );
else
return $request;
}
public function get_sql( )
{
return $this->sql;
}
} // end class
Sau đó, bạn có thể điều chỉnh các lớp theo nhu cầu của mình.
Tôi sử dụng thủ thuật được đề cập ở đây
để bảo toàn thứ tự của UNION
truy vấn phụ.Bạn có thể sửa đổi nó cho phù hợp với sublimit
của chúng tôi tham số.
Điều này cũng sẽ hoạt động đối với các truy vấn chính , bằng cách sử dụng posts_request
bộ lọc chẳng hạn.
Tôi hy vọng điều này sẽ hữu ích.