PHP/SQL select based on sum result - php

I'm having a hard time limiting my query results based on sum. Example code:
$rows = Entry::find()
->section('cities')
->select('state')
->having("sum(case when covered = '1' then 1 else 0 end) = 0")
->asArray()
->all();
I want to only select states where 0 cities are "covered". Running my code above the "having" line seems to be ignored (it includes both zero and non-zero cases). I tried using "where" instead of "having" but it results in a PDOException - "Invalid use of group function". I assume "having" is the right approach, but that I'm making a novice mistake -- any tips?
/*** UPDATE 1 ***/
Thanks #scaisEdge and #angelm for the tip -- groupBy helps, but it's still not working as expected. It seems the "having" line is still ignored. With modified code:
$test = Entry::find()
->section('cities')
->select(['state', 'covered', "sum(case when covered = '1' then 1 else 0 end) as numCovered"])
->groupBy('state')
->having("sum(case when covered = '1' then 1 else 0 end) = 0")
->asArray()
->all();
I log the following results:
{state: "AL", covered: "0", numCovered: "0"}
{state: "AK", covered: "0", numCovered: "0"}
{state: "CA", covered: "1", numCovered: "19"}
{state: "CO", covered: "0", numCovered: "0"}
...
As you can see above, states (CA) are included when numCovered is clearly not 0.
I also tried the following code for "having" (which I assume is the same):
->having("numCovered = 0")
/*** UPDATE 2 ***/
Using a reduced test case suggested by #cpalmer still results in "CA" being selected despite having numCovered = 19. I'm now wondering if this is a peculiarity with Craft CMS, since it would seem my query is correct?
$test = Entry::find()
->section('cities')
->select('state')
->groupBy('state')
->having("sum(case when covered = '1' then 1 else 0 end) = 0")
->asArray()
->all();
Is there a way to write this query without having?
/*** UPDATE 3 ***/
As suggested by the DB Fiddle posted by #pocketrocket my sql should work. Dumping the raw sql suggests the having line is ignored. The issue likely resides with CraftCMS/Yii and my lack of understanding of the environment.

First of all: I am total with #Olivier, you should decouple the SQL part of your question from the architecture itself. For the SQL part, it's important to let others know which database or SQL dialect you use (MySQL, PostgreSQL, MsSQL...).
Just guessing it's MySQL what you are using:
Both ways should actually work, repeating the querypart in having or referencing it by name as suggested by user126587
If both don't work and you would like to work without having maybe you can implement a subselect?
SELECT
state,
sum(case when covered = '1' then 1 else 0 end) as numCovered
FROM cities
GROUP BY state
HAVING sum(case when covered = '1' then 1 else 0 end) = 0;
SELECT
state,
sum(case when covered = '1' then 1 else 0 end) as numCovered
FROM cities
GROUP BY state
HAVING numCovered = 0;
SELECT * FROM (
SELECT
state,
sum(case when covered = '1' then 1 else 0 end) as numCovered
FROM cities
GROUP BY state
) sub_select
WHERE sub_select.numCovered = 0;
You can play around with it here: DB Fiddle Link

try add a groupBy() for state
$rows = Entry::find()
->section('cities')
->select('state')
->groupBy('state')
->having("sum(case when covered = '1' then 1 else 0 end) = 0")
->asArray()
->all();

Try removing 'covered' from your selected columns. In some SQL databases, when you group your data, simply having a column in your select statement that isn't in the group by statement or in an aggregate can cause an error. In your case, I think this column is causing the unexpected results. If you really want to include it, throw it into an aggregate and alias it, like 'MAX(covered) as covered'
$test = Entry::find()
->section('cities')
->select(['state', "sum(case when covered = '1' then 1 else 0 end) as numCovered"])
->groupBy('state')
->having("sum(case when covered = '1' then 1 else 0 end) = 0")
->asArray()
->all();

Try putting in the having part "numCovered = 0".

Well my guess is you are forgetting a single quote around the 0 in your having clause.
$test = Entry::find()
->section('cities')
->select('state')
->groupBy('state')
->having("sum(case when covered = '1' then 1 else 0 end) = '0'")
->asArray()
->all();

Related

WHERE NOT IN AND WHERE error

I tried moving the WHERE clause many times but I'm still having an error. Am I doing the WHERE NOT IN AND WHERE clause wrong? The code is working perfectly fine until I added the WHERE ha_rooms.deleted != 1 clause. I also tried using deleted <> 1 but it still shows the same error
$query2 = $this->db->query("SELECT * FROM ha_rooms
WHERE ha_rooms.deleted != 1 JOIN ha_user_room_merged
WHERE ha_rooms.room_id NOT IN (SELECT ha_user_room_merged.room_id
FROM ha_user_room_merged WHERE ha_user_room_merged.deleted = 0)
group by ha_rooms.room_id");
The error is this
You have an error in your SQL syntax; check the manual that
corresponds to your MySQL server version for the right syntax to use
near 'JOIN ha_user_room_merged WHERE ha_rooms.room_id NOT IN (SELECT
ha_user_room_merg' at line 1
If I said frankly, your query is not done properly in anyway but it gives pretty much clear vision of what you are trying to do if you don't have any other logic behind that. So, I would like to suggest you to do google for SQL Syntax, Ordering, use of GROUP BY, WHERE sub-query etc. BTW below is the proper version of your query please check if it's useful for you
SELECT hr.*
FROM ha_rooms hr
JOIN ha_user_room_merged hurm On hurm.room_id = hr.room_id
WHERE hurm.deleted = 0
AND hr.deleted <> 1
Use below codeigniter query. in your join there is no ON
$all_ids = 0;
$this->db->select('group_concat(room_id) as ids');
$this->db->where('deleted', 0);
$ids = $this->db->get('ha_user_room_merged')->row_array();
if($ids) {
$all_ids = explode(',', $ids['ids']);
}
$this->db->where('hr.deleted !=', 1);
$this->db->where_not_in('hr.room_id', $all_ids);
$this->db->join('ha_user_room_merged hrm', 'hrm.room_id = hr.room_id');
$this->db->group_by('hr.room_id');
$this->db->get('ha_rooms hr')->result_array();
Please try..
select * from ha_rooms as a inner join ha_user_room_merged b on a.ha_rooms.id = b.room_id
where b.deleted <> 0
Output
ha_rooms.room_id ha_user_room_merged.room_id deleted
1 1 1
2 2 1
3 3 1
4 4 1
Thanks :)

Conditionally Count a MYSQL Column Rows and Output Data via PHP

Sorry i am new to this. Just trying to learn. I am trying to conditionally count the number of times a particular condition occurs in SQL, using the case and count functions. This counts the number of males/females stored in eeg table. Here is my SQL query.
SELECT COUNT(CASE WHEN `Gender` = 'Male' THEN 1 END),
COUNT(CASE WHEN `Gender` = 'Female' THEN 1 END)
FROM `eeg`
This outputs the data when i run the query on the mysql backend (phpmyadmin), but in my php file, I get an "Undefined Index" error for those 2 rows. All othjer rows are perfectly okay. I do not know how to output those particular set of data to a variable.
Here is the SQL query (in full) in the php file:
$result = mysql_query("SELECT MONTH(ScanDate), YEAR(ScanDate),
COUNT(Investigation),
COUNT(CASE WHEN `Gender` = 'Male' THEN 1 END),
COUNT(CASE WHEN `Gender` = 'Female' THEN 1 END),
SUM(InvestigationAmount), SUM(AmountDue)
FROM eeg
WHERE Investigation = '{$investigation}'
AND ScanDate BETWEEN '{$ScanDate1}'
AND '{$ScanDate2}'");
Here is the while loop (in full):
while($row=mysql_fetch_array($result)){
$month_doe=$row['MONTH(ScanDate)'];
$year_doe=$row['YEAR(ScanDate)'];
$si=$row['COUNT(Investigation)'];
$male=$row["COUNT(CASE WHEN 'Gender' = 'Male' THEN 1 END)"];
$female=$row["COUNT(CASE WHEN 'Gender' = 'Female' THEN 1 END)"];
$sum_investigation=number_format($si);
$sia=$row['SUM(InvestigationAmount)'];
$sum_investigationamount=number_format($sia);
$srd=$row['SUM(AmountDue)'];
$sum_rebatedue=number_format($srd);
}
Thank you for your help. Been literally pulling my hair out, but love to learn and improve. And yes, mysql_query is depreciated :D
screenshots below:
Code screenshot
Use an alias for the expressions and use the alias to access the results of the expressions from php:
$result = mysql_query("SELECT MONTH(ScanDate) as sdyear,
YEAR(ScanDate) as sdmonth,
COUNT(Investigation) as investigation,
COUNT(CASE WHEN `Gender` = 'Male' THEN 1 END) as MaleCount,
COUNT(CASE WHEN `Gender` = 'Female' THEN 1 END) as FemaleCount,
SUM(InvestigationAmount) as investigationamount,
SUM(AmountDue) as amountdue
FROM eeg
WHERE Investigation = '{$investigation}'
AND ScanDate BETWEEN '{$ScanDate1}'
AND '{$ScanDate2}'");
while($row=mysql_fetch_array($result)){
$month_doe=$row['sdmonth'];
$year_doe=$row['sdyear'];
$si=$row['investigation'];
$male=$row["MaleCount"];
$female=$row["FemaleCount"];
$sum_investigation=number_format($si);
$sia=$row['investigationamount'];
$sum_investigationamount=number_format($sia);
$srd=$row['amountdue)'];
$sum_rebatedue=number_format($srd);
}
I would use this approach for every field that is an expression (the other sum() fields in the above query).

Merge loop of SQL queries into one SQL query

I built a loop in PHP that makes 500 SQL queries but I would like to merge the 500 SQL queries into one and get the same return (the companies and the count of the users on each company)
Example of PHP code
$companies = array();
foreach ($fortune500Service->listAll() as $c ){
$count = $entityManager
->createQueryBuilder()
->select("count(u)")
->from("AppBundle\Entity\User","u")
->where("u.email LIKE :d")
->setParameter("d", "%#" . $c["Domain"])
->getQuery()->getSingleScalarResult();
if ($count == 0) {
continue;
}
$companies[] = array(
"Domain" => $c["Domain"],
"Company" => "{$c["Company"]} ({$count})",
);
}
return $companies;
Example of 2 SQL queries that I want to merge
Query 1
SELECT
count(u0_.id)
FROM
user u0_
WHERE
u0_.email LIKE '%#company1.com'
Query 2
SELECT
count(u0_.id)
FROM
user u0_
WHERE
u0_.email LIKE '%#company2.com'
I prefer a solution using createQueryBuilder http://symfony.com/doc/current/book/doctrine.html#querying-for-objects-using-doctrine-s-query-builder but I am happy also with an SQL native query.
Use conditional aggregation:
SELECT COUNT(CASE WHEN u0_.email LIKE '%#company1.com' THEN 1 END) as First_cnt,
COUNT(CASE WHEN u0_.email LIKE '%#company2.com' THEN 1 END) as First_cnt
FROM user u0_

Symfony2 bad response from query with maxresults and firstresult

I've meet a serious problem with setMaxResult and setFirstResult.
When i'm trying to get result without setMaxResults and setFirstResult, it works OK, all rows returned.
When i'm using offset = 0 and limit=10 , it works good, 10 rows returned.
When i'm using offset = 10 and limit = 10 , it return 5 rows (must be 7)
Another example, i've used offset = 0 , limit = 20 ,it returned 15 rows.But it must be 17 rows.
With offset=0 and limit = 30 , it returned all 17 rows .... Why this query works so bad ? With offset = 0 and limit 20, it should have returned all 17 rows... but not 15..
Code :
$eligibleCircles = $this->getAllCircles($user);
$results = $this->getEntityManager()
->createQuery(
'SELECT
e
FROM
TestBundle:Event e
LEFT JOIN
e.eligibleCircles eligibleCircles
WHERE
(
eligibleCircles in (:eligibleCircles)
OR
e.owner = :user
)
AND
e.eventStatus = :eventStatus
AND
NOT EXISTS (
SELECT
eh
FROM
TestBundle:EventHidden eh
WHERE
eh.user = :user
AND
eh.event = e
)
AND
e.startDate < :currentDate
ORDER BY e.startDate DESC
'
)
->setParameter('eventStatus', 3)
->setParameter('eligibleCircles', $eligibleCircles )
->setParameter('user', $user )
->setParameter('currentDate', new \DateTime('now') )
->setFirstResult($offset)
->setMaxResults($limitNr)
->getResult();
Cerad is correct with regard to the sql-limit not being useful when you have a join in your query.
If you want to paginate while using Doctrine2 there are some helpfull tools for you available.
Have a look at the documentation here:
http://doctrine-orm.readthedocs.org/en/latest/tutorials/pagination.html
You need a bit of extra code, but most of the complex stuff is handled for you.
It will also require 1 or 2 additional queries to find the correct records+data. This may or may not be a problem in your situation.

problem in counting two fields in one query

guys i need to count new private messages and old one from a table
so first thing come to mind is using mysql_num_rows and easy thing to do
// check new pms
$user_id = $userinfo['user_id'];
$sql = "SELECT author_id FROM bb3privmsgs_to WHERE user_id='$user_id' AND (pm_new='1' OR pm_unread='1')";
$result = $db->sql_query($sql) ;
$new_pms = $db->sql_numrows($result);
$db->sql_freeresult($result);
// check old pms
$sql = "SELECT author_id FROM bb3privmsgs_to WHERE user_id='$user_id' AND (pm_new='0' OR pm_unread='0')";
$result = $db->sql_query($sql) ;
$old_pms = $db->sql_numrows($result);
$db->sql_freeresult($result);
but how can i count these two fields just in one statement and shorter lines ?~
Use this query instead:
SELECT SUM(CASE WHEN pm_new = '1' OR pm_unread = '1' THEN 1 ELSE 0 END) AS new_pms,
SUM(CASE WHEN pm_new = '0' OR pm_unread = '0' THEN 1 ELSE 0 END) AS old_pms
FROM bb3privmsgs_to
WHERE user_id='$user_id'
Here's a MySQL-specific version that reads more cleanly:
SELECT COUNT(IF(pm_new = '1' OR pm_unread = '1', 1, NULL)) AS new_pms,
COUNT(IF(pm_new = '0' OR pm_unread = '0', 1, NULL)) AS old_pms
FROM bb3privmsgs_to
WHERE user_id='$user_id'
MySQL will cast comparisons to 1 or 0. You can use SUM() to add up the portion of the WHERE clause you were trying to count results for.
This is a (MySQL specific) shorter alternative to the CASE WHEN examples.
SELECT
SUM(pm_new='1' OR pm_unread='1') as new_pms,
SUM(pm_new='0' OR pm_unread='0') as old_pms
FROM bb3privmsgs_to
WHERE user_id='$userid'
In SQL Server, you can do something like this:
SELECT
SUM(CASE WHEN pm_new='1' OR pm_unread='1' THEN 1 ELSE 0 END),
SUM(CASE WHEN pm_new='0' OR pm_unread='0' THEN 1 ELSE 0 END)
FROM
bb3privmsgs_to WHERE user_id='$user_id'
I'll suppose you can do about the same thing in mySql, let me get back to you on the details...
As a lazy alternative to some of the other suggestions:
SELECT SUM(newPMS) AS newPMS,
SUM(oldPMS) AS oldPMS
FROM ( SELECT COUNT(author_id) AS newPMS,
0 AS oldPMS
FROM bb3privmsgs_to
WHERE user_id='$user_id'
AND (pm_new='1' OR pm_unread='1')
UNION
SELECT 0 AS newPMS
COUNT(author_id) AS oldPMS
FROM bb3privmsgs_to
WHERE user_id='$user_id'
AND (pm_new='0' OR pm_unread='0')
)

Categories