SQL Retrieve data with more than 1 record - sql

I was encountered with a "not a GROUP BY expression"error.
I would need to search for similar title, medium and description.
Artist table consist of artistid, artist last name and artist first name.
work table consist of workid, title, medium, description, artistid
artistid are unique key.
List the details of any works of art that have more than one copy recorded in the database.
SELECT W.workid, W.title, W.medium, W.description, W.artistid, A.FirstName, A.LastName
FROM dtoohey.work W, dtoohey.artist A
GROUP BY W.artistid, A.FirstName, A.LastName
having count(*) > 1;

Seems you don't have a proper join between the tables (i have suggest one .. you should do the correct one)
If you want the group by on W.artistIid (alias the count of the workid for each artist) you cannot have W.workid, W.title, W.medium in select
SELECT W.artistid, A.FirstName, A.LastName
FROM dtoohey.work W
INNER JOIN dtoohey.artist A ON A.artistid = W.artistid
GROUP BY W.artistid, A.FirstName, A.LastName
having count(*) > 1;
otherwise if you want check if the select return more that one rows for the column select you must add all column to the group by clause
SELECT W.workid, W.title, W.medium, W.description, W.artistid, A.FirstName, A.LastName
FROM dtoohey.work W
INNER JOIN dtoohey.artist A ON A.artistid = W.artistid
GROUP BY W.workid, W.title, W.medium, W.description, W.artistid, A.FirstName, A.LastName
having count(*) > 1;

Maybe writing an answer (that focuses on #mathguy's observation of the missing join specifity and the one, that the mix of SELECT columns versus GROUP BY/HAVING does not fit) is better in finding out what your problem is and giving ideas how to enhance the question ;-) ... next time I suggest to be more heavy on the question, so the world does not have to work so heavy on the answers.
I do not think this is a MySQL, Oracle, or what not database specific problem, but more a SQL beginners learning journey ... you may want to also look for join explanations here: "Difference between Inner Join & Full join"
Starting from the minimal subset of info given in question: 2 tables artist and work related presumable over a shared id (say artist_id).
One thing directly asking for trouble in databases like PostgreSQL or ParStream is selecting columns in group by queries neither being grouped by nor aggregated / filtered. But here we go:
Create tables:
$psql> CREATE TABLE artist(artist_id INT, given_name VARCHAR(42), family_name VARCHAR(99));
CREATE TABLE
$psql> CREATE TABLE work(work_id INT, artist_id INT, title VARCHAR(42));
CREATE TABLE
Insert some data:
$psql> INSERT INTO artist VALUES(1, 'John', 'Doe');
INSERT 0 1
$psql> INSERT INTO artist VALUES(2, 'Natalie', 'Noir');
INSERT 0 1
$psql> INSERT INTO work VALUES(43, 1, 'The game is on');
INSERT 0 1
$psql> INSERT INTO work VALUES(44, 1, 'The game is over');
INSERT 0 1
$psql> INSERT INTO work VALUES(98, 2, 'La nuit commonce');
INSERT 0 1
$psql> INSERT INTO work VALUES(97, 2, 'Un jour se lve');
INSERT 0 1
Check what is in it:
$psql> SELECT * FROM work;
work_id | artist_id | title
---------+-----------+------------------
43 | 1 | The game is on
44 | 1 | The game is over
98 | 2 | La nuit commonce
97 | 2 | Un jour se lve
(4 rows)
$psql> SELECT * FROM artist;
artist_id | given_name | family_name
-----------+------------+-------------
1 | John | Doe
2 | Natalie | Noir
(2 rows)
Show the implicit INNER JOIN:
$psql> SELECT * FROM work W, artist A;
work_id | artist_id | title | artist_id | given_name | family_name
---------+-----------+------------------+-----------+------------+-------------
43 | 1 | The game is on | 1 | John | Doe
43 | 1 | The game is on | 2 | Natalie | Noir
44 | 1 | The game is over | 1 | John | Doe
44 | 1 | The game is over | 2 | Natalie | Noir
98 | 2 | La nuit commonce | 1 | John | Doe
98 | 2 | La nuit commonce | 2 | Natalie | Noir
97 | 2 | Un jour se lve | 1 | John | Doe
97 | 2 | Un jour se lve | 2 | Natalie | Noir
(8 rows)
Show the explicit INNER JOIN with a dummy condition to let the parser pass our query (Update: Don't use this at home, only to show the mish-mash.):
$psql> SELECT * FROM work W INNER JOIN artist A ON 1 = 1;
work_id | artist_id | title | artist_id | given_name | family_name
---------+-----------+------------------+-----------+------------+-------------
43 | 1 | The game is on | 1 | John | Doe
43 | 1 | The game is on | 2 | Natalie | Noir
44 | 1 | The game is over | 1 | John | Doe
44 | 1 | The game is over | 2 | Natalie | Noir
98 | 2 | La nuit commonce | 1 | John | Doe
98 | 2 | La nuit commonce | 2 | Natalie | Noir
97 | 2 | Un jour se lve | 1 | John | Doe
97 | 2 | Un jour se lve | 2 | Natalie | Noir
(8 rows)
Now a more useful INNER JOIN matching only these entries from the two tables, that are related through "creator" relationship:
$psql> SELECT * FROM work W INNER JOIN artist A ON W.artist_id = A.artist_id;
work_id | artist_id | title | artist_id | given_name | family_name
---------+-----------+------------------+-----------+------------+-------------
43 | 1 | The game is on | 1 | John | Doe
44 | 1 | The game is over | 1 | John | Doe
98 | 2 | La nuit commonce | 2 | Natalie | Noir
97 | 2 | Un jour se lve | 2 | Natalie | Noir
(4 rows)
So above we blindly trust the data managing part to magically enter artist_id values always correctly and matching our expectations (in real life a REFERENCES foreign key constraint would surely placed on the column in the work table (no work without an artist/creator would dictate the artist table to be the "first" causally).
Above you also see that selecting from a list of tables is identical to an INNER JOIN without any constraint i.e. the cartesian product of all entries from table work with all all entries from table artist.
Now your query (edited a tad for the minimal table model) besides not clear to me in its request idea, errors out as explained on top of this answer text:
$psql> SELECT W.work_id, W.title, W.artist_id, A.given_name, A.family_name FROM work W, artist A GROUP BY W.artist_id, A.given_name, A.family_name HAVING COUNT(*) > 1;
ERROR: column "w.work_id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT W.work_id, W.title, W.artist_id, A.given_name, A.fam...
This is of course not cured by using a more meaningful joined input set (claro, as the error points to the mismatch in select and group by lists:
$psql> SELECT W.work_id, W.title, W.artist_id, A.given_name, A.family_name FROM work W INNER JOIN artist A ON W.artist_id = A.artist_id GROUP BY W.artist_id, A.given_name, A.family_name HAVING COUNT(*) > 1;
ERROR: column "w.work_id" must appear in the GROUP BY clause or be used in an aggregate function
LINE 1: SELECT W.work_id, W.title, W.artist_id, A.given_name, A.fam...
You need suggest input on what you want to achieve to obtain a single answer (working). Until you do so, here are some offerings:
Given that you join on only existing pairs of artis and work ids, you do not need that having clause, as neither non-existing artist nor missing works nor missing combinations of artists and works will ever enter the rowset your query works on, so:
$psql> SELECT title, R.* FROM ( SELECT W.work_id AS work_id_filtered, W.artist_id, A.given_name, A.family_name FROM work W INNER JOIN artist A ON W.artist_id = A.artist_id GROUP BY W.work_id, W.artist_id, A.given_name, A.family_name) R INNER JOIN work WW ON WW.work_id = R.work_id_filtered;
title | work_id_filtered | artist_id | given_name | family_name
------------------+------------------+-----------+------------+-------------
The game is on | 43 | 1 | John | Doe
The game is over | 44 | 1 | John | Doe
La nuit commonce | 98 | 2 | Natalie | Noir
Un jour se lve | 97 | 2 | Natalie | Noir
(4 rows)
This should give you a bit clumsy but good enough for my sunday morning the nice list of all titles (non-grouped files) combined with the grouped fields from the inner query. Formatted query might be written as:
SELECT title,
R.*
FROM
(SELECT W.work_id AS work_id_filtered,
W.artist_id,
A.given_name,
A.family_name
FROM
work W
INNER JOIN artist A ON W.artist_id = A.artist_id
GROUP BY W.work_id,
W.artist_id,
A.given_name,
A.family_name) R
INNER JOIN
work WW ON WW.work_id = R.work_id_filtered;
Removing any GROUP BY (until the question offers detail on why it would be needed for the task):
$psql> SELECT W.work_id, W.title, W.artist_id, A.given_name, A.family_name FROM work W INNER JOIN artist A ON W.artist_id = A.artist_id;
work_id | title | artist_id | given_name | family_name
---------+------------------+-----------+------------+-------------
43 | The game is on | 1 | John | Doe
44 | The game is over | 1 | John | Doe
98 | La nuit commonce | 2 | Natalie | Noir
97 | Un jour se lve | 2 | Natalie | Noir
(4 rows)
Query formatted to not have to scroll horizontally:
SELECT W.work_id, W.title, W.artist_id, A.given_name, A.family_name
FROM work W INNER JOIN artist A ON W.artist_id = A.artist_id;
Note: Yes, as #ThorstenKettner pointed rightfully out, I made up the term "FULL INNER JOIN", I smoetimes do, sorry. Maybe my brain needs cartesian filling to balance the LEFT|RIGHT|FULL OUTER JOINs - who knows ;-)

I used scaisEdge answer and edited. What I realize was if I select workid(unique key), I won't be able to display anything but without it, all my field is up.

As has been mentioned, the problem is mainly that you don't yet fully understand what you are doing.
First point is your join. By merely separating the tables with commas, you are using a syntax that was made redundant more than twenty years ago. It seems rare that you, as a beginner, use this. You must have found this in a very old book or tutorial. In short: Don't join tables like this. Use explicit joins. A comma means CROSS JOIN. So what you have is:
FROM dtoohey.work W CROSS JOIN dtoohey.artist A
That means that you combine every artist with every work. This is most likely not what you want. You want to join related artists and works. Your query shows there is an artistid in your work table, so one work is made by one artist in your model. The appropriate join would hence be:
FROM dtoohey.work w
INNER JOIN dtoohey.artist a ON a.artistid = w.artistid
The second point is that you are aggregating rows. GROUP BY W.artistid, A.FirstName, A.LastName tells the DBMS to aggregate the rows such that you get one result row per artist. With having count(*) > 1 you say you only want artists with more than one work. But in your select clause you are showing a work (W.workid, W.title, W.medium, W.description). Which one? If you only show one row per artist and each artist has more than one work, which work of an artist do you show? The DBMS notices that you forgot to tell it what to select and raises an error. And you will probably agree now that the query with the given GROUP BY and HAVING clauses makes no sense.

Related

Compare Two Relations in SQL

I just started studying SQL and this is a demo given by the teacher in an online course and it works fine. The statement is looking for "students such that number of other students with same GPA is equal to number of other students with same sizeHS":
select *
from Student S1
where (
select count(*)
from Student S2
where S2.sID <> S1.sID and S2.GPA = S1.GPA
) = (
select count(*)
from Student S2
where S2.sID <> S1.sID and S2.sizeHS = S1.sizeHS
);
It seems that in this where clause, we're comparing two relations (because the result of a subquery is a relation), but most of the time we are comparing attributes(as far as I've seen).
So I'm thinking about whether there are requirements for how many attributes, and how many tuples, the RELATION should contain when comparing two RELATIONS. If not, how do we compare two RELATIONS when there're multiple attributes or multiple tuples and what do we get for result?
Note:
Student relation has 4 attributes: sID, sName, GPA, sizeHS. And here's the data:
+-----+--------+-----+--------+
| sID | sName | GPA | sizeHS |
+-----+--------+-----+--------+
| 123 | Amy | 3.9 | 1000 |
| 234 | Bob | 3.6 | 1500 |
| 345 | Craig | 3.5 | 500 |
| 456 | Doris | 3.9 | 1000 |
| 567 | Edward | 2.9 | 2000 |
| 678 | Fay | 3.8 | 200 |
| 789 | Gary | 3.4 | 800 |
| 987 | Helen | 3.7 | 800 |
| 876 | Irene | 3.9 | 400 |
| 765 | Jay | 2.9 | 1500 |
| 654 | Amy | 3.9 | 1000 |
| 543 | Craig | 3.4 | 2000 |
+-----+--------+-----+--------+
and the result of this query is:
+-----+--------+-----+---------+
| sID | sName | GPA | sizeHS |
+-----+--------+-----+---------+
| 345 | Craig | 3.5 | 500 |
| 567 | Edward | 2.9 | 2000 |
| 678 | Fay | 3.8 | 200 |
| 789 | Gary | 3.4 | 800 |
| 765 | Jay | 2.9 | 1500 |
| 543 | Craig | 3.4 | 2000 |
+-----+--------+-----+---------+
because the result of a subquery is a relation
Relation is the scientific name for what we call a table in a database and I like the name "table" much better than "relation". A table is easy to imagine. We know them from our school time schedule for instance. Yes, we relate things here inside a table (day and time and the subject taught in school), but we can also relate tables to tables (pupils' timetables with the table of class rooms, the overall subject schedule, and the teacher's timetables). As such, tables in an RDBMS are also related to each other (hence the name relational database management system). I find the name relation for a table quite confusing (and many people use the word "relation" to describe the relations between tables instead).
So, yes, a query result itself is again a table ("relation"). And from tables we can of course select:
select * from (select * from b) as subq;
And then there are scalar queries that return exactly one row and one column. select count(*) from b is such a query. While this is still a table we can select from
select * from (select count(*) as cnt from b) as subq;
we can even use them where we usually have single values, e.g. in the select clause:
select a.*, (select count(*) from b) as cnt from a;
In your query you have two scalar subqueries in your where clause.
With subqueries there is another distinction to make: we have correlated and non-correlated subqueries. The last query I have just shown contains a non-correlated subquery. It selects the count of b rows for every single result row, no matter what that row contains elsewise. A correlated subquery on the other hand may look like this:
select a.*, (select count(*) from b where b.x = a.y) as cnt from a;
Here, the subquery is related to the main table. For every result row we look up the count of b rows matching the a row we are displaying via where b.x = a.y, so the count is different from row to row (but we'd get the same count for a rows sharing the same y value).
Your subqueries are also correlated. As with the select clause, the where clause deals with one row at a time (in order to keep or dismiss it). So we look at one student S1 at a time. For this student we count other students (S2, where S2.sID <> S1.sID) who have the same GPA (and S2.GPA = S1.GPA) and count other students who have the same sizeHS. We only keep students (S1) where there are exactly as many other students with the same GPA as there are with the same sizeHS.
UPDATE
As do dealing with multiple tuples as in
select *
from Student S1
where (
select count(*), avg(grade)
from Student S2
where S2.sID <> S1.sID and S2.GPA = S1.GPA
) = (
select count(*), avg(grade)
from Student S2
where S2.sID <> S1.sID and S2.sizeHS = S1.sizeHS
);
this is possible in some DBMS, but not in SQL Server. SQL Server doesn't know tuples.
But there are other means to achieve the same. You could just add two subqueries:
select * from student s1
where (...) = (...) -- compare counts here
and (...) = (...) -- compare averages here
Or get the data in the FROM clause and then deal with it. E.g.:
select *
from Student S1
cross apply
(
select count(*) as cnt, avg(grade) as avg_grade
from Student S2
where S2.sID <> S1.sID and S2.GPA = S1.GPA
) sx
cross apply
(
select count(*) as cnt, avg(grade) as avg_grade
from Student S2
where S2.sID <> S1.sID and S2.sizeHS = S1.sizeHS
) sy
where sx.cnt = sy.cnt and sx.avg_grade = sy.avg_grade;
There are relational operations:
The intersection operator produces the set of tuples that two
relations share in common. Intersection is implemented in SQL in the
form of the INTERSECT operator.
The difference operator acts on two relations and produces the set of tuples from the first relation that do not exist in the second relation. Difference is implemented in SQL in the form of the EXCEPT or MINUS operator.
So, in the context of SQL Server, for example, you can do:
SELECT *
FROM R1
EXCEPT
SELECT *
FROM R2
to get rows in R1 not included in R2 and the reverse - to get all differences.
Of course, the attributes must be the same - if not, you need to explicit set the attributes in the SELECT.

In a query (no editing of tables) how do I join data without any similarities?

I Have a query that finds a table, here's an example one.
Name |Age |Hair |Happy | Sad |
Jon | 15 | Black |NULL | NULL|
Kyle | 18 |Blonde |YES |NULL |
Brad | 17 | Blue |NULL |YES |
Name and age come from one table in a database, hair color comes from a second which is joined, and happy and sad come from a third table.My goal would be to make the first line of the chart like this:
Name |Age |Hair |Happy |Sad |
Jon | 15 |Black |Yes |Yes |
Basically I want to get rid of the rows under the first and get the non NULL data joined to the right. The problem is that there is no column where the Yes values are on the Jon row, so I have no idea how to get them there. Any suggestions?
PS. With the data I am using I can't just put a 'YES' in the 'Jon' row and call it a day, I would need to find the specific value from the lower rows and somehow get that value in the boxes that are NULL.
Do you just want COALESCE()?
COALESCE(Happy, 'Yes') as happy
COALESCE() replaces a NULL value with another value.
If you want to join on a NULL value work with nested selects. The inner select gets an Id for NULLs, the outer select joins
select COALESCE(x.Happy, yn_table.description) as happy, ...
from
(select
t1.Happy,
CASE WHEN t1.Happy is null THEN 1 END as happy_id
from t1 ...) x
left join yn_table
on x.xhappy_id = yn_table.id
If you apply an ORDER BY to the query, you can then select the first row relative to this order with WHERE rownum = 1. If you don't apply an ORDER BY, then the order is random.
After reading your new comment...
the sense is that in my real data the yes under the other names will be a number of a piece of equipment. I want the numbers of the equipment in one row instead of having like 8 rows with only 4 ' yes' values and the rest null.
... I come to the conclusion that this a XY problem.
You are asking about a detail you think will solve your problem, instead of explaining the problem and asking how to solve it.
If you want to store several pieces of equipment per person, you need three tables.
You need a Person table, an Article table and a junction table relating articles to persons to equip them. Let's call this table Equipment.
Person
------
PersonId (Primary Key)
Name
optional attributes like age, hair color
Article
-------
ArticleId (Primary Key)
Description
optional attributes like weight, color etc.
Equipment
---------
PersonId (Primary Key, Foreign Key to table Person)
ArticleId (Primary Key, Foreign Key to table Article)
Quantity (optional, if each person can have only one of each article, we don't need this)
Let's say we have
Person: PersonId | Name
1 | Jon
2 | Kyle
3 | Brad
Article: ArticleId | Description
1 | Hat
2 | Bottle
3 | Bag
4 | Camera
5 | Shoes
Equipment: PersonId | ArticleId | Quantity
1 | 1 | 1
1 | 4 | 1
1 | 5 | 1
2 | 3 | 2
2 | 4 | 1
Now Jon has a hat, a camera and shoes. Kyle has 2 bags and one camera. Brad has nothing.
You can query the persons and their equipment like this
SELECT
p.PersonId, p.Name, a.ArticleId, a.Description AS Equipment, e.Quantity
FROM
Person p
LEFT JOIN Equipment e
ON p.PersonId = e.PersonId
LEFT JOIN Article a
ON e.ArticleId = a.ArticleId
ORDER BY p.Name, a.Description
The result will be
PersonId | Name | ArticleId | Equipment | Quantity
---------+------+-----------+-----------+---------
3 | Brad | NULL | NULL | NULL
1 | Jon | 4 | Camera | 1
1 | Jon | 1 | Hat | 1
1 | Jon | 5 | Shoes | 1
2 | Kyle | 3 | Bag | 2
2 | Kyle | 4 | Camera | 1
See example: http://sqlfiddle.com/#!4/7e05d/2/0
Since you tagged the question with the oracle tag, you could just use NVL(), which allows you to specify a value that would replace a NULL value in the column you select from.
Assuming that you want the 1st row because it contains the smallest age:
- wrap your query inside a CTE
- in another CTE get the 1st row of the query
- in another CTE get the max values of Happy and Sad of your query (for your sample data they both are 'YES')
- cross join the last 2 CTEs.
with
cte as (
<your query here>
),
firstrow as (
select name, age, hair from cte
order by age
fetch first row only
),
maxs as (
select max(happy) happy, max(sad) sad
from cte
)
select f.*, m.*
from firstrow f cross join maxs m
You can try this:
SELECT A.Name,
A.Age,
B.Hair,
C.Happy,
C.Sad
FROM A
INNER JOIN B
ON A.Name = B.Name
INNER JOIN C
ON A.Name = B.Name
(Assuming that Name is the key columns in the 3 tables)

How to combine in one sql query in extra column the result of 2 group by queries?

Considering the following mdl_course_completions table that describes a course completion for a user:
id,bigint
userid,bigint
course,bigint
timeenrolled,bigint
timestarted,bigint
timecompleted,bigint
reaggregate,bigint
To determinate if a course has been finished by a student, I use a predicate on the timecompleted field.
When this field is null, the student has not finished the course, but when this field is not null, that means the student has finished the course.
Thus, the count of the number of students that finished course by course is given by:
SELECT mdl_course.fullname,count(*) as "number of students that didn't finish courses"
FROM mdl_course_completions
INNER JOIN mdl_course on mdl_course.id = mdl_course_completions.course
WHERE timecompleted IS NOT NULL
GROUP BY mdl_course.fullname
;
the result is:
| course name | number of students that finish courses |
|-------------|----------------------------------------|
| course 1 | 50 |
| course 2 | 200 |
| course 3 | 120 |
AND the count of the number of students that DIDN'T finished course by course is given by:
SELECT mdl_course.fullname,count(*) as "number student that didn't finish courses"
FROM mdl_course_completions
INNER JOIN mdl_course on mdl_course.id = mdl_course_completions.course
WHERE timecompleted IS NULL
GROUP BY mdl_course.fullname
;
the result is:
| course name | number of students that didn't finish courses |
|-------------|-----------------------------------------------|
| course 1 | 12 |
| course 2 | 12 |
| course 3 | 120 |
I wonder how can I combine this 2 queries to get in one query the results in an extra column such as:
| course name | number of students that finish courses | number of students that didn't finish courses |
|-------------|------------------------------------|-------------------------------------------|
| course 1 | 50 | 12 |
| course 2 | 200 | 12 |
| course 3 | 120 | 120 |
I am using postgresql.In my opinion, this kind of stuff is not related to database system. I just don't know how to proceed to combine these 2 queries in one in an extra column with the GROUP BY clause.
Use conditional aggregation.
SELECT mdl_course.fullname
,SUM((timecompleted IS NOT NULL)::int) as "number student that finish courses"
,SUM((timecompleted IS NULL)::int) as "number student that didn't finish courses"
FROM mdl_course_completions
INNER JOIN mdl_course on mdl_course.id = mdl_course_completions.course
GROUP BY mdl_course.fullname
From PostgreSQL 9.4 on, you can use the FILTER clause with aggregate functions:
count(*) FILTER (WHERE timecompleted IS NOT NULL)

Multiple select or distinct

I am new to sql so looking for a little help - got the first part down however I am having issues with the second part.
I got the three tables tied together. First I needed to tie tblPatient.ID = tblPatientVisit.PatientID together to eliminate dups which works
Now I need to take those results and eliminate dups in the MRN but my query is only returning one result which is WRONG - LOL
Query
select
tblPatient.id,
tblPatient.firstname,
tblPatient.lastname,
tblPatient.dob,
tblPatient.mrn,
tblPatientSmokingScreenOrder.SmokeStatus,
tblPatientVisit.VisitNo
from
tblPatient,
tblPatientSmokingScreenOrder,
tblPatientVisit
Where
tblPatient.ID = tblPatientVisit.PatientID
and tblPatientVisit.ID = tblPatientSmokingScreenOrder.VisitID
and tblPatient.ID in(
Select Distinct
tblPatient.mrn
From
tblPatient
where
isdate(DOB) = 1
and Convert(date,DOB) <'12/10/2000'
and tblPatientVisit.PatientType = 'I')
Actual Results:
ID | firstName | LastName | DOB | MRN | SmokeStatus | VisitNO
12 | Test Guy | Today | 12/12/1023 | 0015396 | Never Smoker | 0013957431
Desired Results:
90 | BOB | BUILDER | 02/24/1974 | 0015476 | Former Smoker | 0015476001
77 | DORA | EXPLORER | 06/04/1929 | 0015463 | Never Smoker | 0015463001
76 | MELODY | VALENTINE | 09/17/1954 | 0015461 | Current | 0015461001
32 | STRAWBERRY | SHORTCAKE | 07/06/1945 | 0015415 | Current | 0015415001
32 | STRAWBERRY | SHORTCAKE | 07/06/1945 | 0015415 | Never Smoker | 0015415001
32 | STRAWBERRY | SHORTCAKE | 07/06/1945 | 0015415 | Former Smoker | 0015415001
12 | Test Guy | Today | 12/12/1023 | 0015345 | Never Smoker | 0013957431
Anyone have any suggestions on how I go down to the next level and get all the rows with one unique MRN. From the data above I should have 5 in my list. Any help would be appreciated.
Thanks
If I had to guess -- The only thing that looks odd (but maybe it's OK) is that you're comparing patient.ID from your parent query to patient.mrn in the subquery.
Beyond that -- things to check:
(1)
Do you get all your patients with the inner query?
Select Distinct
tblPatient.mrn
From
tblPatient
where
isdate(DOB) = 1
and Convert(date,DOB) <'12/10/2000'
and tblPatientVisit.PatientType = 'I'
(2)
What is your patient type for the missing records? (Your filtering it to tblPatientVisit.PatientType = 'I' -- do the missing records have that patient type as well?)
Perhaps you need to invert the logic here. As in,
Select Distinct
patients.mrn1
From (select
tblPatient.id as id1,
tblPatient.firstname as firstname1,
tblPatient.lastname as lastname1,
tblPatient.dob as DOB1,
tblPatient.mrn as mrn1,
tblPatientSmokingScreenOrder.SmokeStatus as SmokeStatus1,
tblPatientVisit.VisitNo as VisitNo1,
tblPatientVisit.PatientType as PatientType1,
from
tblPatient,
tblPatientSmokingScreenOrder,
tblPatientVisit
Where
tblPatient.ID = tblPatientVisit.PatientID
and tblPatientVisit.ID = tblPatientSmokingScreenOrder.VisitID
) as patients
where
isdate(patients.DOB1) = 1
and Convert(date,patients.DOB1) <'12/10/2000'
and patients.PatientType1 = 'I');
Cleaned it up a bit and I think they were right. MRN wont match patient id, at least not from your example data. You should not need an inner query. This query should give you what you want.
SELECT DISTINCT
p.id,
p.firstname,
p.lastname,
p.dob,
p.mrn,
s.SmokeStatus,
v.VisitNo
FROM
tblPatient p
JOIN tblPatientVisit v ON p.id = v.patientId
JOIN tblPatientSmokingScreenOrder s ON v.id = s.visitId
WHERE
isdate(p.DOB) = 1
AND CONVERT(date,p.DOB) <'12/10/2000'
AND v.PatientType = 'I'

join on three tables? Error in phpMyAdmin

I'm trying to use a join on three tables query I found in another post (post #5 here). When I try to use this in the SQL tab of one of my tables in phpMyAdmin, it gives me an error:
#1066 - Not unique table/alias: 'm'
The exact query I'm trying to use is:
select r.*,m.SkuAbbr, v.VoucherNbr from arrc_RedeemActivity r, arrc_Merchant m, arrc_Voucher v
LEFT OUTER JOIN arrc_Merchant m ON (r.MerchantID = m.MerchantID)
LEFT OUTER JOIN arrc_Voucher v ON (r.VoucherID = v.VoucherID)
I'm not entirely certain it will do what I need it to do or that I'm using the right kind of join (my grasp of SQL is pretty limited at this point), but I was hoping to at least see what it produced.
(What I'm trying to do, if anyone cares to assist, is get all columns from arrc_RedeemActivity, plus SkuAbbr from arrc_Merchant where the merchant IDs match in those two tables, plus VoucherNbr from arrc_Voucher where VoucherIDs match in those two tables.)
Edited to add table samples
Table arrc_RedeemActivity
RedeemID | VoucherID | MerchantID | RedeemAmt
----------------------------------------------
1 | 2 | 3 | 25
2 | 6 | 5 | 50
Table arrc_Merchant
MerchantID | SkuAbbr
---------------------
3 | abc
5 | def
Table arrc_Voucher
VoucherID | VoucherNbr
-----------------------
2 | 12345
6 | 23456
So ideally, what I'd like to get back would be:
RedeemID | VoucherID | MerchantID | RedeemAmt | SkuAbbr | VoucherNbr
-----------------------------------------------------------------------
1 | 2 | 3 | 25 | abc | 12345
2 | 2 | 5 | 50 | def | 23456
The problem was you had duplicate table references - which would work, except for that this included table aliasing.
If you want to only see rows where there are supporting records in both tables, use:
SELECT r.*,
m.SkuAbbr,
v.VoucherNbr
FROM arrc_RedeemActivity r
JOIN arrc_Merchant m ON m.merchantid = r.merchantid
JOIN arrc_Voucher v ON v.voucherid = r.voucherid
This will show NULL for the m and v references that don't have a match based on the JOIN criteria:
SELECT r.*,
m.SkuAbbr,
v.VoucherNbr
FROM arrc_RedeemActivity r
LEFT JOIN arrc_Merchant m ON m.merchantid = r.merchantid
LEFT JOIN arrc_Voucher v ON v.voucherid = r.voucherid