Mysql Join rows from multiple tables - mysql

I am asking here because what english words i need to search for this what i want to do.
I can solve this with multiple queries then join arrays with for and foreach loops but why to do it if there is smarter way.
My query now looks like:
SELECT p.* , pi.path, pv.videoid
FROM products p
JOIN productimage pi ON (p.id = pi.product)
JOIN productvideo pv ON (p.id = pv.product)
WHERE p.id=:id
my tables look like
product:
|---id---|---name----|---desc---|---price---|---active----|
|---1----|---somet---|---dsa----|---456-----|---1/0-------|
|---2----|---somet2--|---ddsasa-|---44556---|---1/0-------|
product video:
|----id----|---product----|----videoid-------|
|----4-----|---1----------|-----youtubeid----|
|----4-----|---1----------|--secondyoutubeid-|
and same as video for images, and i want to make same for files
result like this to make it easy
//result of $stmt->fetchAll(PDO::FETCH_ASSOC);
$arr = array(
0 => array(
"id" => "1",
"name" => "hello world",
"category" => "3",
"videoid" => array("oneyoutubeid", "secondid", "thirdid"), //videos that is for this product
"path" => array("imagepathofproduct", "secondimageofproduct"),
"active" => "1"
),
1 => array(
"id" => "2",
"name" => "hello world product 2",
"category" => "4",
"videoid" => array("oneyoutubeid", "secondid", "thirdid"), //videos that is for this product
"path" => array("imagepathofproduct", "secondimageofproduct"),
"active" => "1"
),
);
to use it like this: return $stmt->fetchAll(PDO::FETCH_ASSOC); and start foreach for displaying data.
thankyou very much

solved by concat and i will explode it into array.
but still waiting for more smart solutions.
Solution:
SELECT p.* , GROUP_CONCAT(DISTINCT path ORDER BY pi.id) AS images, GROUP_CONCAT(DISTINCT videoid ORDER BY pi.id) AS videos FROM products p JOIN productimage pi ON (p.id = pi.product) JOIN productvideo pv ON (p.id = pv.product) WHERE p.id=1
Home it will helpt to somebody

Related

how to optimize mysql query in phalcon

i used this query:
$brands = TblBrand::find(array("id In (Select p.brand_id From EShop\\Models\\TblProduct as p Where p.id In (Select cp.product_id From EShop\\Models\\TblProductCategory as cp Where cp.group_id_1='$id'))", "order" => "title_fa asc"));
if($brands != null and count($brands) > 0)
{
foreach($brands as $brand)
{
$brandInProductCategory[$id][] = array
(
"id" => $brand->getId(),
"title_fa" => $brand->getTitleFa(),
"title_en" => $brand->getTitleEn()
);
}
}
TblBrand => 110 records
TblProduct => 2000 records
TblProductCategory => 2500 records
when i used this code, my site donot show and loading page very long time ...
but when i remove this code, my site show.
how to solve this problem?
The issue is your query. You are using the IN statement in a nested format, and that is always going to be slower than anything else. MySQL will need to first evaluate what is in the IN statement, return that and then do it all over again for the next level of records.
Try simplifying your query. Something like this:
SELECT *
FROM Brands
INNER JOIN Products ON Brand.id = Products.brand_id
INNER JOIN ProductCategory ON ProductCategory.product_id = Products.id
WHERE ProductCategory.group_id_1 = $id
To achieve the above, you can either use the Query Builder and get the results that way
https://docs.phalconphp.com/en/latest/api/Phalcon_Mvc_Model_Query_Builder.html
or if you have set up relationships in your models between brands, products and product categories, you can use that.
https://docs.phalconphp.com/en/latest/reference/model-relationships.html
example:
$Brands = Brands::query()
->innerJoin("Products", "Products.brand_id = Brand.id")
->innerJoin("ProductCategory", "ProductCategory.product_id = Products.id")
->where("ProductCategory.group_id_1 = :group_id:")
->bind(["group_id" => $id])
->cache(["key" => __METHOD__.$id] // if defined modelCache as DI service
->execute();
$brandInProductCategory[$id] = [];
foreach($Brands AS $Brand) {
array_push($brandInProductCategory[$id], [
"id" => $Brand->getId(),
"title_fa" => $Brand->getTitleFa(),
"title_en" => $Brand->getTitleEn()
]);
}

query including many tables cakephp 3

Using cakephp 3 I want to have the same result that I will have if I execute the query below (Get all paiements of appartements that belongs to Complex XX) :
SELECT p.*
FROM immeubles i inner join appartements a on i.id=a.immeuble_id
inner join paiements p on p.appartement_id = a.id
where i.complex_id=4
tables used are :
Immeubles (id,name,complex_id(Foreign Key));
Appartements(id, num,immeuble_id(foreign key))
paiements(id,montant,appartement_id(foreign key))
I tried first to extract all appartements that belongs to complex=4 by using the code below but i don't know how to concatenate the results with the table Paiements in order to show all fields of paiements :
$this->Appartements->find()
->join([
'i' => [
'table' => 'Immeubles',
'type' => 'inner',
'conditions' => [
'i.id=Appartements.immeuble_id',
'i.complex_id' => 4
]]]);

CakePHP 3.0 how to contain a left outer join

Suppose that Candidates has one field named Ratings.
The association is optional.
I want all Candidates having a rating other than 0 or no rating at all
and to contain the ratings in the result.
I managed to get the right query for the first part by coding a left outer join like this:
$this->Candidates
->find('all')
->leftJoin(
['Ratings' => 'ratings'],
[
'Ratings.candidate_id = Candidates.id',
'Ratings.rating = 0',
],
['Ratings.rating' => 'integer'])
->where('Ratings.id IS NULL');
However I failed to get the rating contained within the result. How can that be done?
Did you add contain() to the query?
$this->Candidates
->find('all')
->contain('Ratings')
->leftJoin(
['Ratings' => 'ratings'],
[
'Ratings.candidate_id = Candidates.id',
'Ratings.rating = 0',
],
['Ratings.rating' => 'integer'])
->where('Ratings.id IS NULL');

SQL join and group items as an array

I have the following SQL
SELECT articles.id, articles.title, tags.name AS tags
FROM articles
LEFT JOIN article_tag_association ON articles.id = article_tag_association.article_id
LEFT JOIN tags ON tags.id = article_tag_association.tag_id
This works OK except it creates a row for each tag an article has, which messes with the limit
e.g.
[
"0" => ["id" => "1", "title" => "test", "tags" => "tag1"],
"1" => ["id" => "1", "title" => "test", "tags" => "tag2"],
"2" => ["id" => "2", "title" => "test2", "tags" => "tag1"],
]
(only 2 articles but three rows)
is there a way to make it return each article with an array of tags?
something like:
[
"0" => ["id" => "1", "title" => "test", "tags" => ["tag1", "tag2"]],
"1" => ["id" => "2", "title" => "test2", "tags" => ["tag1"]],
]
by default you cannot return an array. but you can decorate/concatenate your columns to produce an array like string. if its a good idea? depends on your situation. Also please be aware MySQL has some limitations for group_concat (will only return 1024*chars)
anyway just for test purpose you can try this:
SELECT
concat(
'[',
concat('ID => "', articles.id,'"'),
concat('Title => "', articles.title,'"'),
concat('Tags => [', GROUP_CONCAT(concat('"',tags.name, '"')), ']'),
']'
) as Array_String
FROM
articles
LEFT JOIN
article_tag_association ON articles.id = article_tag_association.article_id
LEFT JOIN
tags ON tags.id = article_tag_association.tag_id
GROUP BY articles.id
this will give you each row as an array, if you want everything in one line put them all under a group_concat.
note: if your result is larger than 1024 char you have to use
SET group_concat_max_len = 1000000; >> size of your string length
PS: haven't tested above code. test it :)
SELECT articles.id, articles.title, GROUP_CONCAT(tags.name) AS tags
FROM articles
LEFT JOIN article_tag_association ON articles.id = article_tag_association.article_id
LEFT JOIN tags ON tags.id = article_tag_association.tag_id
GROUP BY articles.id
You can't return an array in mysql, but you can get this concatenated string and split it into an array on PHP side. You can choose an character used for 'glue' by GROUP_CONCAT(name SEPARATOR '#') for a one that should not appear in any name and thus be safe for splitting into the array.

Merging Many to Many Results

I'm using the following SELECT statement:
SELECT *
FROM prefix_site_tmplvars
LEFT JOIN prefix_site_tmplvar_contentvalues
ON prefix_site_tmplvar_contentvalues.tmplvarid = prefix_site_tmplvars.id
LEFT JOIN prefix_site_content
ON prefix_site_tmplvar_contentvalues.contentid = prefix_site_content.id
WHERE prefix_site_tmplvar_contentvalues.value = "chocolate"
This is what I get back:
[id] => 2
[name] => flavor
[value] => chocolate
[id] => 2
[name] => type
[value] => cookie
This is the result I'd like to get:
[id] => 2
[flavor] => chocolate
[type] => cookie
Is there a way to combine my results so I don't have a bunch of rows referring to the same ID? If now, how should I handle this?
I'm using Modx and this is working with the Template Variable tables: http://wiki.modxcms.com/index.php/Template_Variable_Database_Tables
You can just use case statements:
SELECT
id,
MAX( CASE WHEN name = 'flavor' THEN value ELSE NULL END ) AS flavor,
MAX( CASE WHEN name = 'type' THEN value ELSE NULL END ) AS type
FROM prefix_site_tmplvars
LEFT JOIN prefix_site_tmplvar_contentvalues
ON prefix_site_tmplvar_contentvalues.tmplvarid = prefix_site_tmplvars.id
LEFT JOIN prefix_site_content
ON prefix_site_tmplvar_contentvalues.contentid = prefix_site_content.id
WHERE prefix_site_tmplvar_contentvalues.value = "chocolate"
GROUP BY id
Of course, this approach only works if you know ahead of time what keys you want to select; but it seems like in this case you do know (flavor + type).
Problem is, you might have
{ "id": 2, "flavor": "chocolate", "type": "cookie" }
and another row with
{ "id": 3, "flavor": "vanilla", "calories": "375" }
...I don't think there's an easy way to solve the problem in MySQL; you'd need to decide what keys to look for.
On the other hand you can collapse the rows in PHP:
while($tuple = ...fetch tuple from mysql cursor...)
{
list ($id, $name, $value) = $tuple;
if (!isset($objs[$id]))
{
// You might want to add also "'id' => $id" to the array definition
$objs[$id]= array ($name => $value);
}
else
{
$obj = $objs[$id];
$obj[$name] = $value;
$objs[$id] = $obj;
}
}
Now $objs contains the desired data; you still need to get it back into Modx, though.
You can do this with a group by:
SELECT id,
max(case when name = 'flavor' then value end) as flavor,
max(case when name = 'type' then value end) as type
FROM prefix_site_tmplvars LEFT JOIN
prefix_site_tmplvar_contentvalues
ON prefix_site_tmplvar_contentvalues.tmplvarid = prefix_site_tmplvars.id LEFT JOIN
prefix_site_content ON prefix_site_tmplvar_contentvalues.contentid = prefix_site_content.id
WHERE prefix_site_tmplvar_contentvalues.value = "chocolate"
group by id