I'm trying to find a patient's appointments and messages count. My table records are like below 3 table patient, appointments and messages
Patient table
pid fname lname
1 john sid
2 rother ford
3 megan rough
4 louis kane
appointments table
id pid appointment_date
1 1 2015-08-04
2 2 2015-08-05
3 1 2015-08-06
4 1 2015-08-07
5 3 2015-08-07
6 2 2015-08-08
7 4 2015-08-13
8 1 2015-08-12
Messages table
id pid description message_date
1 2 join 2015-08-04
2 2 update 2015-08-05
3 3 join 2015-08-05
4 4 update 2015-08-10
5 3 test 2015-08-07
So if write query to find counts i'm getting wrong values
SELECT pd.fname,pd.lname , pd.pid, COUNT( a.id ) AS app_cnt, COUNT( m.id ) AS mes_cnt
FROM patient pd
LEFT OUTER JOIN appointments a ON a.pid = pd.pid
LEFT OUTER JOIN messages m ON m.pid = pd.pid
GROUP BY pd.pid
ORDER BY pd.pid
fname lname pid app_cnt mes_cnt
john sid 1 4 0
rother ford 2 4 4
megan rough 3 2 2
louis kane 4 1 1
Here pid 1 have 4 appointments and 0 messages, pid 2 have 2 appointments and 2 messages but getting wrong values.
Can someone please help to resolve this issue. I'm not interested in writing sub queries for this.
Functionality looks simple but I'm really facing problem for writing query.
Anymore suggestions please.
After thoroughly analysing your problem and tables, It cannot be done directly using simple query as LEFT OUTER JOIN is returning some superfluous records, that's why to filter it, you will have to use temporary table and modify the query as:
Select temp.fname, temp.lname, temp.pid, a_count, count(m.pid) as m_count from
(SELECT fname,lname,pd.pid, count(a.pid) as a_count
FROM patients pd
LEFT OUTER JOIN appointments a ON a.pid = pd.pid group by pd.pid) temp
LEFT OUTER JOIN messages m ON m.pid = temp.pid
group by temp.pid
Explanation:
It will join patients and appointments table and group them by pid so that messages from message table do not repeat for each patients.pid.
The wrong result is as a result of left outer join as it is giving wrong results for this query
SELECT *
FROM patients pd
LEFT OUTER JOIN appointments a ON a.pid = pd.pid
LEFT OUTER JOIN messages m ON m.pid = pd.pid
Since we need to limit the results of first two joins, hence temporary table is necessary.
Related
I have 4 tables and I want to select the information from these tables using one query.
I have a table name
tbl_marketing
db_maid db_client db_status db_process
4 test done ddd
tbl_meeting
db_meetingid db_meetingsubject db_mid
1 test 4
tbl_phonecall
db_phid db_subject db_mid
1 ggg 4
2 fff 4
tbl_email
db_eid db_email db_mid
1 xxx 4
Remark: this is an example of my 4 tables. My tables contain more columns
db_mid is the id of tbl_marketing
I want to select all information without repetition
I use this query:
select
marketing.*,
meeting.db_meetingid,meeting.db_meetingsubject,meeting.db_sd,meeting.db_dd,meeting.db_duration as meetingDuration,meeting.db_place,meeting.db_mom,meeting.db_momattache,meeting.db_status as meetingStatus,meeting.db_nextmeeting,meeting.db_lastmeeting,meeting.db_attendees,meeting.db_note as meetingNote,meeting.db_mid,
phonecall.db_phid,phonecall.db_subject as phoneSubject,phonecall.db_desc as phoneDesc,phonecall.db_nextdate,phonecall.db_doc,phonecall.db_duration as phoneDuration,phonecall.db_phstatus,phonecall.db_pnote,phonecall.db_client as phoneClient,phonecall.db_crf,phonecall.db_callto,phonecall.db_phone as phonecallPhone,phonecall.db_logs as phoneLogs,phonecall.db_mid,phonecall.db_dateedit as phoneEdit,phonecall.db_phdate,
email.db_eid,email.db_edate,email.db_esubject,email.db_edesc,email.db_erf,email.db_emailto,email.db_email as eEmail,email.db_dos,email.db_dor,email.db_estatus,email.db_logs as eLogs,email.db_note as eNote,
location.db_location as location,
location.db_lid,
company.db_company as com,
company.db_coid,
subcompany.db_subid,
subcompany.db_subcompany as sub,
user.db_uid,
concat(user.db_fname,' ' ,user.db_lname) as tr,
subj.db_subjid,
subj.db_subject as s
from tbl_marketing as marketing
left join tbl_location as location
on
marketing.db_location=location.db_lid
left join tbl_meeting as meeting
on
marketing.db_maid=meeting.db_mid
left join tbl_phonecall as phonecall
on
marketing.db_maid=phonecall.db_mid
left join tbl_email as email
on
marketing.db_maid=email.db_mid
left join tbl_company as company
on
marketing.db_companyname=company.db_coid
left join tbl_subcompany as subcompany
on
marketing.db_subcompany=subcompany.db_subid
left join tbl_user as user
on
marketing.db_transfered=user.db_uid
left join tbl_subject as subj
on
phonecall.db_subject=subj.db_subjid
where
marketing.db_maid='$read'
and
meeting.db_mid='$read'
and
email.db_mid='$read'
and
phonecall.db_mid='$read'
I use this query to select the information but when I have 2 rows, the same db_mid as tbl_phonecall in the example, then the information is duplicated as follows:
table marketing
1 test 4
1 test 4
table phonecall
1 ggg 4
2 fff 4
table meeting
1 test 4
1 test 4
table email
1 xxx 4
1 xxx 4
As visible above all tables have a duplicate value without phonecall because he have 2 rows I want to show all the information without this repetition
if I use group by mid I don't receive the second information in tbl_phonecall
$rows=array();
while($rows=mysqli_fetch_array($sql)){
$row[]=$rows;}
<table>
foreach($rows as row){
<tr>
<td><?php echo $row['phID'];?></td>
</tr>
}
</table>
output
# subject mid
1 test 4
2 ggg 4
not like this
1,2 test,ggg 4,4
If you only have duplicates in the phonecall table, the following should work. If other left joined tables can have duplicates, just add GROUP_CONCAT as needed.
select
marketing.*,
meeting.db_meetingid,
meeting.db_meetingsubject,
meeting.db_sd,
meeting.db_dd,
meeting.db_duration as meetingDuration,
meeting.db_place,
meeting.db_mom,
meeting.db_momattache,
meeting.db_status as meetingStatus,
meeting.db_nextmeeting,
meeting.db_lastmeeting,
meeting.db_attendees,
meeting.db_note as meetingNote,
meeting.db_mid,
GROUP_CONCAT(phonecall.db_phid) as phID,
GROUP_CONCAT(phonecall.db_subject) as phoneSubject,
GROUP_CONCAT(phonecall.db_desc) as phoneDesc,
GROUP_CONCAT(phonecall.db_nextdate) as phNextDate,
GROUP_CONCAT(phonecall.db_doc) as phDoc,
GROUP_CONCAT(phonecall.db_duration) as phoneDuration,
GROUP_CONCAT(phonecall.db_phstatus) as phStatus,
GROUP_CONCAT(phonecall.db_pnote) as phPNote,
GROUP_CONCAT(phonecall.db_client) as phoneClient,
GROUP_CONCAT(phonecall.db_crf) as phCrf,
GROUP_CONCAT(phonecall.db_callto) as phCallTo,
GROUP_CONCAT(phonecall.db_phone) as phonecallPhone,
GROUP_CONCAT(phonecall.db_logs) as phoneLogs,
GROUP_CONCAT(phonecall.db_mid) as phMid,
GROUP_CONCAT(phonecall.db_dateedit) as phoneEdit,
GROUP_CONCAT(phonecall.db_phdate) as phdate,
email.db_eid,
email.db_edate,
email.db_esubject,
email.db_edesc,
email.db_erf,
email.db_emailto,
email.db_email as eEmail,
email.db_dos,
email.db_dor,
email.db_estatus,
email.db_logs as eLogs,
email.db_note as eNote,
location.db_location as location,
location.db_lid,
company.db_company as com,
company.db_coid,
subcompany.db_subid,
subcompany.db_subcompany as sub,
user.db_uid,
concat(user.db_fname,' ' ,user.db_lname) as tr,
subj.db_subjid,
subj.db_subject as s
from tbl_marketing as marketing
left join tbl_location as location
on marketing.db_location=location.db_lid
left join tbl_meeting as meeting
on marketing.db_maid=meeting.db_mid
left join tbl_phonecall as phonecall
on marketing.db_maid=phonecall.db_mid
left join tbl_email as email
on marketing.db_maid=email.db_mid
left join tbl_company as company
on marketing.db_companyname=company.db_coid
left join tbl_subcompany as subcompany
on marketing.db_subcompany=subcompany.db_subid
left join tbl_user as user
on marketing.db_transfered=user.db_uid
left join tbl_subject as subj
on phonecall.db_subject=subj.db_subjid
where marketing.db_maid='$read' and
meeting.db_mid='$read' and
email.db_mid='$read' and
phonecall.db_mid='$read'
GROUP BY meeting.db_mid
I have those 3 tables
candidates
cdid cdname
1 Moussa
2 Moustafa
3 Haitham
positions
posid posname
1 pos1
2 pos2
3 pos3
joboffers
cdid posid salary
1 1 600
1 2 800
1 3 700
2 1 500
2 2 700
2 3 900
3 2 1000
3 3 500
I need to make a query that get the cdname with max salary for every position
the table should be like this
cdname posname salary
moussa pos1 600
haitham pos2 1000
moustafa pos3 900
I am using this query but it's not getting the correct cdid
$sql="select joboffers.cdid,joboffers.posid,Max(joboffers.salary),candidates.cdname
FROM joboffers,candidates
Where joboffers.cdid=candidates.cdid
Group by joboffers.posid";
Try this :)
SELECT sub.cdname, sub.posname, sub.salary
FROM (
SELECT * FROM joboffers jo
INNER JOIN positions ps USING (posid)
INNER JOIN candidates cd USING (cdid)
ORDER BY posid, salary DESC
) sub
GROUP BY sub.posid
I think you need to group by all non-aggregated columns, like this:
SELECT j.cdid, c.cdname, j.posid, MAX(j.salary)
FROM joboffers j
JOIN candidates c ON j.cdid = c.cdid
GROUP BY j.cdid, c.cdname, j.posid
SELECT c.cdname, p.posname, MAX(j.salary)
FROM joboffers j
INNER JOIN candidates c ON j.cdid = c.cdid
INNER JOIN positions p ON j.posid = p.posid
GROUP BY c.cdname, p.posname
enter code hereSELECT customerNumber, MAX(amount)
FROM payments
GROUP BY customerNumber
ORDER BY MAX(amount);
I have 4 tables as below
Table1: restos
Structure:
resu_id resu_name resu_address
-------------------------------------
1 ABC Exapmple
2 DEF Example
3 GHD Example
Table2:foodtype
Structure:
id typename
---------------
12 Indian
23 Punjabi
Table3: resto_foodtypes
Structure:
resu_id foodty_id
--------------------
1 12
2 23
3 12
Table4: discnts
Structure:
id resu_id amt_dscPer(%age discount)
---------------------------
19 1 15
20 2 25
Now i want to display the restaurant along with discounts available for the restauarant.
Currently restaurants are getting displayed but for the restaurant not present in discnts table are returning null values from discnts table.
below is the query that m using
SELECT * from `restos` r join resto_foodtypes rf on rf.resu_id = r.resu_id
join foodtype f on rf.foodty_id = f.id left join discnts dcfm on
r.resu_id= dcfm.resu_id where true;
I want that the restaurants that are not present in discnts table should not be included in resultset. For e.g. resu_id=3 is not present in discnts table.
To exclude results that have no entry in discnts table, use a INNER JOIN instead of a LEFT JOIN.
To include these results, but have a 0 displayed (instead of the defaults NULL), you can use IFNULL(expr, 0) function:
SELECT
r.resu_name AS Name,
f.typename AS Food,
IFNULL(dcfm.amt_dscPer, 0) AS Discount
FROM `restos` r
JOIN resto_foodtypes rf ON rf.resu_id = r.resu_id
JOIN foodtype f ON rf.foodty_id = f.id
LEFT JOIN discnts dcfm ON r.resu_id= dcfm.resu_id;
IFNULL returns the first parameter if it is not null, the second if expr is indeed null.
I have 3 tables office, Computer and Maintain. Office just list of offices, computers belongs to office as well as has many Maintains.
I want to LEFT join all tables with just the latest entry from Maintain table. The code below works but it just group the oldest entry in the maintain.
SELECT `Computer`.`id`, `Computer`.`control`, `Computer`.`operator`, `Computer`.`datePurchased`, `Computer`.`type`, `Computer`.`property`, `Computer`.`printer`, `Computer`.`scanner`, `Computer`.`osx`, `Computer`.`applications`, `Computer`.`licence`, `Computer`.`isStandAlone`, `Computer`.`isInternet`, `Computer`.`isNetwork`, `Computer`.`generalStatus`, `Computer`.`ip_address`, `Computer`.`mac_address`, `Computer`.`user_id`, `Computer`.`office_id`, `Computer`.`created`, `Computer`.`modified`, `Computer`.`deleted`, `Office`.`id`, `Office`.`description`, `Office`.`main_office`, `Maintain`.`id`, `Maintain`.`dateEncoded`, `Maintain`.`findings`, `Maintain`.`checkedBy`, `Maintain`.`remarks`, `Maintain`.`computer_id`, `Maintain`.`created`, `Maintain`.`modified`, `Maintain`.`user_id` FROM `computers`.`computer` AS `Computer`
LEFT JOIN `computers`.`office` AS `Office`
ON (`Office`.`id` = `Computer`.`office_id`)
LEFT JOIN `computers`.`maintain` AS `Maintain`
ON (`Computer`.`id` = `Maintain`.`computer_id`)
LEFT JOIN (SELECT MAX(dateEncoded) maxDate, findings FROM maintain GROUP BY computer_id) AS `P2`
ON (`Maintain`.`dateEncoded` = `p2`.`maxDate`)
WHERE `Office`.`main_office` LIKE '%CVPH MON%'
GROUP BY `Computer`.`id`
ORDER BY `Office`.`description` ASC
SAMPLE
OFFICE
1 AAAA
2 BBBB
COMPUTER
id name office_id
1 CP1 1
2 CP2 1
3 CP3 2
Maintain
id description date computer_id
1 Fix 06/20/2014 1
2 Fix 06/11/2014 1
3 Fix 06/12/2014 2
4 Fix 06/15/2014 2
Result if query on computer=CP1 should be
Office Computer_name Maintain_desc Date
AAA CP1 Fix 06/20/2014 <- Latest entry in maintain
You can do so
SELECT `c`.`id`,
`c`.`name`,
`c`.`office_id` ,
`o`.`name` office_name,
`m`.`date`,
`m`.`description`
FROM `computer` AS c
LEFT JOIN `office` AS `o`
ON (`o`.`id` = `c`.`office_id`)
LEFT JOIN `maintain` AS m
ON (`c`.`id` = `m`.`computer_id`)
INNER JOIN
(SELECT computer_id,MAX(`date`) maxdate
FROM maintain
GROUP BY computer_id ) t
ON(m.`date`=t.maxdate AND m.computer_id= t.computer_id)
WHERE `c`.`name` ='CP1' ... more conditions
Demo
Im joining 3 tables to present a table with users highest score
My tables
game_log:
---ID---user_ID---score---time---
| 1 52 567 10 |
| 2 53 641 13 |
| 3 52 465 8 |
| 4 53 451 14 |
---------------------------------
users:
---ID---name---countyid---
| 52 Dave 1 |
| 53 John 2 |
------------------------
county:
---countyid---countyname---
| 1 Orange wichit |
| 2 Orange clemts |
--------------------------
SQL:
SELECT * FROM game_log
INNER JOIN users ON game_log.user_ID=users.ID
INNER JOIN county ON users.countyid=county.countyid
ORDER BY game_log.score DESC , game_log.time LIMIT 20";
Above code gives me this result:
Rank---Name--------County------Score---Time
1 John Orange clemts 641 13
2 Dave Orange wichit 567 10
3 John Orange clemts 465 8
4 Dave Orange wichit 451 14
My problem is that I want the highscore table to display the top 20 users with the highest score, not the 20 highest scores.
Like this:
Rank---Name--------County------Score---Time
1 John Orange clemts 641 13
2 Dave Orange wichit 567 10
Need som help with this, not familiar with joining tables ;-)
This approach will show the top 20 users and each user's highest score, and if they have multiple instances of the same score, it'll show the information for the earliest one (lowest time value for that user and score).
SELECT *
FROM game_log gl
INNER JOIN users u
ON gl.user_ID = u.ID
INNER JOIN county c
ON u.countyid = c.countyid
WHERE not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id
and gl2.score > gl.score)
and not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id
and gl2.time < gl.time
and gl2.score = gl.score)
ORDER BY gl.score DESC, gl.time LIMIT 20;
Without doing this, if the same user in the top 20 had the same score 2+ times, they would be listed 2+ times, and you would not get back 20 people by using LIMIT 20 because the same person would be taking up N rows out of that 20.
SQL Fiddle here showing data with a tie: http://sqlfiddle.com/#!2/0ac931/5/0
GROUP BY should do the job.
SELECT users.ID, users.name, county.countyname, MAX(game_log.score) AS score, game_log.time
FROM game_log
INNER JOIN users ON game_log.user_ID = users.ID
INNER JOIN county ON users.countyid = county.countyid
GROUP BY game_log.user_ID
ORDER BY game_log.score DESC, game_log.time
LIMIT 20;
Try it out with SQL Fiddle.
I would do this with the not exists approach to get the highest score for each user. The rest of the query is the same:
SELECT *
FROM game_log gl INNER JOIN
users u
ON gl.user_ID = u.ID INNER JOIN
county c
ON u.countyid = c.countyid
WHERE not exists (select 1
from game_log gl2
where gl2.user_id = gl.user_id and gl2.score > gl.score
)
ORDER BY gl.score DESC, gl.time
LIMIT 20;
The where clause is saying "keep this row if no other row for the same user has a higher score".
Another way to do this is with the aggregation approach:
SELECT *
FROM (select user_id, max(score) as maxscore
from game_log gl
group by user_id
) gl INNER JOIN
users u
ON gl.user_ID = u.ID INNER JOIN
county c
ON u.countyid = c.countyid
ORDER BY gl.maxscore DESC
LIMIT 20;
But this method loses the information about time. It is possible to include that, but it makes the query more complicated.