Mysql
 sql >> Base de Dados >  >> RDS >> Mysql

Wordpress combinar consultas


Esta é uma versão atualizada da resposta, mais flexível que a anterior.

Aqui está uma ideia usando um SQL UNION :

  • Podemos usar os dados do posts_clauses filtro para reescrever a consulta SQL do posts_request filtro.

  • Estendemos o WP_Query classe para atingir nosso objetivo. Na verdade, fazemos isso duas vezes:
    • WP_Query_Empty :para obter a consulta SQL gerada de cada subconsulta, mas sem fazer a consulta ao banco de dados.
    • WP_Query_Combine :para buscar as postagens.

  • A implementação a seguir suporta a combinação de N subconsultas.

Aqui estão duas demonstrações:

Demonstração nº 1:


Vamos supor que você tenha seis posts, ordenados por data (DESC):
CCC
AAA
BBB
CCC
YYY
ZZZ
XXX 

onde o XXX , YYY e ZZZ são mais antigos que DT=2013-12-14 13:03:40 .

Vamos ordenar nossas postagens para que as postagens publicadas após DT são ordenados por título (ASC) e os posts são publicados antes de DT são ordenados por título (DESC):
AAA
BBB
CCC
ZZZ
YYY
XXX 

Então podemos usar o seguinte:
/**
 * 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 );

Isso gera a seguinte 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 

Demonstração nº 2:


Aqui está o seu exemplo:
/**
 * 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 );

Isso deve fornecer uma 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

Demonstração nº 3:


Também poderíamos combinar mais de duas 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 );

Aulas:


Aqui estão nossas aulas de demonstração:
/**
 * 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

Você pode então ajustar as classes às suas necessidades.

Eu uso o truque mencionado aqui para preservar a ordem de UNION subconsultas. Você pode modificá-lo de acordo com nosso sublimit parâmetro.

Isso também deve funcionar para consultas principais , usando o posts_request filtro, por exemplo.

Eu espero que isso ajude.