Pivoting table in mysql - php

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!

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',
)),
)

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'

Related

connect arrays into one

How i can transform this first array [ONE}: Into second array [TWO} where i can call meta_key and than i get var: i get this array by calling specified sql rows and i am using this query: $user_data = $wpdb->get_results("SELECT * FROM $wpdb->usermeta WHERE user_id = $fivesdraft->user_id "); maybe its another better way to connect it in query? not in PHP
Array ONE
(
[0] => stdClass Object
(
[umeta_id] => 16
[user_id] => 2
[meta_key] => nickname
[meta_value] => user1
)
[1] => stdClass Object
(
[umeta_id] => 17
[user_id] => 2
[meta_key] => first_name
[meta_value] => testname
)
[2] => stdClass Object
(
[umeta_id] => 18
[user_id] => 2
[meta_key] => last_name
[meta_value] => testlastname
)
[3] => stdClass Object
(
[umeta_id] => 19
[user_id] => 2
[meta_key] => description
[meta_value] => user desc
)
)
Array TWO
(
[nickname] => 'user1'
[first_name] => 'user desc'
[last_name] => 'user desc'
[description] => 'user desc'
...
)
You can use array_reduce:
$myArray = array_reduce($arrayOne, function ($result, $item) {
$result[$item->meta_key] = $item->meta_value;
return $result;
}, array());
Just loop over the array and write the values in a new array
$two = array();
foreach ($one as $item) {
$two[$item->meta_key] = $item->meta_value;
}

Adding a key to a multidimensional array from another multidimensional array

I have seen lots on adding to multidimensional arrays but I can't figure out how they apply to my situation. I am trying to end up with one large multi-dimensional array.
I have a multidimensional associative array compiled from a mysql query of a membership database. All my arrays are compiled using while loops.
Array(
[0] => Array ( [full_name] => Amy Smith [id] => 00111111 [member_ref] => 1 [type] => 1 )
[1] => Array ( [full_name] => Bob Smith [id] => 00222222 [member_ref] => 2 [type] => 0 )
[2] => Array ( [full_name] => Cam Smith [id] => 00333333 [member_ref] => 3 [type] => 2 )
)
This was compiled from the mysql_query
SELECT full_name, id, member_ref, type
FROM members
ORDER BY full_name asc
I then have a list of [id] numbers in another table for people who have paid membership. This is provided by another organisation. This will become the [paid] key.
Array(
[0] => Array ( [id] => 00111111 )
[1] => Array ( [id] => 00333333 )
[2] => Array ( [id] => 00444444 )
)
From query:
SELECT * FROM paid
And finally a table that counts members attendances within a specified timeframe, to be the [log] key.
Array(
[0] => Array ( [member_ref] => 1 [COUNT(member_ref)] => 17 )
[1] => Array ( [member_ref] => 2 [COUNT(member_ref)] => 10 )
[2] => Array ( [member_ref] => 3 [COUNT(member_ref)] => 1 )
)
From
SELECT member_ref, COUNT(member_ref)
FROM log
WHERE timestamp >= STR_TO_DATE('$from')
AND timestamp < STR_TO_DATE('$to')
GROUP BY member_ref
What i want to end up with is: (Note that Bob Smith hasn't paid)
Array(
[0] => Array ( [full_name] => Amy Smith [id] => 00111111 [member_ref] => 1 [type] => 1 [paid] => 00111111 [log] => 17)
[1] => Array ( [full_name] => Bob Smith [id] => 00222222 [member_ref] => 2 [type] => 0 [paid] => 0 [log] => 10)
[2] => Array ( [full_name] => Cam Smith [id] => 00333333 [member_ref] => 3 [type] => 2 [paid] => 00333333 [log] => 1)
)
I can then output it as necessary, but crucuially I can sort the data by any of the keys.
I am open to doing this with a single mysql query with the sort key being a php $var, but I can't figure out the JOIN elements, it's just far too complex for me to understand.
Thanks
Working from the answer of NSjonas:
SELECT m.full_name,
m.id,
m.member_ref,
m.type,
(p.id IS NOT NULL) paid,
COUNT(l.member_ref) log
FROM members m
LEFT JOIN paid p
ON m.id = p.id
LEFT JOIN log l
ON m.member_ref = l.member_ref
AND l.timestamp >= '$from'
AND l.timestamp < '$to'
GROUP BY m.full_name, m.id, m.member_ref, m.type, p.id
ORDER BY m.full_name ASC
My mySQL is a bit rusty but I think this is basically what your looking for. Definitely possible in 1 query
SELECT m.full_name, m.id, m.member_ref, m.type, COUNT(l.member_ref) log,
Case WHEN p.Id != null
THEN true
ELSE false paid
FROM members m
LEFT JOIN paid p
ON m.id = p.id
LEFT JOIN log l
ON m.member_ref = l.member_ref
WHERE l.timestamp >= STR_TO_DATE('$from')
AND l.timestamp < STR_TO_DATE('$to')
ORDER BY m.full_name asc

Formatting CakePHP join statement

I'm using CakePHP 2.3.8 and I'm trying to more efficiently join two tables. buildings and building_rental_rates
buildings
id | name | description | property_owner | building_type
1 Big Big Building 1 1
building_rental_rates
id | building_type | rate_name | rate
1 1 daily 150.00
2 1 hourly 15.00
I want to look up a building and select the different rental rates. The tables are joined on building_type. Here's the find statement I have
$buildings = $this->Building ->find('all',array(
'Building.property_owner' => '1',
'fields' => array('Building.*','BuildingRentalRate.*'),
'joins' => array(
array(
'table' => 'building_rental_rates',
'alias' => 'BuildingRentalRate',
'type' => 'inner',
'conditions' => array(
'Building.building_type = BuildingRentalRate.building_type'
)
)
)
));
Here is the result
Array
(
[0] => Array
(
[Building] => Array
(
[id] => 1
[name] => Big
[description] => Big Building
[property_owner] => 1
[building_type] => 1
)
[BuildingRentalRate] => Array
(
[id] => 1
[building_type] => 1
[rate_name] => daily
[rate] => 150.00
)
)
[1] => Array
(
[Building] => Array
(
[id] => 1
[name] => Big
[description] => Big Building
[property_owner] => 1
[building_type] => 1
)
[BuildingRentalRate] => Array
(
[id] => 2
[building_type] => 1
[rate_name] => hourly
[rate] => 15.00
)
)
)
While the data is found properly, it's a pain to traverse through it. Can I use a join statement to result in this output? Notice how the BuildingRentalRate is an array containing all entries of that table that share the same building_type
Array
(
[0] => Array
(
[Building] => Array
(
[id] => 1
[name] => Big
[description] => Big Building
[property_owner] => 1
[building_type] => 1
)
[BuildingRentalRate] => Array
(
[0] => Array
(
[id] => 1
[building_type] => 1
[rate_name] => daily
[rate] => 150.00
)
[1] => Array
(
[id] => 2
[building_type] => 1
[rate_name] => hourly
[rate] => 15.00
)
)
)
)
I know Cake can output results like this when using model associations, but I wasn't able to get my associations correct apparently because it kept on joining Building.id on BuildingRentalRate.building_type (it should be Building.building_type = BuildingRentalRate.building_type)
Even with my association like this...
//Building.php
public $hasMany = array('BuildingRentalRate' => array('foreignKey' => 'building_type'));
//BuildingRentalRate.php
public $belongsTo = array('Building' => array('foreignKey' => 'building_type'));
It would join Building.id on BuildingRentalRate.building_type despite specificying building_type in both models as the foreign key.
Can I do some kind of nested SQL query in the conditions or is there an easier way of doing this?
you should be able to avoid using joins if in BuildingRentalRate.php you set your relationship this way
$public belongsTo = array(
'Building' => array(
'foreignKey' => false,
'conditions' => array
(
'Building.building_type' => 'BuildingRentalRate.building_type'
)
)
);

mysql select all, group by orderid

I was wondering if anyone could help me with selecting information from my table, but grouping the results depending on the order id. I'm sure this is quite simple, but I can't seem to get the code working.
Here's my attempt - which is only showing 1 result, instead of 6:
4 results with orderid 55542
2 results with orderid 55543
SQL:
SELECT *
FROM #__users_orders
WHERE userid = 22
GROUP BY orderid
ORDER BY date DESC
Any help would be appreciated :)
EDIT:
I'd like to acheive this (or something similar)
Array[55542]
(
[0] => stdClass Object
(
[id] => 6
[userid] => 66
[orderid] => 55542
[date] => 2011-08-05 16:30:24
[code] => 121021
[title] => 7 STAR CHICKEN A/KING 71198 1.3KG
[units] => 2
[ctns] =>
)
[1] => stdClass Object
(
[id] => 1
[userid] => 66
[orderid] => 55542
[date] => 2011-08-05 16:06:12
[code] => 302371
[title] => ANCHOVY FILL 730GM
[units] => 2
[ctns] =>
)
[2] => stdClass Object
(
[id] => 6
[userid] => 66
[orderid] => 55542
[date] => 2011-08-05 16:30:24
[code] => 121021
[title] => 7 STAR CHICKEN A/KING 71198 1.3KG
[units] => 2
[ctns] =>
)
[3] => stdClass Object
(
[id] => 1
[userid] => 66
[orderid] => 55542
[date] => 2011-08-05 16:06:12
[code] => 302371
[title] => ANCHOVY FILL 730GM
[units] => 2
[ctns] =>
)
)
Array[55543]
(
[0] => stdClass Object
(
[id] => 6
[userid] => 66
[orderid] => 55543
[date] => 2011-08-05 16:30:24
[code] => 121021
[title] => 7 STAR CHICKEN A/KING 71198 1.3KG
[units] => 2
[ctns] =>
)
[1] => stdClass Object
(
[id] => 1
[userid] => 66
[orderid] => 55543
[date] => 2011-08-05 16:06:12
[code] => 302371
[title] => ANCHOVY FILL 730GM
[units] => 2
[ctns] =>
)
)
SELECT *
FROM #__users_orders
WHERE userid = 22
ORDER BY orderid DESC
Just select your items like this and create your object/array hierarchy in the frontend by iterating over the results and creating a new array for every new orderid that comes by.
SELECT orderid, COUNT(*)
FROM #__users_orders
WHERE userid = 22
GROUP BY orderid
ORDER BY date DESC
Normally, you have to use an aggregate (eg COUNT, SUM) and GROUP BY matched. So that columns in the SELECT but not in the COUNT or SUM are in the GROUP BY
Only MySQL allows you to not follow this rule. Other DB engines would give an error.
the query seems ok, maybe your extracting the results the wrong way
SELECT * FROM table GROUP BY field1;
should return same number of rows than
SELECT field1, field2 FROM table GROUP BY field1;
but different number than
SELECT * FROM table;
You should not select "*" in this query.
When you group by "some columns". You can only select "some columns" or some_aggregate_function(other columns).
e.g. If you want to get the aggregate ordersize and latest date for each order id, you would do something like -
SELECT orderid, sum(ordersize), max(date) FROM #__users_orders WHERE userid = 22 GROUP BY orderid ORDER BY max(date) DESC

Merge arrays from 2 SQL results

So i have a tags table setup in SQL
lists, lists_tags, tags
Each list can have multiple tags, and tags have two types genre and producer. Now if we are looking for all lists with tags 'action' these are the steps im following
SELECT GROUP_CONCAT(mini_lists_tags.list_id) AS list_ids
FROM (`mini_tags`)
LEFT JOIN `mini_lists_tags` ON `mini_lists_tags`.`tag_id` = `mini_tags`.`tag_id`
WHERE `mini_tags`.`tag_slug` = 'action'
That will return an array 1,2 for the ids of the list.
SELECT *
FROM (`mini_lists_anime`)
JOIN `mini_lists` ON `mini_lists`.`list_id` = `mini_lists_anime`.`list_id`
WHERE `mini_lists`.`list_id` IN ('1', '2')
AND `mini_lists`.`list_state` = 'active'
that gets all the lists in an array EXAMPLE:
Array
(
[0] => stdClass Object
(
[list_id] => 1
[list_episodes] => 13
[list_duration] => 24
[list_aired] => 1238623200
[list_age_rate] => PG-13 - Teens 13 or older
[user_id] => 1
[list_mal] => 5342
[list_category] => Anime
[list_type] => TV
[list_status] => Completed
[list_title] => Asura Cryin'
[list_alt_titles] => アスラクライン
[list_thumb] => 17071
[list_likes] => 0
[list_date] => 1300609723
[list_update] => 0
[list_state] => active
[list_info] =>
)
[1] => stdClass Object
(
[list_id] => 2
[list_episodes] => 26
[list_duration] => 23
[list_aired] => 1238623200
[list_age_rate] => PG-13 - Teens 13 or older
[user_id] => 1
[list_mal] => 329
[list_category] => Anime
[list_type] => TV
[list_status] => Completed
[list_title] => Planetes
[list_alt_titles] => プラネテス
[list_thumb] => 4822
[list_likes] => 0
[list_date] => 1300609723
[list_update] => 0
[list_state] => active
[list_info] =>
)
)
And then we get the tags
SELECT `mini_lists_tags`.`list_id`, `mini_tags`.`tag_type`, GROUP_CONCAT(mini_tags.tag_name) AS tag_names
FROM (`mini_lists_tags`)
INNER JOIN `mini_tags` ON `mini_tags`.`tag_id` = `mini_lists_tags`.`tag_id`
WHERE `mini_lists_tags`.`list_id` IN ('1', '2')
GROUP BY `mini_lists_tags`.`list_id`, `mini_tags`.`tag_type`
that gets all the tags in an array EXAMPLE:
Array
(
[0] => stdClass Object
(
[list_id] => 1
[tag_type] => Genre
[tag_names] => Supernatural,Action,Mecha
)
[1] => stdClass Object
(
[list_id] => 1
[tag_type] => Producers
[tag_names] => Seven Arcs
)
[2] => stdClass Object
(
[list_id] => 2
[tag_type] => Genre
[tag_names] => Romance,Action,Sci-fi,Comedy,Slice of Life,Drama,Space
)
[3] => stdClass Object
(
[list_id] => 2
[tag_type] => Producers
[tag_names] => Sunrise,Bandai Entertainment,Bandai Visual,Bang Zoom! Entertainment
)
)
Now the problem is I need to get them merged on the list_id so it returns something like this for each one.
stdClass Object
(
[list_id] => 1
[list_episodes] => 13
[list_duration] => 24
[list_aired] => 1238623200
[list_age_rate] => PG-13 - Teens 13 or older
[user_id] => 1
[list_mal] => 5342
[list_category] => Anime
[list_type] => TV
[list_status] => Completed
[list_title] => Asura Cryin'
[list_alt_titles] => アスラクライン
[list_thumb] => 17071
[list_likes] => 0
[list_date] => 1300609723
[list_update] => 0
[list_state] => active
[list_info] =>
[list_tags] => Array
(
[0] => stdClass Object
(
[tag_type] => Genre
[tag_names] => Mecha,Action,Supernatural
)
[1] => stdClass Object
(
[tag_type] => Producers
[tag_names] => Seven Arcs
)
)
)
Any advice is appreciated, i'm really lost. if there is better solution than this, i am all ears.
You can do another type of join that will return the parent item multiple times merged with each child object like so:
Array(
[0] => stdClass Object
(
[list_id] => 1
[list_episodes] => 13
[list_duration] => 24
...etc
[tag_type] => Genre
[tag_names] => Supernatural,Action,Mecha
...etc
)
[1] => stdClass Object
(
[list_id] => 1
[list_episodes] => 13
[list_duration] => 24
...etc
[tag_type] => Producers
[tag_names] => Seven Arcs
...etc
)
[2] => stdClass Object
(
[list_id] => 2
[list_episodes] => 26
[list_duration] => 23
...etc
[tag_type] => Genre
[tag_names] => Supernatural,Action,Mecha
...etc
)
[3] => stdClass Object
(
[list_id] => 2
[list_episodes] => 26
[list_duration] => 23
...etc
[tag_type] => Producers
[tag_names] => Seven Arcs
...etc
)
)
You will then need to loop through your results merging down the results into their child/parent relationships. This is because SQL always returns rows as results, not complex structures.
Although this is more complex to deal with, it's ususally less process intensive than making looped sql queries for each parent object (known as n+1 queries)

Categories