questions about two tables and joins and foreign and primary keys.

I have two tables and am trying to generate a select statement (fist time i’ve ever used a join)

I have two tables Patient and Study

Patients primary key is PatientID
Patient has two columns, PatientID, StudyID

Study primary key is StudyID
Study has two columns, StudyID, StudyDescription

select
pt.PatientID,
pt.StudyID
st.StudyDesciption
from Patient pt
join Study st on pt.fk = st.pk
where st.PatientID = “foo”

I can’t quite get the syntax right for the join

is pt.fk StudyID and st.pk st.StudyID

Is pt just an alias for the work Patient and st Study? like are they usable interchangeably?

I don’t see where you created / defined pk or fk.
Try this for your join:

JOIN  Study st ON pt.StudyID = st.StudyID

The table design for Patient and Study is a bit interesting.

a few questions:

  1. In addition to primary key, are you using PatientID as MRN Reference?
  2. Is your intent to store study level information on the Patient Table?

The example here looks like 2 out of a set of at least 3 tables.

I expect that the database has

PatientDetails , holding PatientID and name, perhaps address, date of birth… who knows

Patient (badly named, should be PatientStudies or similar ) which connects Patients with Studies … one patient can have zero to many studies. It holds the PatientID and the StudyID

Study which describes the Study

To see all the data, you might link together all 3
PatientsDetails.PatientID = Patient.PatientID and Patient.StudyID = StudyID

If you used an INNER join here, you would get any patient that had at least one study.
You would see several rows for a patient that had several studies
And no rows for patients that had no studies.

To see all patients, even if they have no studies, you need an OUTER join, which means ‘it doesnt matter if there are no studies’

To rework your query in that way:

select
pt.PatientID, pt.StudyID, st.StudyDesciption
from Patient pt
left outer join Study st
on pt.StudyID = st.StudyID
where st.PatientID = ‘foo’

remove the left outer if you only want rows for patients who do have studies.
PatientID here is surrounded by single quotes, but it may be a number, in which case you dont need the quotes

SQL Fiddle is a handy site for testing out queries, joins, etc. Supports syntax for popular DBs.

select 
pt.PatientID, 
pt.StudyID
st.StudyDesciption
from Patient pt, Study st 
where st.PatientID = "foo"
and pt.StudyID=st.StudyID

no inner or outter or complex hoops here… simple straight forward… unless I mis-read the requirements

Thanks all.
I never quite wrapped my head around joins. What dave S did seems more like something I can understand.
I’m not sure I understand why join was necessary when what Dave wrote seems to me to be how it should be.

Join is the more modern syntax for the same thing dave posted which I find easier to read by a mile most times but then I started with sql in 1989 or so which predates this syntax by many years :slight_smile:

One of the things that JOIN syntax avoids is all the vendor specific extensions to sql that they used to write inner & outer joins "old style) Like = or = which meant “left join” or “right join” in some systems and was easy to overlook

One things Dave’s query wont do that the suggested JOIN will do is return ALL patients whether they have a study associated or not (thats where the outer parts comes in to play)

What LEFT OUTER JOIN means is “keep EVERY ROW from the LEFT HAND” tables regardless of whether they have a match on the right
SO in the case of

select
pt.PatientID, pt.StudyID, st.StudyDesciption
from Patient pt
left outer join Study st
on pt.StudyID = st.StudyID

where st.PatientID = ‘foo’

EVERY row from pt (patient) would be listed regardless of whether a match in study (st) was found using pt.StudyID = st.StudyID

Daves will only list it IF there IS a match - so slightly different

If I might continue this thread…
I have two tables, a study table and a series table.
A study has many series.

My study table was suppose to have a column called modalities but doesn’t.
Modalities when examined (in other databases) looks like CT/MR/CR/OT
My series table has a column called modality
Each series can have a modality.

I want to create a study table query that ‘fabricates’ modalities by querying the series table for a unique list of modalities and building the modalities column.

It’s like somehow I have to build a select statement for the study table that contains a pseudo column built fom a select statement of the series table for the study, but it has to be 1 sql statement.

Glad I ran into this post as I’m starting a new project for work and trying to plan things out and what DaveS posted also makes more sense to me then joins or I should say I can understand what Dave posted better then joins.

not sure I quite follow

you want the study table to have a new column, modalities, that is made up of all the modalities on the series associated with the study ?

do you need to store this or not ?

Just be aware of the slight difference in the two queries

Say you have a list of employees & spouses
And you want on big list so you can remember everyones name & their spouses name
Not everyone is married so some will have a spouse entry and some wont (say its in another table just for fun

So what we have is a table of employees and a tables of spouses

Employees
    employee_id
    name

Spouses
    spouses_employee_id
    name

nice & simple

If we have data like

  Employee
      1 Dave S
      2 Norman Palardy

  Spouses  
      1 Maria S (made this one up so ... forgive me)

you can see that I am not married (dont tell my wife)

So how with a single query do you get one list that includes all employees & spouses regardless of married status ?
Daves query wont do it
It would only list those who HAVE spouses
So in this case when you need “a list of X whether they have a related Y or not” outer joins are likely required

LEFT and RIGHT literally refer to the two tables in any join (the first one is “left” and the second “right”

  • inner means return the result if there is a match in the specified columns
  • outer returns the (left or right) either way (match or no)

I think I have all this right but its mostly off the top of my head so … old age memory & all that :slight_smile:

And there are more options (cross which is fun)
This post is decent walking through a pile of options with diagrams & decent explanations & examples

[quote=240810:@Norman Palardy]not sure I quite follow

you want the study table to have a new column, modalities, that is made up of all the modalities on the series associated with the study ?

do you need to store this or not ?[/quote]

I think you got it, and no I don’t need to store it I just need a record set that looks like it came from the study table with the ‘computed’ column.

ok … so to get a “distinct list” of modalities thats literally using the key word “distinct”

the trickiest part is taking a pile of rows and turning it into one big long string (something sql isn’t great at)

lets assume a “study” has a “study id” (or some unique id) and that the series also have that study id on them (otherwise we’re kind of stuck)

getting the distinct list of series as a pile of rows is something like

select DISTINCT series.modality from series, study where series.study_id = study.study_id AND STUDY.STUDY_ID = ....

which lets you get the list (as separate rows) of modalities for a specific study (where the … is you put the study_id)

like I said the hardest part is getting all those rows as one big long string as if they were the single value for a column
SOME db servers actually have extended SQL to do this transform specifically because its quite useful

not knowing what DB you’re using its hard to say what you should or can do

sqlite has group_concat which should work with the right query

which db ARE you using ?

I’m using Oracle. Which seems to have string functions that you can embed in the sql statements.

Thanks Norm :smiley:

Yes… if you want all the items in Table A, regardless of if there is a match in Table B then you DO need to use a LEFT join
but I think what I posted met the conditions of the original requirement.

otherwise it would be

select 
pt.PatientID, 
pt.StudyID
st.StudyDesciption
from Patient pt 
LEFT JOIN Study st 
    ON pt.StudyID=st.StudyID
where st.PatientID = "foo"

or better yet

select 
pt.PatientID, 
pt.StudyID
NVL(st.StudyDesciption,(NO DESCRIPTION') as StudyDescription
from Patient pt 
LEFT JOIN Study st 
    ON pt.StudyID=st.StudyID
where st.PatientID = "foo"

assuming the use of ORACLE Syntax for NVL

for PACS design, table structure should follow hierarchy in which lower level references the parent.

For example:
T_PATIENT
T_STUDY - should store foreign key for patient_pk
T_SERIES - should store foreign key for study_pk
T_INSTANCE - should store foreign key for series_pk

for oracle, you should be able to use listagg function to aggregate the series modality list for study level.

for example (pseudo code):

select a.patient_mrn, b.accession_number, b.study_instance_uid, listagg(c.modality, ' / ' ) within group (order by c.modality) as modalities_in_study

from T_PATIENT a
join T_STUDY b on b.patient_id = a.patient_id
join T_SERIES c on c.study_id = b.study_id
where a.patient_mrn = 'foo';

I forgot the group by:

[code]select a.patient_mrn, b.accession_number, b.study_instance_uid, listagg(c.modality, ’ / ’ ) within group (order by c.modality) as modalities_in_study

from T_PATIENT a
join T_STUDY b on b.patient_id = a.patient_id
join T_SERIES c on c.study_id = b.study_id
where a.patient_mrn = ‘foo’
group by a.patient_mrn, b.accession_number, b.study_instance_uid;[/code]

This PACS doesn’t have a patient table. It is in fact a study root database.
However I have ‘emulated’ a patient table using the study table.

Select distinct Patient_Name, Patient_Sex, Patient_ID etc from Study pa where etc etc …