Esta es una versión actualizada de la respuesta, que es más flexible que la anterior.
Aquí hay una idea usando SQL UNION
:
-
Podemos usar los datos de las
posts_clauses
filtro para reescribir la consulta SQL desdeposts_request
filtrar. -
Extendemos el
WP_Query
clase para lograr nuestro objetivo. De hecho, lo hacemos dos veces:WP_Query_Empty
:para obtener la consulta SQL generada de cada subconsulta, pero sin realizar la consulta de la base de datos.WP_Query_Combine
:para buscar las publicaciones.
-
La siguiente implementación admite la combinación de
N
subconsultas.
Aquí hay dos demostraciones:
Demostración n.º 1:
Supongamos que tiene seis publicaciones, ordenadas por fecha (DESC):
CCC
AAA
BBB
CCC
YYY
ZZZ
XXX
donde el XXX
, YYY
y ZZZ
son mayores de DT=2013-12-14 13:03:40
.
Ordenemos nuestras publicaciones para que las publicaciones se publiquen después del DT
se ordenan por título (ASC) y las publicaciones se publican antes de DT
están ordenados por título (DESC):
AAA
BBB
CCC
ZZZ
YYY
XXX
Entonces podemos usar lo siguiente:
/**
* 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 );
Esto genera la siguiente consulta SQL:
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
Demostración n.º 2:
Aquí está tu ejemplo:
/**
* 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 );
Esto debería darte una consulta como esta:
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
Demostración n.º 3:
También podríamos combinar más de dos subconsultas:
/**
* 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 );
Clases:
Aquí están nuestras clases de demostración:
/**
* 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
A continuación, puede ajustar las clases a sus necesidades.
Yo uso el truco mencionado aquí
para preservar el orden de UNION
subconsultas. Puede modificarlo en consecuencia con nuestro sublimit
parámetro.
Esto también debería funcionar para consultas principales , mediante el posts_request
filtro, por ejemplo.
Espero que esto ayude.