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();
Related
I have 4 tables.
table groups
| ID | NAME |
1 Premium
2 Silver
table user
| ID | group_id | NAME |
1 1 Serhan
2 2 Farhat
table user_statistics
| ID | user_id | TYPE |
1 1 1
2 2 0
table votes
| ID | user_id | VOTE |
1 1 1
2 2 0
3 1 0
I created an sql query to retrieve user details who is in same group. It's worked! Then I want to retrieve any of vote that have been voted to user in groups. I want to count the vote. So basically I've made this sql query.
global $conn;
$res_groups = array();
$stmt = $conn->prepare("
SELECT * FROM groups
");
$stmt->execute();
$stmt->setFetchMode(PDO::FETCH_ASSOC);
while ($group = $stmt->fetch()){
$groups = array();
$groups['id'] = $group['id'];
$groups['name'] = $group['name'];
$user_arr = array();
$stmts = $conn->prepare('
SELECT l.*,
(SELECT MAX(ls.date) from user_statistics ls WHERE ls.user_id = l.id GROUP BY ls.user_id) as ls_date,
(SELECT SUM(IF(ls.type="0", ls.type, 0)) FROM user_statistics ls WHERE ls.user_id = l.id GROUP BY ls.user_id) as ls_us,
(SELECT SUM(IF(ls.type="1", ls.type, 0)) FROM user_statistics ls WHERE ls.user_id = l.id GROUP BY ls.user_id) as ls_uk,
(SELECT COUNT(*) FROM user_statistics WHERE type="1" AND ls.user_id = l.id GROUP BY ls.user_id) as totals,
(SELECT COUNT(*) FROM votes v WHERE v.vote=0 AND confirm=0 AND v.user_id = l.id) as badvote,
(SELECT COUNT(*) FROM votes v WHERE v.vote=1 AND confirm=0 AND v.user_id = l.id) as goodvote
FROM user l
LEFT JOIN
user_statistics ls on l.id = ls.user_id
LEFT JOIN votes v on v.user_id = l.id
WHERE l.group_id = '.$groups['id'].' AND status = 1
GROUP BY l.id,ls.user_id
');
$stmts->execute();
$stmts->setFetchMode(PDO::FETCH_ASSOC);
while ($usr = $stmts->fetch()){
$totalvote=($usr['badvote']+$usr['goodvote']);
if($totalvote>0){
$badvote=bcdiv($usr['badvote']*100/$totalvote,1,2);
$goodvote=bcdiv($usr['goodvote']*100/$totalvote,1,2);
}else{
$badvote=0;
$goodvote=0;
}
$votes[] = array(
"count" => $usr['badvote'],
"percent" => $badvote
);
$votes[] = array(
"count" => $usr['goodvote'],
"percent" => $goodvote
);
$user_arr[] = array(
"id" => $usr['id'],
"group_id" => $usr['group_id'],
"name" => $usr['name'],
"votes_summary" => $votes
);
}
$groups['list'] = $user_arr;
$res_groups[] = $groups;
}
All code seems work unless one thing. The VOTE is always return to count all column in my database VOTES and apply the data to all of my user. What I want is to get how many vote that each user get based on vote type GOOD or BAD.
Any help will be nice.
Looking to your code you could refactor your query avoiding select subquery one for each row and using two subquery with group by in join.
You have also the same code for bad and good vote could be you need different code for obtain different values
SELECT l.*
, t1.ls_date
, t1.ls_us
, t1.ls_uk
, t2.totals
, t2.badvote
, t2.goodvote
FROM user l
INNER JOIN (
SELECT ls.user_id
, MAX(ls.date) ls_date
, SUM(IF(ls.type="0", ls.type, 0)) ls_us
, SUM(IF(ls.type="1", ls.type, 0)) ls_uk
from user_statistics ls
GROUP BY ls.user_id
) t1 on t1.user_id = l.id
LEFT JOIN (
SELECT v.user_id
, sum( case when type="1" then 1 else 0 end ) totals
/* these are the same */
, sum ( case when v.vote=1 AND confirm=0 then 1 else 0 END ) badvote
/* these are the same */
, sum ( case when v.vote=1 AND confirm=0 then 1 else 0 END ) goodvote
FROM votes v
) t2 ON t2.user_id = l.id
WHERE l.group_id = '.$groups['id'].'
AND status = 1
And you should avoid the use of PHP var in SQL (you are at risk for SQL injection). For this you should take a look at your db driver for prepared statement and binding value, or at least be sure you sanitize properly the php var content
I have a table in my database named contacts and a table named views.
On the table contacts I have the following fields:
id
status
first_name
last_name
The status can be cold, prospect or lost.
On the table views I have the following fields:
user_id
art_views
art_title
The relation between those 2 tables is id and user_id.
I need a query to make a new html table with the following columns:
art_title
cold
prospect
lost
Now I have the following query (UPDATED):
SELECT
v.art_title,
SUM(CASE c.status WHEN 'cold' THEN v.art_views ELSE 0 END) cold,
SUM(CASE c.status WHEN 'prospect' THEN v.art_views ELSE 0 END) prospect,
SUM(CASE c.status WHEN 'lost' THEN v.art_views ELSE 0 END) lost
FROM views v
JOIN contacts c ON v.user_id = c.id
GROUP BY v.art_title
This query is working now (thanks to Gerv) but i still have users who don't have a status. So i leave the field user_id in the table 'views' empty. How can i change the query for those users so i can count them also?
I tried to: SUM(CASE v.user_id WHEN ' ' THEN v.art_views ELSE 0 END) test,
but with no result here.
You can switch the logic by selecting from the views table and joining de contacts table.
Below query will pivot the status with a CASE clause
SELECT
v.art_title,
SUM(CASE c.status WHEN 'cold' THEN v.art_views ELSE 0 END) cold,
SUM(CASE c.status WHEN 'prospect' THEN v.art_views ELSE 0 END) prospect,
SUM(CASE c.status WHEN 'lost' THEN v.art_views ELSE 0 END) lost,
SUM(CASE c.status WHEN NULL THEN v.art_views ELSE 0 END) no_user
FROM views v
LEFT JOIN contacts c ON v.user_id = c.id
GROUP BY v.art_title
ORDER BY (cold+lost+prospect+no_user) DESC
LIMIT 10
Try
SELECT art_title, SUM(art_views), status FROM contacts AS c INNER JOIN views AS v ON v.user_id = c.id GROUP BY v.art_title, c.status
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.
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 );
}
}
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.