Struggling With DQL and Case When Expression - php

I am attempting the following query and am having some trouble with the count(CASE WHEN r.status=2 THEN 1 ELSE 0 END) part.
I really need to capture the count of status's where they are equal to "complete". I tried the following but I keep getting: Expected Doctrine\ORM\Query\Lexer:T_FROM, got '.'
can someone help?
$em = $this->getDoctrine()->getManager();
$query = $em->createQuery(
'SELECT count(r.rpId) AS referralCount, count(CASE WHEN r.status=2 THEN 1 ELSE 0 END) as referralCompleted, p.firstName, p.lastName, p.npi, u.username
FROM MainReferralCaptureBundle:Referral r, MainReferralCaptureBundle:Physician p, MainUserBundle:User u
WHERE r.valid = 1
AND r.rpId = p.id
AND r.submittedBy = u.id
AND r.createdDate BETWEEN :startdate AND :enddate
GROUP BY p.id')
->setParameter('startdate', $form->get('startdate')->getData())
->setParameter('enddate', $form->get('enddate')->getData());

You're not grouping by all non-aggregate fields from your SELECT statement. That doesn't cause an error in some rdbms but it does cause results to be rubbish.
Additionally:
count(CASE WHEN r.status=2 THEN 1 ELSE 0 END)
Will count all records due to the ELSE 0. Remove the ELSE altogether and it will only count when the condition is met. Likewise you could change the ELSE 0 to ELSE NULL. NULL is excluded from aggregates. You could also change COUNT() to SUM().
That's all I notice from the query itself, not sure what the error you're getting is about.

Related

function results dont match query results php

EDIT: the responses work well but not quite what i was after i dont think i explained it very well, i just want to return 1 result per function so count how many odd numbers are in the result and count how many even numbers are in the result?
sql fiddle for the data
http://sqlfiddle.com/#!9/dfbfa7/1
Im trying to count even and odd numbers by splitting the the results using 2 functions but my results dont match up to the results in the mysql table.
data
select number from table group by number
results
1,4,6,7,8,9,11,12
function to return odd number count
SELECT
COUNT(CASE WHEN (number% 2) > 0 THEN number ELSE NULL END) as odd
FROM test.table
WHERE date = CURDATE() and time > now() - interval 60 second group by number
LIMIT 1
and the function to return even number count
SELECT
COUNT(CASE WHEN (number% 2) = 0 THEN number ELSE NULL END) as even
FROM test.table
WHERE date = CURDATE() and time > now() - interval 60 second group by number
LIMIT 1
i was hoping the results would be
odd = 4
even = 4
but instead im getting
odd = 8
even = 0
where am i going wrong?
Maybe try something like thise.
For even numbers:
COUNT(SELECT * FROM `table` WHERE mod(number, 2)=0);
For odd numbers:
COUNT(SELECT * FROM `table` WHERE mod(number, 2)>0);
This will return the only number of odd and even numbers, then collect those numbers with php.
COUNT will add up all the lines it traverses
In your case you must use SUM:
SUM(CASE WHEN (number% 2) > 0 THEN 1 ELSE 0 END) as odd
SUM(CASE WHEN (number% 2) = 0 THEN 1 ELSE 0 END) as even
for simple operations the IF is much cleaner than the CASE:
SUM(IF(number % 2 > 0, 1, 0)) as odd
SUM(IF(number% 2 = 0, 1, 0)) as even
The reason your query is returning wrong result is probably because you are using limit 1 try removing it also if you have to use group by use group by number in before you do calculations for this query, i have altered the query here for example.
SELECT
count(CASE WHEN (a.number% 2) = 0 THEN a.number ELSE NULL END) as even,
count(CASE WHEN (a.number% 2) <> 0 THEN a.number ELSE NULL END) as odd
FROM (select number from test group by number) a;
http://sqlfiddle.com/#!9/dfbfa7/22
for the set up you have created im getting 5 even and 5 odd in results, may be you have to recheck the condition you have in where clause if your results are different with actual data.

how can i select data and sub query group by

When i select data only show first lot data . but i need all lot data.
Here is my query:
SELECT lot,
(select count(pass) FROM pass_fail_result where pass=0) toatl_fail,
(select count(pass) FROM pass_fail_result where pass=1) toatl_pass FROM pass_fail_result group by lot;
I want to show all pass result like pass=10 and fail=2
The easiest way to do this is via conditional aggregation, where we count or sum CASE expressions which target the failing or passing records:
SELECT
lot,
COUNT(CASE WHEN pass = 0 THEN 1 END) AS toatl_fail,
COUNT(CASE WHEN pass = 1 THEN 1 END) AS toatl_pass
FROM pass_fail_result
GROUP BY
lot;

Refactoring multiple mysql queries into 1

I am currently developing a system which generates various reports for a client and i need pointing in the direction in-order to achieve what i need.
The current situation is that i need to produce an overview of tasks which are outstanding (and some details about what is remaining).
So far i have created a list of all tasks so far (including information about the customer), this was achieved in the following query:
SELECT `fusion_repairs`.*, `fusion_customers`.*
FROM `fusion_repairs`
RIGHT JOIN `fusion_customers`
ON `fusion_repairs`.`customer_id` = `fusion_customers`.`customer_id`
ORDER BY `fusion_repairs`.`repair_id`
DESC LIMIT 20
Ideally i need to be able to check to see if the customer has been emailed about the task (fusion_mail table). If there are no mail records found for this task then i still want to show it (so another right join is out of the question.
I also need to do the same with the fusion_response table to check to see if the customer has sent in a response. If they haven't then i still want to display the task anyway.
Obviously i know i can achieve this by running a query within the main select query, but this doesn't sit well in my head. Is there a way i can do this in 1 query instead?
Thank you.
Change your query structure to use left joins only - essentially the same as a right join but then not having to swap between left and right join.
The fact you right-joined customers onto repairs tells me customers is the main table so now we can say get me everything from customers, join in repairs if a record exists.
SELECT `fusion_repairs`.*, `fusion_customers`.*
FROM `fusion_customers`
LEFT OUTER JOIN `fusion_repairs` ON `fusion_repairs`.`customer_id` = `fusion_customers`.`customer_id`
ORDER BY `fusion_repairs`.`repair_id`
DESC LIMIT 20
Here we can use left outer join to see if we have any mail, if not, do not remove the row and the same with response
SELECT `fusion_repairs`.*, `fusion_customers`.*
FROM `fusion_customers`
LEFT OUTER JOIN `fusion_repairs` ON `fusion_repairs`.`customer_id` = `fusion_customers`.`customer_id`
LEFT OUTER JOIN `fusion_mail` ON `fusion_mail`.`repair_id` = `fusion_repairs`.`repair_id`
LEFT OUTER JOIN `fusion_response` ON `fusion_response`.`repair_id` = `fusion_repairs`.`repair_id`
ORDER BY `fusion_repairs`.`repair_id`
DESC LIMIT 20
Now my question is.. What do you want to be returned? If there are 5 mail records then there will be 5 records for the 1 repair record. Do you just want a yes/no tick just to say they have then maybe a correlated subquery in the main select is actually what you want or a group by and count the number of mail inside a case to tick.
Let me know what you want as your output to finish the query up.
EDIT:
I've made an update to the query to take into account the information you gave me. Please note I have no idea how you want to output the final result so I just made a guess! Let me know if this solves it otherwise back to the drawing board for me!
SELECT fusion_repairs.*,
fusion_customers.*,
CONCAT( CASE WHEN fusionMail.mail_type_A > 0 THEN 'displayA' ELSE '' END,
CASE WHEN fusionMail.mail_type_B > 0 THEN 'displayB' ELSE '' END,
CASE WHEN fusionMail.mail_type_C > 0 THEN 'displayC' ELSE '' END) as email_font_awesome_icon,
CONCAT( CASE WHEN fusionResponse.response_status_A > 0 THEN 'displayA' ELSE '' END,
CASE WHEN fusionResponse.response_status_B > 0 THEN 'displayB' ELSE '' END,
CASE WHEN fusionResponse.response_status_C > 0 THEN 'displayC' ELSE '' END) as response_font_awesome_icon
FROM fusion_customers
LEFT OUTER JOIN fusion_repairs ON fusion_repairs.customer_id = fusion_customers.customer_id
LEFT OUTER JOIN (
SELECT repair_id,
SUM(CASE WHEN mail_type = 1 THEN 1 else 0 END) AS mail_type_A,
SUM(CASE WHEN mail_type = 2 THEN 1 else 0 END) AS mail_type_B,
SUM(CASE WHEN mail_type = 3 THEN 1 else 0 END) AS mail_type_C
FROM fusion_mail
GROUP BY repair_id
) fusionMail ON fusionMail.repair_id = fusion_repairs.repair_id
LEFT OUTER JOIN (
SELECT repair_id,
SUM(CASE WHEN response_status = 1 THEN 1 else 0 END) AS response_status_A,
SUM(CASE WHEN response_status = 2 THEN 1 else 0 END) AS response_status_B,
SUM(CASE WHEN response_status = 3 THEN 1 else 0 END) AS response_status_C
FROM fusion_response
GROUP BY repair_id
) fusionResponse ON fusionResponse.repair_id = fusion_repairs.repair_id

Doctrine Query Builder Select Count with Case

I have the following sql-statement that I want to transform into doctrine query builder. The goal is to count how many ratings exist with rating value 1 and with rating value 2.
SELECT
COUNT(CASE WHEN rating.rating = 1 THEN rating.rating END) as rat1,
COUNT(CASE WHEN rating.rating = 2 THEN rating.rating END) as rat2
FROM rating
This sql statement is working fine - but when I try to transform it into a Doctrine statement, it does not anymore. When nothing should get counted (because no ratings for this value exist), it returns me a "1" instead of a 0. How can I tell doctrine to simply return a zero when there is nothing to count?
I tried it by removing the "ELSE 0" , but then I get an error that this part is required..
return $qb
->addSelect('COUNT(CASE WHEN r.rating = 1 THEN r.rating ELSE 0 END) as rat_1')
->addSelect('COUNT(CASE WHEN r.rating = 2 THEN r.rating ELSE 0 END) as rat_2')
->getQuery()
->getResult();
Regards,
"sum" is not required - example:
votings 2,2,2,2,2 should return 5 , because the rating with value 2 got voted 5 times.
To count distinct id's in one column depending on the value in another column the answer from Fuzzy Tree does not work in this case.
To count only the distinct values you should give null as a parameter and set it to null like:
->addSelect('COUNT(DISTINCT(CASE WHEN r.rating = 1 THEN rating.rating ELSE :nada END)) as rat_1')
->setParameter(':nada', null)
As vkp mentioned, you can use SUM but instead of summing the rating, sum either a 1 or a 0 to simulate a COUNT
SUM(CASE WHEN r.rating = 1 THEN 1 ELSE 0 END)

MySQL alias calculation

It confuses me.. total left calculation seems doesn't work.
I am trying to get the total voucher and get total used and the total left.
please help.
SELECT
(IFNULL(SUM(value), 0)) AS total_voucher,
(
SELECT
IFNULL(SUM(value), 0))
FROM
voucher_history
WHERE
idUser = 1 AND isUsed = 1 AND DATE(FROM_UNIXTIME(datetime)) = '2014-03-04'
) AS total_used,
(total_voucher-total_used) AS total_left
FROM
voucher_history
WHERE
idUser = 1 AND isUsed = 0 AND DATE(FROM_UNIXTIME(datetime)) <= '2014-03-05'
You can do this with conditional aggregation, rather than using a subquery:
SELECT coalesce(SUM(value), 0)) AS total_voucher,
sum(case when is_used = 1 then value else 0 end) as total_used,
sum(case when is_used = 1 then 0 else value end) as total_left
FROM voucher_history
WHERE idUser = 1 AND DATE(FROM_UNIXTIME(datetime)) <= '2014-03-05';
Your query has the problem that it is trying to use column aliases (total_voucher and total_used) in the same select statement. SQL does not support this. You would need to use a subquery to get that functionality.

Categories