suppose this
user table:
[id] [mail] [pass]
happen table:
[id] [uid] [date] [content]
vote table
[uid] [hid] [type] [datetime]
1 user can have 0 or more happen, 1 happen can have 0 or more votes..
I want to get the total number of votes for a specific happen
SELECT
H.*,
SUM(CASE WHEN V.type='C' THEN 1 ELSE 0 END) AS upvotes,
SUM(CASE WHEN V.type='R' THEN 1 ELSE 0 END) AS downvotes
FROM
happens H
LEFT JOIN
votes AS V
ON V.hid = H.id
WHERE
H.uid = :uid
the problem is that if no votes is associated on a happen I get a null row from mysql, not EMPTY but NULL
how can I avoid this null result?
[edit]
some try:
basic
SELECT H.* FROM happens H LEFT JOIN votes AS V ON V.hid = H.id WHERE H.uid = '178d937'
result -> empty
All other try with the SUM, COUNT, HAVING ect ect gives:
[id] [uid] [what] [latitude] [longitude] [date] [time] [hide] [upvotes] [downvotes]
NULL NULL NULL NULL NULL NULL NULL NULL 0 0
Use HAVING to check for the null
SELECT
H.*,
SUM(CASE WHEN V.type='C' THEN 1 ELSE 0 END) AS upvotes,
SUM(CASE WHEN V.type='R' THEN 1 ELSE 0 END) AS downvotes
FROM
happens H
LEFT JOIN
votes AS V
ON V.hid = H.id
WHERE
H.uid = :uid
HAVING upvotes IS NOT NULL
SELECT H.*,
SUM(IFNULL(V.type = 'C', 0)) AS upvotes,
SUM(IFNULL(V.type = 'R', 0)) AS downvotes
If you change your LEFT JOIN to an INNER JOIN (or just JOIN) you will only get the happens records that have at least 1 associated record in the votes table:
SELECT
H.*,
SUM(CASE WHEN V.type='C' THEN 1 ELSE 0 END) AS upvotes,
SUM(CASE WHEN V.type='R' THEN 1 ELSE 0 END) AS downvotes
FROM
happens H
INNER JOIN
votes AS V
ON V.hid = H.id
WHERE
H.uid = :uid
Check out http://www.w3schools.com/sql/sql_join.asp for info on the different join types.
EDIT:
Ok, so to get all happens, but with 0 for those without votes try (add COALESCE):
SELECT
H.*,
COALESCE(SUM(CASE WHEN V.type='C' THEN 1 ELSE 0 END),0) AS upvotes,
COALESCE(SUM(CASE WHEN V.type='R' THEN 1 ELSE 0 END),0) AS downvotes
FROM
happens H
LEFT JOIN
votes AS V
ON V.hid = H.id
WHERE
H.uid = :uid
SELECT
H.*,
COALESCE(SUM(CASE WHEN V.type='C' THEN 1 ELSE 0 END), 0) AS upvotes,
COALESCE(SUM(CASE WHEN V.type='R' THEN 1 ELSE 0 END), 0) AS downvotes
Left Join will return all rows on the left and NULL values on the right if ON condition does not match.Use Inner Join.
solved using 2 statement
first select all happen
second select all votes for every happen and merging the values
here is the solution from my php happenModel class:
public function getUserHappensWithVotes() {
$selectQuery = <<<QUERY
SELECT
SUM(CASE WHEN V.type='C' THEN 1 ELSE 0 END) AS upvotes,
SUM(CASE WHEN V.type='R' THEN 1 ELSE 0 END) AS downvotes
FROM
votes V
WHERE
V.uid = :uid
AND
V.hid = :hid
QUERY;
// get all happen as array of class
$userHappens = $this->userHappens;
// empty container
$happenWithVotes = array();
foreach( $userHappens AS $happen ) {
$sql = $this->pdo->prepare( $selectQuery );
$sql->execute( array( ':uid'=>$happen->uid, ':hid'=>$happen->id ) );
// fetching the count of up and down votes of every happen
$votes = $sql->fetch( PDO::FETCH_ASSOC );
// merging happen class as array with votes
$happen = array_merge( (array) $happen, $votes );
// rebuild the userHappen array
$happenWithVotes[] = (object) $happen;
}
if ( $this->encoder ) {
return $this->encoder->setData( $this->parse( $happenWithVotes ) )->encode();
} else {
return $this->parse( $happenWithVotes );
}
}
Related
I am working with mysql with codeigniter, hHere is my table structure:
Table A:
id(doctorid) name
-----------------------------
1 abc
2 xyz
3 ahd
4 djd
Table B:
id doctor_id type
-------------------------------------
1 1 Nuter
2 3 Spay
Now I want to get all records of doctor with count of type, I want the following result:
id name Nuter Spay
---------------------------------------
1 abc 1 0
2 xyz 0 Spay
I tried with following code but not working for me, how can I do this ?
$this->db->select('A.*');
$this->db->from('A');
$this->db->join('B', 'B.doctor_id = (SELECT COUNT(Type) FROM B )');
$query = $this->db->get();
Try this code:
$this->db->select('A.*,B.type');
$this->db->from('A');
$this->db->join('B', 'B.doctor_id = A.id');
$query = $this->db->get();
It seems like you are looking for conditional aggregation.
In pure SQL, the query should look like:
SELECT
A.id,
A.name,
SUM(CASE WHEN B.type = 'Nuter' THEN 1 ELSE 0 END) as Nuter,
SUM(CASE WHEN B.type = 'Spay' THEN 1 ELSE 0 END) as Spay
FROM A
INNER JOIN B ON B.doctor_id = A.id
GROUP BY A.id, A.name
If you have a 1-1 relationship between tables A and B instead of 1-N (as your sample data suggests), then this can be simplified as:
SELECT
A.id,
A.name,
CASE WHEN B.type = 'Nuter' THEN 1 ELSE 0 END as Nuter,
CASE WHEN B.type = 'Spay' THEN 1 ELSE 0 END as Spay
FROM A
INNER JOIN B ON B.doctor_id = A.id
Try:
$this->db->select(
"A.id,
A.name,
CASE WHEN B.type = 'Nuter' THEN 1 ELSE 0 END as Nuter,
CASE WHEN B.type = 'Spay' THEN 1 ELSE 0 END as Spay"
);
$this->db->from('A');
$this->db->join('B', 'B.doctor_id = A.id');
$query = $this->db->get();
I have a problem with my queries. I have now 2 queries and that works fine. But when I want to add another query with a PIVOT, it doesn't work. I have tried a lot of things but nothing works..
This is my first two queries
$query = "SET SQL_BIG_SELECTS = 1;";
$query .= "SELECT * FROM datakram, datakram2, datakram3 WHERE datakram.NAME = datakram2.NAME AND datakram2.NAME = datakram3.NAME"
And I want to add a PIVOT for table "datakram4". But I want only the rows where the NAME is equal to the NAME in the others tables. Without the PIVOT it works..
My PIVOT code.
SELECT `name` ,
MAX( CASE WHEN `year` =2017 THEN `income` ELSE 0 END ) AS INCOME_2017,
MAX( CASE WHEN `year` =2017 THEN `expense` ELSE 0 END ) AS EXPENSE_2017,
MAX( CASE WHEN `year` =2016 THEN `income` ELSE 0 END ) AS INCOME_2016,
MAX( CASE WHEN `year` =2016 THEN `expense` ELSE 0 END ) AS EXPENSE_2016
FROM `test_data` GROUP BY `name`
I use multi_query for my php script.
Simply join the queries:
SELECT d4.*
FROM datakram d1
INNER JOIN datakram2 d2
ON d1.`NAME` = d2.`NAME`
INNER JOIN datakram3 d3
ON d2.`NAME` = d3.`NAME`
INNER JOIN
(SELECT `name` ,
MAX(CASE WHEN `year`=2017 THEN `income` ELSE 0 END) AS INCOME_2017,
MAX(CASE WHEN `year`=2017 THEN `expense` ELSE 0 END) AS EXPENSE_2017,
MAX(CASE WHEN `year`=2016 THEN `income` ELSE 0 END) AS INCOME_2016,
MAX(CASE WHEN `year`=2016 THEN `expense` ELSE 0 END) AS EXPENSE_2016
FROM `test_data`
GROUP BY `name`
) d4
ON d3.`NAME` = d4.`name`
A simple way is to filter in the WHERE clause:
SELECT `name` ,
MAX(CASE WHEN `year` = 2017 THEN `income` ELSE 0 END) AS INCOME_2017,
MAX(CASE WHEN `year` = 2017 THEN `expense` ELSE 0 END) AS EXPENSE_2017,
MAX(CASE WHEN `year` = 2016 THEN `income` ELSE 0 END) AS INCOME_2016,
MAX(CASE WHEN `year` = 2016 THEN `expense` ELSE 0 END) AS EXPENSE_2016
FROM `test_data` td
WHERE EXISTS (SELECT 1 FROM FROM datakram d WHERE d.name = td.NAME) AND
EXISTS (SELECT 1 FROM FROM datakram2 d WHERE d.name = td.NAME) AND
EXISTS (SELECT 1 FROM FROM datakram3 d WHERE d.name = td.NAME)
GROUP BY `name` ;
I have this tbl_religion with fieldname creldesc
and tbl_member with these fields
and I have this query to count all existing religion in tbl_member and count also the no. of male and female with that religion so i made this query :
SELECT m.creldesc as type,
COUNT(m.creldesc) as total,
SUM(CASE WHEN m.cgender='Male' THEN 1 ELSE 0 END) as male,
SUM(CASE WHEN m.cgender='Female' THEN 1 ELSE 0 END) as female
FROM tbl_member as m, tbl_barangay as b, tbl_household as h
WHERE m.chholdnumber = h.chholdnumber and h.cbrgycode=b.cbrgycode and b.cbrgyname = 'AGAO'
and m.crelationdesc !='Brgy. Captain'
GROUP BY m.creldesc
ORDER BY tot DESC;
and get this output :
what I want to achieve is also display the remaining religion from tbl_religion with 0 as value. I made a left join but get an error. Can someone help me how can I achive that ?
type total male female
Roman Catholic 7 4 3
Baptist 3 1 2
Islam 3 3 0
Iglesia ni Cristo 1 1 0
Free Methodist 1 1 0
Ang Dating Daan 1 0 1
Aglipay/Filipinsta 0 0 0
All the Gospel 0 0 0
Alpha Omega 0 0 0
and so on.............................
Use same joining approach for all tables, like this:
SELECT m.creldesc as type, COUNT(m.creldesc) as tot,
SUM(CASE WHEN m.cgender='Male' THEN 1 ELSE 0 END) as male,
SUM(CASE WHEN m.cgender='Female' THEN 1 ELSE 0 END) as female
FROM tbl_religion
LEFT JOIN tbl_member as m
ON tbl_religion.creldesc=m.creldesc
LEFT JOIN tbl_household as h
ON m.chholdnumber = h.chholdnumber
LEFT JOIN tbl_barangay as b
ON h.cbrgycode=b.cbrgycode
WHERE b.cbrgyname = 'AGAO' AND m.crelationdesc !='Brgy. Captain'
GROUP BY m.creldesc
ORDER BY tot DESC
Your current query approach of ... FROM tbl_member, tbl_barangay, tbl_household LEFT JOIN tbl_religion ... is wrong and will produce syntax error.
You can not make this:
FROM #TABLE1, #TABLE2, #TABLE3 LEFT JOIN #TABLE0 ON ...
You can select items only from a table at once... same for join.
SELECT m.creldesc as type,
COUNT(m.creldesc) as tot,
SUM(CASE WHEN m.cgender='Male' THEN 1 ELSE 0 END) as male,
SUM(CASE WHEN m.cgender='Female' THEN 1 ELSE 0 END) as female
FROM tbl_member as m
LEFT JOIN tbl_religion z ON z.creldesc = m.creldesc
LEFT JOIN tbl_barangay h ON h.cbrgycode=b.cbrgycode
SO ON.... WITH JOINS
GROUP BY m.creldesc
UPDATE WOKING CODE:
SELECT m.creldesc as type, COUNT(m.creldesc) as tot, SUM(CASE WHEN m.cgender='Male' THEN 1 ELSE 0 END) as male,
SUM(CASE WHEN m.cgender='Female' THEN 1 ELSE 0 END) as female
FROM tbl_member as m
LEFT JOIN tbl_religion r ON r.creldesc = m.creldesc
LEFT JOIN tbl_barangay as b ON m.chholdnumber = b.cbrgycode
LEFT JOIN tbl_household as h ON h.cbrgycode = b.cbrgycode
WHERE b.cbrgyname = 'AGAO' and m.crelationdesc !='Brgy. Captain'
GROUP BY m.creldesc
ORDER BY tot DESC
Here's what I did:
SELECT tbl_member.creldesc AS type,
COUNT(tbl_member.creldesc) AS total,
SUM(CASE WHEN tbl_member.cgender::text = 'Male'::text THEN 1 ELSE 0 END) AS male,
SUM(CASE WHEN tbl_member.cgender::text = 'Female'::text THEN 1 ELSE 0 END) AS female
FROM tbl_member
GROUP BY tbl_member.creldesc
UNION
SELECT b.creldesc AS type, 0 AS total, 0 AS male, 0 AS female
FROM tbl_religion b
WHERE NOT (b.creldesc::text IN
( SELECT DISTINCT tbl_member.creldesc FROM tbl_member))
GROUP BY b.creldesc;
and here is it's output :
that's all :) I used UNION instead of LEFT JOIN.
SELECT ac1.id, ac1.course_name, case when a.yettoapprove is not null
then 'true'
else 'false'
end OrderedAll , a.* from (SELECT ac.id, sum(case when ad.status = '2' then 1 else 0 end) as yettoapprove , sum(case when ad.status = '1' then 1 else 0 end) as approved,case when ad.status = '2' is not null
then 'true'
else 'false'
end OrderedAll FROM aw_ou_student_data ad , jos_users ju , aw_ou_lookup_courses ac,aw_ou_lookup_colleges ac1 where ac1.id=ju.college_code and ac.id in (27,28,29,30,133,32,33,34,35,36,37,38,39,40,41,42,43,44,134,135) and ac.school_id=2 and ju.id=ad.user_id and ac.id=ju.course_code GROUP BY ac.id ORDER BY ac.id ) a left join aw_ou_lookup_courses ac1 on ac1.id = a.id
In the above inner query in above one i have given 20 ac.id but i am getting only 14 out records as a result. How do i get the total 20 records as a result. i.e The id present in the in condition if it doesnt satisfy then i should a record with 0 as values for it beside that id.
How do i do it..?
A simple left join with condition should do it:
SELECT
table1.id,
table2.*
FROM
table table1
LEFT JOIN table2 ON table1.id = table2.id AND (your conditions here)
I am building a polling system and I have a query right now that is supposed to select all of the polls and count all of the votes for each of the polls (which lie in a separate table). So my tables look like:
Polls:
ID
Title
Body
Votes:
ID
PollID
Vote (This value is either 0 or 1)
Well the totaling of the votes looks like it is working, the issue is that it is currently only displaying one record.
Currently my query looks like this:
SELECT POLLS.ID,
POLLS.TITLE,
POLLS.BODY,
Sum(CASE
WHEN VOTES.VOTE = 1
AND VOTES.POLLID = POLLS.ID THEN 1
ELSE 0
END) AS yay,
Sum(CASE
WHEN VOTES.VOTE = 0
AND VOTES.POLLID = POLLS.ID THEN 1
ELSE 0
END) AS nay,
FROM polls,
VOTES
ORDER BY POLLS.ID
Also I am using PHP with Codeigniter.
SELECT polls.ID,polls.title,polls.body,
SUM(case when votes.vote = 1 then 1 else 0 end) AS yay,
SUM(case when votes.vote = 0 then 1 else 0 end) AS nay,
FROM polls
JOIN votes ON polls.ID = votes.pollID
GROUP BY poll.ID, polls.title, polls.body
ORDER BY polls.ID
You need to GROUP BY pollID the values in the votes table:
SELECT p.ID, p.title, p.body, v.yay, v.nay
FROM
(
SELECT PollID,
SUM(CASE WHEN vote = 1 THEN 1 ELSE 0 END) AS yay,
SUM(CASE WHEN vote = 0 THEN 1 ELSE 0 END) AS nay,
FROM votes
GROUP BY PollID
) v
INNER JOIN Polls p ON v.PollID = p.Id
ORDER BY p.ID
I'd go with:
SELECT polls.ID, polls.title, polls.body,
SUM(IF(votes.vote = 1, 1, 0)) AS yay,
SUM(IF(votes.vote = 0, 1, 0)) AS nay
FROM polls
JOIN votes ON (votes.pollID = polls.ID)
GROUP BY ID, title, body
What you are missing is "GROUP BY". Use GROUP BY PollID while taking sum.