Merging Many to Many Results - mysql

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

Related

mysql - select result of two conditionals for the same table in one query

I am struggling to get correct results with this. I want to test if both, or either, exist. In results table, 'michael' exists while 'mike' does not.
$stmt = $dbnet->prepare("
SELECT * FROM
(SELECT cats AS cats1 FROM results WHERE name = :original) AS a,
(SELECT cats AS cats2 FROM results WHERE name = :parsed) AS b
");
$binding = array(
'original' => 'michael',
'parsed' => 'mike'
);
$stmt->execute($binding);
$results = $stmt->fetchAll(PDO::FETCH_ASSOC);
//if there was a result then output
if($results)
{
echo '<pre>'.print_r($results,1).'</pre>';
}
I get no results with this even though 'michael' is in the database.
If I test for 'original' => 'michael', 'parsed' => 'michael' I get results...both the same of course since I tested the same value for each :
Array
(
[0] => Array
(
[cats1] => 6,11
[cats2] => 6,11
)
)
What I expect is one of the following :
no results meaning neither michael or mike exist
result for cats1 and empty for cats2 (michael exists mike does not)
empty for cats1 and result for cats2 (mike exists michael does not)
No, I cannot use WHERE name = 'michael' OR name = 'mike' because what I do after changes depending if both have results or just one or the other.
This is working as epxected... both always needs a result even if that is empty to return correctly.
$stmt = $dbnet->prepare("
SELECT
IFNULL( (SELECT cats AS cats1 FROM results WHERE name = :original), '') AS a,
IFNULL( (SELECT cats AS cats2 FROM results WHERE name = :parsed), '') AS b
");

Yii 1.1 model relation can't use joined table column on select

This is my class
Class System extends CActiveRecord
{
public function relations() {
return array(
'SystemInverterModuleMountGrouped' => array(self::HAS_MANY, 'SystemInverterModule', array('id' => 'system_inverter_id'),
'through' => 'SystemInverter',
'group' => 'system_id, SystemInverterModuleMountGrouped.mount_id',
'condition' => 'SystemInverterModuleMountGrouped.mount_id is not null AND SystemInverterModuleMountGrouped.mount_id != "" AND number_of_mounts > 0 AND
CASE WHEN Mount.unit_of_measure = 1 THEN
SystemInverterModuleMountGrouped.existing_array != 1
ELSE true
END',
'select' => '*, '
. 'SUM(SystemInverterModuleMountGrouped.number_of_mounts) AS number_of_mounts_grouped, ',
'with' => 'Mount'
)
);
}
}
This is working fine, but now I want to sum number_of_mounts in a certain condition
array(
'select' => '*, '
. 'IF(Mount.unit_of_measure IN (1,2,3), 0, SUM(SystemInverterModuleMountGrouped.number_of_mounts)) AS number_of_mounts_grouped, ',
)
It doesn’t work and yii throws an error
Active record “SystemInverterModule” is trying to select an invalid column “IF(Mount.unit_of_measure IN (1”. Note, the column must exist in the table or be an expression with alias.
Notice that I’m able to use Mount.unit_of_measure on the condition
'condition' => 'CASE WHEN Mount.unit_of_measure = 1 THEN '
It works with raw sql query
SELECT IF(Mount.unit_of_measure IN (1,2,3), 0, SUM(SystemInverterModuleMountGrouped.number_of_mounts)) AS number_of_mounts_grouped
FROM `SystemInverterModules` `SystemInverterModuleMountGrouped`
LEFT OUTER JOIN `SystemInverter` `SystemInverter`
ON (`SystemInverter`.`id` = `SystemInverterModuleMountGrouped`.`system_inverter_id`)
LEFT OUTER JOIN `Inventory` `Mount`
ON (`SystemInverterModuleMountGrouped`.`mount_id` = `Mount`.`id`)
WHERE (SystemInverterModuleMountGrouped.mount_id is not null AND SystemInverterModuleMountGrouped.mount_id != "" AND
number_of_mounts > 0 AND
CASE WHEN Mount.unit_of_measure = 1 THEN
SystemInverterModuleMountGrouped.existing_array != 1
ELSE true END
)
AND (`SystemInverter`.`system_id` = '42146')
GROUP BY system_id, SystemInverterModuleMountGrouped.mount_id
ORDER BY sort_mounting ASC
You should use array for declaring select in this case - string format does not work well for complicated expressions:
'select' => [
'*',
'IF(Mount.unit_of_measure IN (1,2,3), 0, SUM(SystemInverterModuleMountGrouped.number_of_mounts)) AS number_of_mounts_grouped',
],

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()
]);
}

Mysql Join rows from multiple tables

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

MySQL show all columns while using WHERE clause

Sorry if this is a silly question but did not use much SQL lately and can't find much help on this either.
I need to get all rows to show in a result even if the result is 0 or null. The problem I have is because of the WHERE clause (without the WHERE the rows are all displayed but the data is not).
SELECT SUM(c.amount) AS 'total_income', p.ref_id AS 'property'
FROM property p
LEFT JOIN contract c
ON p.id = c.property_ref_id
LEFT JOIN contract_payment cp
ON c.id = cp.contract_id
WHERE cp.paid = 1 AND year(cp.date_paid) = :year
GROUP BY p.id
Displaying the result set without the Where and Bad Data would be like this
array
0 =>
array
'total_income' => null
'property' => string 'test/0001' (length=9)
1 =>
array
'total_income' => null
'property' => string 'test/0002' (length=9)
2 =>
array
'total_income' => string '200' (length=3)
'property' => string 'test/0003' (length=9)
3 =>
array
'total_income' => string '16100' (length=5)
'property' => string 'test/0004' (length=9)
While this is the result set with the WHERE clause and Good Data but not all rows
array
0 =>
array
'total_income' => string '4200' (length=4)
'property' => string 'test/0004' (length=9)
Could someone please enlighten me to what modifications could be made to the SQL so as to retrieve my desired data ?
The problem is that you're filtering out all the rows with no match in the cp table, because cp.paid can't be 1 when there's no match there.
Change the WHERE clause to:
WHERE cp.contract_id IS NULL OR (cp.paid = 1 AND year(cp.date_paid) = :year)
or move that condition into the ON clause of the LEFT JOIN with cp:
ON c.id = cp.contract_id AND cp.paid = 1 AND year(cp.date_paid) = :year