I have a Star Wars project that I am working on for Uni.
In my database, I have multiple tables and linking tables. I'm trying to query
"What mode of transport has been used by Rey, Obi-Wan Kenobi and
C-3PO?"
Answer (obviously) is Millennium Falcon.
I've set up the query using INNER JOINs but I'm stuck at the end.
SELECT t.type AS OnlyTransportItCouldBe
FROM transport AS t
INNER JOIN person_transport AS pt
ON pt.transport_id = t.id
INNER JOIN person AS p
ON pt.person_id = p.id
WHERE p.name = 'Rey'
The above returns 3 ships, but I'm confuddled as to how also check Old Ben and Goldenrod. I've tried AND p.name = "Obi-Wan Kenobi" and it returns blank. If I try WHERE p.name = 'Rey', 'Obi-Wan Kenobi' it fails, if I try WHERE p.name = 'Rey' AND 'Obi-Wan Kenobi', the result is blank. I know it's something really simple, but I can't seem to find a solution.
If your requirement is to get the common modes of transport that have been used by Rey, Obi-Wan Kenobi and C-3PO then you must group by type and add a having clause like ethis:
SELECT t.type AS OnlyTransportItCouldBe
FROM transport AS t
INNER JOIN person_transport AS pt ON pt.transport_id = t.id
INNER JOIN person AS p ON pt.person_id = p.id
WHERE p.name IN ('Rey', 'Obi-Wan Kenobi', 'C-3PO')
GROUP BY t.type
HAVING COUNT(DISTINCT p.name) = 3
Related
I am having some problems with this homework question:
I have been able to successfully complete almost all of the query except for the "Only include states with at least 3 diamond mines". I would like to ask how I can add this part to the query.
select I.state, sum(P.capacity)
from Infrastructure I
natural join Mine M
join produces P
on P.mine = M.entryno
join Commodity C
on C.comID = P.commodity
where C.name like '%Diamond%'
group by I.state
If your attempt works fine other than condition mentioned in question following query should work:
select I.state, sum(P.capacity)
from Infrastructure I
natural join Mine M
join produces P
on P.mine = M.entryno
join Commodity C
on C.comID = P.commodity
where C.name like '%Diamond%'
group by I.state
having count(P.mine) >=3;
It will count the no. of commodity for each state as you already has group by on State.
Hope it helps!
Use a HAVING clause to count and assert the number of diamond mines:
SELECT
I.state,
SUM(P.capacity)
FROM Infrastructure I
NATURAL JOIN Mine M
INNER JOIN produces P
ON P.mine = M.entryno
INNER JOIN Commodity C
ON C.comID = P.commodity
WHERE
C.name LIKE '%Diamond%'
GROUP BY
I.state
HAVING
COUNT(*) >= 3;
Note: Natural join seems error prone to me and could break at some point. The best thing to use are explicit joins.
I have a local implemetation of openmrs version 1.9.7 in a hospital in Kisumu, Kenya. Problem comes when I try to write queries to access the patient data collected from the database for data management purposes due to the complexity of the OpenMrs Database. I am also a little dusty with with sql since I have been off practice for a while but I need the data out asap.
The query that I have currently is as below
SELECT p.date_created as date_enrolled, pi.identifier, pi.identifier_type identifier_type ,
pn.given_name,pn.middle_name, pn.family_name, p.person_id, p.gender, p.birthdate, p.death_date,
ob.obs_datetime, cm.name as obs_type, CASE co.datatype_id when '1' then ob.value_numeric
when '2' then (select name from concept_name where concept_id = ob.value_coded limit 1)
when '3' then ob.value_text when '6' then ob.value_datetime when '10' then ob.value_boolean when '13' then ob.value_complex else "N/A" END AS obs_value, e.encounter_datetime
FROM person p JOIN person_name pn ON p.person_id = pn.person_id
JOIN patient_identifier pi ON p.person_id = pi.patient_id
JOIN patient_identifier_type pit ON pit.patient_identifier_type_id = pi.identifier_type
JOIN obs ob ON p.person_id = ob.person_id JOIN encounter e ON e.encounter_id = ob.encounter_id
JOIN concept_name cm ON ob.concept_id = cm.concept_id
JOIN concept co ON ob.concept_id = co.concept_id
JOIN concept_datatype cdt ON cdt.concept_datatype_id = co.datatype_id;
Is there an already existing query used that I can use as a starter and maybe modify to fit my needs?
Or rather how best do you advice for me to go through this?
Thanks
You did quite well, considering all the tables involved. This is very complicated SQL because you are mixing header and detail data in a single row. You have 2 fairly large problems here if you want to write something in a single query: 1) concept_name has multiple locales (languages), when I suspect you only want one. Try something like locale='en' to reduce the number of rows. 2) the obs table is EAV (entity-attribute-value), plus as you discovered, different value fields correspond to different datatypes. To deal with this, take a look at MySql's GROUP_CONCAT function. Here is a rough shot at doing your query, though I removed some things to simplify and just to get it to work. It will give you an idea of how to use GROUP_CONCAT, but I punted on the datatype -> value CASE statement :
SELECT p.date_created as date_enrolled,
pi.identifier, pi.identifier_type, pn.given_name,
left(GROUP_CONCAT(ob.obs_datetime, cm.name SEPARATOR '\t'),60) as 'ItemDate|ItemName',
e.encounter_datetime
FROM person p JOIN person_name pn ON p.person_id = pn.person_id
JOIN patient_identifier pi ON p.person_id = pi.patient_id
JOIN patient_identifier_type pit ON pit.patient_identifier_type_id = pi.identifier_type
JOIN obs ob ON p.person_id = ob.person_id
JOIN encounter e ON e.encounter_id = ob.encounter_id
JOIN concept_name cm ON ob.concept_id = cm.concept_id
JOIN concept co ON ob.concept_id = co.concept_id
JOIN concept_datatype cdt ON cdt.concept_datatype_id = co.datatype_id
where cm.locale = 'en'
group by 2 limit 10;
All that said, I don't think the OpenMRS folks would recommend that you keep trying this. Use their reporting tools instead.
Attempting to display all proposals in the system as long as they satisfy the following conditions:
the source must be supervisor
the status_code in the record table must not be 8 or 3
I have attempted a number of way in trying to make this work but I keep getting different data back which is not what I want.
The problem is how the tables have been set up. In theory a proposal can be applied for by a student and if that happens a record is automatically added to the 'record' table with a status set to '6' for pending. This status is then changed a number of times throughout the application process.
What I need is to show the users all proposals which have not been taken. The status codes to indicate this is '3' (accepted by a student so another student cannot take it) and '8' (not available).
NOTE: not all proposals may have a record in this table (a student has not applied for them)
This is the query so far which satisfies the first condition of being a "supervisor"
SELECT p.proposal_id, p.proposal_title,
p.description, u.user_record_id, u.forename,
u.surname, c.course_title, r.*,
GROUP_CONCAT(DISTINCT t.tag_title) AS tags
FROM proposal p
LEFT JOIN user u on u.user_record_id = p.user_record_id
LEFT JOIN proposal_tags pt on pt.proposal_id = p.proposal_id
LEFT JOIN tag_details t on t.tag_code = pt.tag_code
LEFT JOIN course_details c on c.course_code = p.course_code
LEFT JOIN record r on r.proposal_id = p.proposal_id
WHERE p.source = "Supervisor"
GROUP BY p.proposal_id
My attempt at getting it to display all records which are available:
SELECT p.proposal_id, p.proposal_title,
p.description, u.user_record_id, u.forename,
u.surname, c.course_title, r.*,
GROUP_CONCAT(DISTINCT t.tag_title) AS tags
FROM proposal p
LEFT JOIN user u on u.user_record_id = p.user_record_id
LEFT JOIN proposal_tags pt on pt.proposal_id = p.proposal_id
LEFT JOIN tag_details t on t.tag_code = pt.tag_code
LEFT JOIN course_details c on c.course_code = p.course_code
LEFT JOIN record r on r.proposal_id = p.proposal_id
WHERE p.source = "Supervisor"
AND (r.status_code != 3 OR r.status_code !=8)
GROUP BY p.proposal_id
The above query still returns proposals with the status code 3 and it fails to display any proposals which have yet not been applied for.
SQLFiddle Generated: http://sqlfiddle.com/#!9/b89a9/1/0
Any help would be much appreciated guys! Thank you.
Turned out I was missing a OR statement to achieve my goal.
To get the end result i needed, the query was modified to:
SELECT p.proposal_id, p.proposal_title, p.description, u.user_record_id, u.forename, u.surname, c.course_title, r.*, GROUP_CONCAT(DISTINCT t.tag_title) AS tags FROM proposal p
LEFT JOIN user u on u.user_record_id = p.user_record_id
LEFT JOIN proposal_tags pt on pt.proposal_id = p.proposal_id
LEFT JOIN tag_details t on t.tag_code = pt.tag_code
LEFT JOIN course_details c on c.course_code = p.course_code
LEFT JOIN record r on r.proposal_id = p.proposal_id
WHERE p.source = "Supervisor"
AND (r.status_code not in (3,8) OR r.status_code IS NULL)
GROUP BY p.proposal_id
Successfully brings back all proposals which are available, are from the supervisor and those which have no record in the 'record' table.
Thank you to everyone who helped
Why
Does this give incorrect results?
SELECT
people.name,
SUM(allorders.TOTAL),
SUM(allorders.DISCOUNT),
SUM(allorders.SERVICECHARGE),
SUM(payments.AMOUNT)
FROM
people
INNER JOIN
allorders ON allorders.CUSTOMER = people.ID
INNER JOIN
payments ON payments.CUSTOMER = people.ID
WHERE
people.ID = 7 AND allorders.VOIDED = 0 AND payments.VOIDED = 0
Gives: (the name), 1644000, 1100000, 50000, 1485000
If I do it two tables at a time (INNER JOIN people ON allorders.CUSTOMER = people.ID) in separate queries, I get the correct results. I don't don't even know where the numbers I get come from. Like:
SELECT
people.name,
SUM(allorders.TOTAL),
SUM(allorders.DISCOUNT),
SUM(allorders.SERVICECHARGE)
FROM
people
INNER JOIN
allorders ON allorders.CUSTOMER = people.ID
WHERE people.ID = 7 AND allorders.VOIDED = 0
Gives: (the name), 822000, 550000, 25000
SELECT
people.name,
SUM(payments.AMOUNT)
FROM
people
INNER JOIN payments ON payments.CUSTOMER = people.ID
WHERE people.ID = 7 AND payments.VOIDED = 0
Gives: (the name), 297000
It looks like it doubles, but I don't know why.
The odd thing is I have a similar query that does this sum correctly. I'll post it, but it's a bit complex. Here goes:
SELECT
t1.IDENTIFIER,
ifnull(t1.NAME,""),
t1.PRICE,
t1.GUESTS,
t1.STATUS,
ifnull(t1.NOTE,""),
t1.LINK,
ifnull(t1.EDITOR,""),
concat(t2.FIRSTNAME,"",t2.LASTNAME),
t2.ID,
t3.ID,
ifnull(t1.EMAIL,""),
ifnull(t3.PHONE,""),
ifnull(SUM(p1.AMOUNT),0),
ifnull(SUM(o1.DISCOUNT),0),
ifnull(SUM(o1.TOTAL),0),
ifnull(SUM(o1.SERVICECHARGE),0)
FROM
tables t1
INNER JOIN
people t2 ON t1.SELLER = t2.ID
INNER JOIN
people t3 ON t1.CUSTOMER = t3.ID
INNER JOIN
orderpaymentinfo ON orderpaymentinfo.TABLEID = t1.IDENTIFIER
INNER JOIN
payments p1 ON orderpaymentinfo.PAYMENTID = p1.PAYMENTID
INNER JOIN
allorders o1 ON o1.ORDERID = orderpaymentinfo.ORDERID
WHERE
p1.VOIDED = 0 AND o1.VOIDED = 0 AND t1.DATE = "2014-12-20"
GROUP BY t1.IDENTIFIER
The latter statement does the same join, only it uses an additional helper-table. I'm sorry it's a bit poorly formatted (I'm not great with SO's formatter), but if someone can tell me the difference between the logic in these two statements and how one can be completely wrong while the other right, I'd be very happy.
In response to answer:
Result 1:
Name - 5
Result 2:
Name - 2
Result 3:
Name - 10
Result 4 is truncated in phpMyAdmin - where would I get this easily?
Table structure for the three tables looks like:
SHOW create on the way.
Okay, so I am pretty sure you've a join condition that's basically exploding your result set into something like a Cartesian product. Here's what I think you should try
First, run the following and share the output:
SELECT p.name,COUNT(*)
FROM people as p
INNER JOIN allorders AS a
ON a.CUSTOMER = p.ID
WHERE p.ID = 7 AND a.VOIDED = 0
GROUP BY p.name
Then run
SELECT p.name,COUNT(*)
FROM people AS p
INNER JOIN payments AS pay
ON pay.CUSTOMER = p.ID
WHERE p.ID = 7 AND pay.VOIDED = 0
GROUP BY p.name
Then run
SELECT
p.name,
COUNT(*)
FROM
people as p
INNER JOIN
allorders as a ON a.CUSTOMER = p.ID
INNER JOIN
payments as pay ON pay.CUSTOMER = p.ID
WHERE
p.ID = 7 AND a.VOIDED = 0 AND pay.VOIDED = 0
GROUP BY p.name
Last run the following
SHOW CREATE TABLE people;
SHOW CREATE TABLE payments;
SHOW CREATE TABLE allorders;
The problem is that you don't have the correct understanding of your data. You need to give us a bit more info about the data and the relationships, and the output I've described here should help. Mine is not an answer. But if you run these queries and paste the output of them, you should be able to get an answer, either from me or someone else.
Based on the discussion and edits above, please try:
SELECT
p.name,
SUM(o.TOTAL),
SUM(o.DISCOUNT),
SUM(o.SERVICECHARGE),
MAX(pay.amt)
FROM
people as p
INNER JOIN
allorders AS o ON o.CUSTOMER = p.ID
INNER JOIN (SELECT customer,
SUM(amount) as amt
FROM payments
WHERE voided = 0 AND customer = 7
GROUP BY customer) AS pay
ON pay.customer = p.id
WHERE
p.ID = 7 AND o.VOIDED = 0
GROUP BY p.name
You could also do a subquery in your SELECT statement, but it's pretty obnoxious imo. You could also do min(pay.amt) or avg or even just leave the aggregate out altogether. The above should work... even though there are cleaner ways. I'm providing this answer so you can reason about why you were getting the unexpected result... actually optimizing your query is a different question that you can dive into later, once you've had a chance to look over this
Please I need to figure out what I am doing wrong. I created this inner join code for mysql. it works but it gives me repeated values like repeating a particular row twice or categoryid twice. each of the tables(users,paymentnotification,monthlyreturns) has the categoryid used to check and display the username(users.pname) from the user table, then check and display those that have made payment from the monthly returns and payment table using the categoryid.
$r="SELECT monthlyreturns.categoryid, monthlyreturns.month, monthlyreturns.quarter, monthlyreturns.year,paymentnotification.amount, users.pname, monthlyreturns.ototal, paymentnotification.payee, status
FROM paymentnotification
INNER JOIN (monthlyreturns INNER JOIN users ON monthlyreturns.categoryid=users.categoryid)
ON monthlyreturns.categoryid=paymentnotification.categoryid
ORDER BY monthlyreturns.categoryid DESC";
I think the query you want is more like this:
SELECT b.categoryid, b.month, b.quarter, b.year, a.amount, c.pname, b.ototal, a.payee, status
FROM paymentnotification a
INNER JOIN monthlyreturns b
ON a.categoryid = b.categoryid
INNER JOIN users c
ON b.categoryid = c.categoryid
ORDER BY b.categoryid DESC
The way you are doing the correlations doesn't seem clear and may cause problems. Try this one out and see what happens. If its still doing duplicates, perhaps the nature of the data require further filtering.
Assuming I understand what you're trying to do, you are not joining your tables properly. Try joining one at a time
SELECT DISTINCT monthlyreturns.categoryid, monthlyreturns.month, monthlyreturns.quarter, monthlyreturns.year,paym entnotification.amount, users.pname, monthlyreturns.ototal, paymentnotification.payee, status
FROM paymentnotification
INNER JOIN monthlyreturns
ON paymentnotification.categoryid = monthlyreturns.categoryid
INNER JOIN users
ON monthlyreturns.categoryid = users.categoryid
ORDER BY monthlyreturns.categoryid DESC
I don't see any problem.. I get 4 result rows: check this fiddle http://sqlfiddle.com/#!2/165a22/5
this is the query I used:
SELECT m.categoryid, m.month, m.quarter, m.year,p.amount, u.pname, m.ototal, p.payee, m.status
FROM paymentnotification p JOIN monthlyreturns m ON p.categoryid = m.categoryid
JOIN users u ON u.categoryid = m.categoryid
ORDER BY m.categoryid DESC
there are no duplicated rows, just "unique" rows if you consider every column you choose.
Hope it helps
SELECT M.categoryid, M.month, M.quarter, M.year, M.ototal,
P.amount, P.payee, P.status,
U.pname
FROM paymentnotification AS P
INNER JOIN monthlyreturns AS M ON P.categoryid = M.categoryid
INNER JOIN users AS U ON M.categoryid = U.categoryid
ORDER BY M.categoryid DESC