micky
micky

Reputation: 317

Pivoting table in mysql

I have entries and entrymeta table structure as. id is the primary key and entry_id in entrymeta is foreign key to id in entries table.

entries table:

id created_at
1  2017-04-03
2  2017-07-05

entrymeta table:

id entry_id(foreign key to id) meta_key   meta_value
1  1                            Name       Smith
2  1                            Address    Saniply
3  1                            Profession Student
4  2                            Name       John
5  2                            Address    Aviero
6  2                            Profession Businessman

When I execute a query:

$query =  'SELECT entry_id, created_at, meta_key, meta_value FROM entries INNER JOIN entrymeta WHERE entries.id = entrymeta.entry_id';
$wpdb->get_results( $query );

I get the result like:

Array
(
    [0] => stdClass Object
        (
            [entry_id] => 1
            [created_at] => 2017-04-03
            [meta_key] => Name
            [meta_value] => Smith
        )

    [1] => stdClass Object
        (
            [entry_id] => 1
            [created_at] => 2017-04-03
            [meta_key] => Address
            [meta_value] => Saniply
        )

    [2] => stdClass Object
        (
            [entry_id] => 1
            [created_at] => 2017-04-03
            [meta_key] => Profession
            [meta_value] => Student
        )

    [3] => stdClass Object
        (
            [entry_id] => 2
            [created_at] => 2017-07-05
            [meta_key] => Name
            [meta_value] => John
        )

    [4] => stdClass Object
        (
            [entry_id] => 2
            [created_at] => 2017-07-05
            [meta_key] => Address
            [meta_value] => Aviero
        )

    [5] => stdClass Object
        (
            [entry_id] => 2
            [created_at] => 2017-07-05
            [meta_key] => Profession
            [meta_value] => Businessman
        )
)

How can I achieve something like this instead?

Array
(
    [0] => stdClass Object
        (
            [entry_id] => 1
            [created_at] => 2017-04-03
            [Name] => Smith
            [Address] => Saniply
            [Profession] => Student
        )

    [1] => stdClass Object
        (
            [entry_id] => 2
            [created_at] => 2017-07-05
            [Name] => John
            [Address] => Aviero
            [Profession] => Businessman
        )
)

I am using wordpress, if it has any other simpler methods to achieve this please let me know. Thanks!

Upvotes: 0

Views: 76

Answers (2)

mickmackusa
mickmackusa

Reputation: 47991

I am writing your array of objects (resultset) as $array. My method will assign temporary keys to merge the duplicate entry-id objects. Using isset() means that no unnecessary values overwrites occur while iterating.

Code: (Demo)

$array=[
    (object)['entry_id'=>1,'created_at'=>'2017-04-03','meta_key'=>'Name','meta_value'=>'Smith'],
    (object)['entry_id'=>1,'created_at'=>'2017-04-03','meta_key'=>'Address','meta_value'=>'Saniply'],
    (object)['entry_id'=>1,'created_at'=>'2017-04-03','meta_key'=>'Profession','meta_value'=>'Student'],
    (object)['entry_id'=>2,'created_at'=>'2017-07-05','meta_key'=>'Name','meta_value'=>'John'],
    (object)['entry_id'=>2,'created_at'=>'2017-07-05','meta_key'=>'Address','meta_value'=>'Aviero'],
    (object)['entry_id'=>2,'created_at'=>'2017-07-05','meta_key'=>'Profession','meta_value'=>'Businessman']
];
foreach($array as $obj){                                 // iterate all objs
    $id=$obj->entry_id;                                  // cache for more readable code
    if(!isset($result[$id])){                            // first occurrence of entry_id
        $result[$id]=new \stdClass();                    // avoid Warning: Creating default object from empty value
        $result[$id]->entry_id=$id;                      // store entry_id
        $result[$id]->created_at=$obj->created_at;       // store created_at
    }
    $result[$id]->{$obj->meta_key}=$obj->meta_value;     // unconditionally store meta_key and meta_value
}
var_export(array_values($result));  // remove temporary indexes and display

Output:

array (
  0 => 
  stdClass::__set_state(array(
     'entry_id' => 1,
     'created_at' => '2017-04-03',
     'Name' => 'Smith',
     'Address' => 'Saniply',
     'Profession' => 'Student',
  )),
  1 => 
  stdClass::__set_state(array(
     'entry_id' => 2,
     'created_at' => '2017-07-05',
     'Name' => 'John',
     'Address' => 'Aviero',
     'Profession' => 'Businessman',
  )),
)

Upvotes: 1

Joe
Joe

Reputation: 1696

You have to join entrymeta once for every key you want to include in your result, which isn't very scalable.

SELECT
    e.*,
    m_name.meta_value Name,
    m_addr.meta_value Address,
    m_prof.meta_value Profession
FROM
    entries e
JOIN entrymeta m_name ON m_name.entry_id = e.id
    AND m_name.meta_key = 'Name'
JOIN entrymeta m_addr ON m_addr.entry_id = e.id
    AND m_addr.meta_key = 'Address'
JOIN entrymeta m_prof ON m_prof.entry_id = e.id
    AND m_prof.meta_key = 'Profession'

Upvotes: 1

Related Questions