Trying to group up results of a SQL multi table query - php

alright best way for me to explain is by giving an example
Table 1
ID | name | class
-------------------------
1 | first name | a
2 | second name | b
Table 2
ID | name_id | meta_key | meta_value |
-------------------------------------------------------
1 | 1 | image | someImage.jpg |
2 | 1 | description | very long description |
3 | 2 | image | someImage_2.jpg |
4 | 2 | description | very long description 2 |
I am trying to select name and class from Table 1, and also all the records from Table 2 that has the same name_id as ID (in Table 1).
Here is the query i have now:
SELECT
table1.name,
table1.class,
table2.name_id,
table2.meta_key,
table2.meta_value
FROM
table1
LEFT JOIN
table2 ON ( table1.ID = table2.name_id )
WHERE
table1.ID = '2'
This works and return an array with as many elements as the Table 2 entries that has name_id = 2
Is there a way to return an array with 1 the result from first table, and another array with results from the second table ?
Update:
The ideal results will be:
$result['table1'][0] = array('name' => 'second name',
'class' => 'b')
$result['table2'][0] = array('name_id' => 2,
'meta_key' => 'image',
'meta_value' => 'someImage_2.jpg')
$result['table2'][1] = array('name_id' => 2,
'meta_key' => 'description',
'meta_value' => 'very long description 2')
hopes that makes sense.

I'd split the result array AFTER fetching it from the database...
//Assuming your result-set is assigned to $result:
$table1_keys = array('id', 'name', 'class');
$table2_keys = array('id', 'name_id', 'meta_key', 'meta_value');
$table1_results = array();
$table2_results = array();
foreach($result as $key => $val) {
if(in_array($key, $table1_keys)) {
$table1_results[] = $val;
}
if(in_array($key, $table2_keys)) {
$table2_results[] = $val;
}
}
After this, your result-set should be split into two arrays: $table1_results and $table2_results.
Hope this helps you.

I don't believe there's a way to do it in SQL but I'd label each table so it can be done generically in PHP.
SELECT
':table1',
table1.name,
table1.class,
':table2',
table2.name_id,
table2.meta_key,
table2.meta_value
FROM ...
Although personally I'd just do this.
SELECT 'table1', table1.*, ':table2', table2.*
FROM ...
That makes it easier to duplicate entire rows between client and server and reduces maintenance when you decide to add a new field.
However in your query you are duplicating table1 for each row returned so perhaps this would be better.
SELECT
table1.*,
table2a.meta_value AS image,
table2b.meta_value AS description
FROM
table1
LEFT JOIN
table2 AS table2a ON ( table1.ID = table2a.name_id AND table2a.meta_key = 'image' )
LEFT JOIN
table2 AS table2b ON ( table1.ID = table2b.name_id AND table2b.meta_key = 'description' )
WHERE
table1.ID = '2'

Related

Parse through a nested array and save it in mysql db with parent id

I have a nested array like the following:-
array(
array(
'id' => 45cfeteuid536hjj929,
'name' = 'Cuisines',
'children' => array(
array(
'id' => 45gcfeteuid536hjj929,
'name' = 'Itlaian',
),
array(
'id' => 45bjfe78ng5d536hjj92,
'name' = 'Indian',
'children' => array(
array(
'id' => 457hfe78ng5d53ghy56j,
'name' = 'Punjabi'
)
)
)
)
)
);
I have a table like this:-
|--------------------------------|
| id | name | parent_id |
|--------------------------------|
I want data to be inserted like this:-
|---------------------------------------------------------------|
| id | name | parent_id | api_id |
|---------------------------------------------------------------|
| 1 | Cuisines | 0 | 45cfeteuid536hjj929 |
|---------------------------------------------------------------|
| 2 | Italian | 1 | 45gcfeteuid536hjj929 |
|---------------------------------------------------------------|
| 3 | Indian | 1 | 45bjfe78ng5d536hjj92 |
|---------------------------------------------------------------|
| 4 | Punjabi | 3 | 457hfe78ng5d53ghy56j |
|---------------------------------------------------------------|
The parent_id of the child is the id of the object to which it belongs. The id of the table are autoincrement value generated automatically by the mysql db.
For example:-
Step 1: While saving Cuisine, the id (autoincrement in nature) saved
is 1. Since it is the root, hence parent_id = 0.
Step 2: While saving Italian, the id (autoincrement in nature)
saved is 1. Since it is a child of Cuisines, the parent_id = 1
How can I save the nested array in such way?
One way would be to generate insert statements that look like this (assuming your table is called tab):
insert into tab(name, parent_id, app_id)
select 'Cuisines', coalesce(min(id), 0), '45cfeteuid536hjj929' from tab
where app_id = '';
insert into tab(name, parent_id, app_id)
select 'Itlaian', coalesce(min(id), 0), '45gcfeteuid536hjj929' from tab
where app_id = '45cfeteuid536hjj929';
insert into tab(name, parent_id, app_id)
select 'Indian', coalesce(min(id), 0), '45bjfe78ng5d536hjj92' from tab
where app_id = '45cfeteuid536hjj929';
insert into tab(name, parent_id, app_id)
select 'Punjabi', coalesce(min(id), 0), '457hfe78ng5d53ghy56j' from tab
where app_id = '45bjfe78ng5d536hjj92';
These queries will find the parent ID via the app_id that parent should have. No value for the id column is inserted, as it is assumed that the database will generate it upon insertion.
Here is a recursive PHP function which generates those statements. You'll have to adapt it to actually execute those statements with whatever API you use (PDO, mysqli, ...):
function insertRecords($data, $parent = "") {
foreach($data as $row) {
// Replace next echo statement with the code to actually execute this SQL:
echo "insert into tab(name, parent_id, app_id) select '$row[name]', coalesce(min(id), 0), '$row[id]' from tab where app_id = '$parent';\n";
if (isset($row["children"])) insertRecords($row["children"], $row["id"]);
}
}
Assuming your nested data structure is stored in variable $data, call the above function like this:
insertRecords($data);

Pagination from two SQL Tables with one query. Fastest Way

I have a simple Database:
Table 1: Object Table 2: Data
id | type | added object_id | key | value
------------------------ -----------------------------
1 | app | 2017 1 | name | ...
2 | app | 2017 2 | name | ...
3 | win | 2017 2 | version | ...
2 | dev_name | ...
2 | lang | ...
i created a simple pagination to show only 5 object from type apps with their infomation from the data table:
example code:
$sth = $dbh->prepare("SELECT * FROM object WHERE type = 'app' LIMIT 5");
$sth->execute;
$object = $sth->fetchAll(PDO::FETCH_ASSOC);
foreach($object as $rows) {
$sth = $dbh->prepare("SELECT * FROM data WHERE id = $rows['id']");
$sth->execute;
$data = $sth->fetchAll(PDO::FETCH_ASSOC);
echo $data['name'];
echo $data['version'];
echo $object['added'];
...
Is there a better way? because this is a very bad solution, i want to query only one time where i get an fetch array like this:
array(
// app with id = 1 in object table
[1] => array(
// data from the app with id = 1 in data table
[name] => ...
[version] => ...
[dev_name] => ...
[lang] => ...
// app with id = 2 in object table
[2] => array(
// data from the app with id = 2 in data table
[name] => ...
[version] => ...
[dev_name] => ...
[lang] => ...
...
one query:
SELECT * FROM object inner join data on data.id_object=object.id WHERE object.type = 'app' LIMIT 5
This query return information from two table with associate data.
I think that's what you want
If you want to group by id of the first table you can also execute this query
SELECT * FROM object inner join data on data.id_object=object.id WHERE object.type = 'app' group by object.id LIMIT 5
or
SELECT Distinct(object.id), object.type, .... FROM object inner join data on data.id_object=object.id WHERE object.type = 'app' LIMIT 5
Consider for string comparison use LIKE instead of =

How properly SELECT several one-to-many rows?

We have 3 tables.
1) Order
PK
order_id
2) Order_Products
PK | FK | Column | Column
order_product_id | order_id | product_id | name | quantity
3) Order_Product_Components
PK | FK | Column | Column | Column
order_product_component_id | order_product_id | stock_id | name | quantity
Tables 2 and 3 have one-to-many relationship.
So if we want to get an array of order products with their components what is the best query we can use?
Finally the result should be this format (if we have just 1 product with 2 components):
$products = array(
array(
' 'product_id' => x,
'name' => x,
'quantity' => x,
'stocks' => array(
array(
'stock_id' => x,
'name' => x,
'quantity' => x),
array(
'stock_id' => x,
'name' => x,
'quantity' => x)));
Since order is a reserved word I have changed the order table name to 'orders'. I am using join to get the expected results. I think group by would not be as useful to get the result the way you want it.
$sql= "select * from orders o join order_products op on o.order_id=op.order_id";
$stmt = $db->prepare($sql);
$stmt->execute();
$orders =$stmt->fetchAll();
foreach($orders as $o){
$sql= "select stock_id,name,quantity from order_products op
join order_product_components opc on opc.order_product_id=op.order_product_id where op.order_product_id=:orderprodid";
$stmt = $db->prepare($sql);
$stmt->execute(array('orderprodid'=>$o['order_product_id']));
$comps =$stmt->fetchAll();
$o['stocks']=$comps;
}
You may not want to do a loop of queries, which can be very expensive.
Just do a query joining all tables and order by product id,product name, ... stock id, stock name,... and loop through the results and build your array in one pass.

How can you count matches from a join action in CakePHP?

I want to count to an extra field created, the matches from a join. Is this possible in CakePHP?
I have an example of my data that I curently have.
And how would a query look in mySQL for this type of result?
Table:goal
id | name
-----------
1 Goal X
2 Goal Y
Table: tasks
id | name | goal_id
-------------------
1 task1 1
2 task2 1
3 task3 2
4 task4 2
5 task5 2
Result
id | name | matches
-------------------
1 goal1 2
2 goal2 3
MySQL query :
SELECT goal.id, goal.name, Count( * ) AS matches
FROM goal
RIGHT JOIN task ON goal.id = task.goal_id
GROUP BY goal.id
CakePHP : [if you have model with name Goal and Task]
$options['fields'] = array(
'Goal.id',
'Goal.name',
'count(*) AS matches'
);
$options['joins'] = array(
array(
'table' => 'tasks',
'alias' => 'Task',
'type' => 'Right',
'conditions' => array(
'Goal.id = Task.goal_id'
)
)
);
$options['group'] = array('Goal.id');
$result = $this->Goal->find('all', $options);
mysql should be that:
Select goal.id,goal.name,Count(*) From goal RIGHT JOIN tasks on goal.id=tasks.goal_id Group by goal.id
CakePHP, can't tell you without testing...

Use array as table in MySql JOIN

I would like to use data from an array to add a column and make a join on a MySql table.
Let's say, on one hand, we have an array ($relevance):
$relevance = array(
array('product_id' => 1, 'relevance' => 2),
array('product_id' => 2, 'relevance' => 5),
array('product_id' => 3, 'relevance' => 1),
);
And on the other hand, we have this table (products):
product_id | product_name
--------------------------
1 | Product 1
2 | Product 2
3 | Product 3
Now, I want to select data from the products table and joining them with $relevance based on their product_id in order to get something like this:
product_id | product_name | relevance
---------------------------------------
1 | Product 1 | 2
2 | Product 2 | 5
3 | Product 3 | 1
In other words, how can I make a SELECT with LEFT JOIN using data from both the MySql database and an array which would "mean" something like this:
SELECT `p`.*, `{{$relevance}}`.* FROM `products` AS `p`
LEFT JOIN `{{$relevance}}`
ON p.product_id = {{$relevance}}.product_id
pure sql solution, not efficient though for big recordsets:
$relevances = array()
foreach ($relevance as $v){
$relevances[] = "SELECT {$v['product_id']} as product_id, {$v['relevance']} as relevance"
}
$sql = implode(' UNION ', $relevances);
$sql = "SELECT p.product_id, p.product_name, r.relevance
FROM products p
JOIN ($sql) r ON p.product_id=r.product_id";
Well, you can make another table relevance and then you could just use JOIN. Or you can use loop to get those data. Something like
$relevance = array(
array(1, 'relevance' => 2),
array(2, 'relevance' => 5),
array(3, 'relevance' => 1),
);
$q = mysql_query("SELECT * FROM products")
while($r = mysql_fetch_assoc($q))
{
$productRelevance = $relevance[$r['prod_id']-1];
}
Hoewever this code may fail if you delete some product and those ids wouldn' be in order, e.g.: 1,2,5,6,7,10. I recommend you to use another table.

Categories