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
Related
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',
],
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()
]);
}
I have the following query, where I want to retrieve the earliest/latest date for each group.
Each group can have many members who are travelling at different dates, so I want to know what is the earliest date within that group between those members. Similarly this is done with all existing groups and then displayed in the chosen order.
This is the query I'm using:
public function sortByDate($order, $year)
{
$this->db->select('groups.*, general_info.proposed_date_of_arrival');
$this->db->from('groups');
$this->db->join('general_info', 'general_info.group_id = groups.group_id');
$this->db->where('YEAR(groups.group_year)', $year);
$this->db->group_by('groups.group_id');
$this->db->order_by("general_info.proposed_date_of_arrival", $order);
$val = $this->db->get();
}
But the above only returns and compares the latest/most recent entries to each group, which isn't the type of query that should be generated.
An output of the results:
array (size=2)
0 =>
array (size=5)
'group_id' => string '2' (length=1)
'group_leader_name' => string 'Bob' (length=7)
'group_year' => string '2013-11-01' (length=10)
'year' => string '2013' (length=4)
'proposed_date_of_arrival' => string '2014-02-22' (length=10)
1 =>
array (size=5)
'group_id' => string '1' (length=1)
'group_leader_name' => string 'John' (length=7)
'group_year' => string '2013-11-12' (length=10)
'year' => string '2013' (length=4)
'proposed_date_of_arrival' => string '2014-12-15' (length=10)
There are actually two members each in these two test groups, the earliest in group 1 having a proposed date of arrival of 2013-12-31 and the earliest in group 2 being 2014-01-19.
The actual output of ordering in ascending order should be the group with the date of 2013-12-31 being first, and the group with the date of 2014-01-19 being next. However, I get the above array result instead, which is comparing the two most recent members instead of taking into account all the members proposed dates. How can I compare between all the members dates?
I think this is the solution
public function sortByArrivalDateASC($year)
{
$this->db->select('groups.*, MIN(date(general_info.proposed_date_of_arrival)) as proposed_date_of_arrival');
$this->db->from('groups');
$this->db->join('general_info', 'general_info.group_id = groups.group_id');
//$this->db->where('YEAR(groups.group_year)', $year);
$this->db->group_by('group_id');
$val = $this->db->get();
}
I was missing an aggregate function to group by. I figured I don't really need to implement a descending order, since we only want to know the earliest time of departure.
Thanks to GROUP_BY MySQL reference and this w3schools explanation
I have a sql query that looks like so:
public function get_data() {
$db = new databaseConnection();
$db->do_connection();
$this->uid = $_SESSION['uid'];
$query = "SELECT ut.level, ut.location, ui.quantity, ui.game_item_id
FROM user_table ut, user_inventory ui
WHERE ut.user_id = ui.user_id
AND ut.user_id = :user_id";
$args = array(
":user_id" => $this->uid
);
$db->safeQuery($query,$args);
$results = $db->safe_fetch_all_results();
if($results == null){
die ('Error with user data fetch');
}
var_dump($results);
return $results;
}
The query works as i want, but for some reason the var_dump shows the data coming back as this:
array (size=2)
0 =>
array (size=8)
'level' => string '1' (length=1)
0 => string '1' (length=1)
'location' => string '4554' (length=4)
1 => string '4554' (length=4)
'quantity' => string '2' (length=1)
2 => string '2' (length=1)
'game_item_id' => string '1' (length=1)
3 => string '1' (length=1)
1 =>
array (size=8)
'level' => string '1' (length=1)
0 => string '1' (length=1)
'location' => string '4554' (length=4)
1 => string '4554' (length=4)
'quantity' => string '1' (length=1)
2 => string '1' (length=1)
'game_item_id' => string '5' (length=1)
3 => string '5' (length=1)
As you can see for each record being retrieved by the query, a duplicate is also being retrieved, one with the field name and one with an index number if you wish.
I get the same result when i do a nested foreach statement, i get the duplicate results.
Here is the structure of my tables:
user_table table
user_ID PK
username
password
email
level
location
game_items table
game_item_is PK
item_name
user_inventory table
user_inventory_id PK
user_id FK
game_item_id FK
quantity
Would you guys happen to know why this is happening?
Thanks
That's standard behaviour...
If you Fetch the data from the resultset, you can choose to retrieve it by association (FETCH_ASSOC), by index, or both (FETCH_BOTH)
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