Having these tables into a db:
Athlete with fields: athlete_id, name, surname, date_of_birth, height, weight, bio, photo_id
AthletePhoto with fields: photo_id, photo, mime_type
AthleteResult with fields: athlete_id, gold, silver, bronze
Game with fields: game_id, city, year
The db model:
The code so far can only send data for one of the tables:
db.serialize(function () {
db.all(
'SELECT athlete_id, name, surname FROM Athlete',
function (err, rows) {
return res.send(rows);
}
);
});
so it uses that query: SELECT athlete_id, name, surname FROM Athlete.
Is there a way to combine the tables and send all data?
I've tried to combine 2 tables, Athlete and AthletePhoto but didn't send any data:
SELECT athlete_id, name FROM Athlete UNION SELECT game_id, city, year FROM Game UNION SELECT photo_id as athlete_id, mime_type as name FROM AthletePhoto
Assuming that your database structure correctly represents your application needs, the query which you are trying to make will look something like this:
SELECT
a.athlete_id, a.name, a.surname, a.date_of_birth, a.bio, a.height, a.weight,
ap.photo, ap.mime_type,
ar.gold, ar.silver, ar.bronze,
g.city, g.year
FROM
(
(
(Athlete a JOIN AthletePhoto ap ON a.photo_id = ap.photo_id)
JOIN
AthleteResults ar ON a.athlete_id = ar.athlete_id
)
JOIN
Game g ON ar.game_id = g.game_id
)
There is one mistake in Athlete table, that date_of_birth column is defined twice. You should rename anyone of them. There is no need to use UNION in your query if you want to combine results of different tables, use JOIN instead.
JOIN Combines different tables row-wise
UNION Combines different tables column-wise
Related
For example, I have a simple table books:
author
book
Author-A
Book-A1
Author-A
Book-A2
Author-B
Book-B1
Author-C
Book-C1
Author-C
Book-C2
And I need to count books by each author, so I'll write:
select author, count(*) from books
group by author
# Author-A = 2
# Author-B = 1
# Author-C = 2
But now I need to count books by groups of authors:
groupA = ['Author-A', 'Author-C'],
groupB = ['Author-B']
select authorGroup, count(*) from books
group by {
case author in groupA -> 'groupA'
case author in groupB -> 'groupB'
} as authorGroup
# ['Author-A', 'Author-C'] = 4
# ['Author-B'] = 1
These groups can be different and come from another module.
What's the best way to write this requests? Maybe without union such as:
select author as 'groupA', count(*) from books
where author in { groupA }
union
select author as 'groupB', count(*) from books
where author in { groupB }
because there could be a lot of groups in request (~20-30)
The problem is that these groups can be absolutely dynamic: I can request ['Author-A', 'Author-B'] in one request as one group and ['Author-B', 'Author-C'] in another.
For example, the group is not something like author's country or genre. It can be totally dynamic.
The usual way is to JOIN on to a mapping table, which can be an in-line-view if need be (though I recommend an actual table, which can be indexed).
WITH
author_group AS
(
SELECT 'Author-A' AS author, 'Group-A' AS group_label
UNION ALL
SELECT 'Author-B' AS author, 'Group-B' AS group_label
UNION ALL
SELECT 'Author-C' AS author, 'Group-A' AS group_label
)
SELECT
author_group.group_label,
COUNT(*)
FROM
books
INNER JOIN
author_group
ON author_group.author = books.author
GROUP BY
author_group.group_label
Similar results can be achieved with CASE expressions, but it doesn't scale very well...
WITH
mapped_author AS
(
SELECT
*,
CASE author
WHEN 'Author-A' THEN 'Group-A'
WHEN 'Author-B' THEN 'Group-B'
WHEN 'Author-C' THEN 'Group-A'
END
AS author_group
FROM
books
)
SELECT
author_group,
COUNT(*)
FROM
mapped_author
GROUP BY
author_group
First you need to create a new table that show in what group is the author.
Later you just count
Like this:
select distinct a.group_auth, count(a.book) over (partition by a.group_auth)
from
(select
case when b.Author in [groupA] then 'groupA',
when b.Author in [groupB] then 'groupB'
end case as group_auth,
b.book as book
from books b
) as a
;
Assume we have loaded a flat file with patient diagnosis data into a table called “Data”. The table structure is:
Create table Data (
Firstname varchar(50),
Lastname varchar(50),
Date_of_birth datetime,
Medical_record_number varchar(20),
Diagnosis_date datetime,
Diagnosis_code varchar(20))
The data in the flat file looks like this:
'jane','jones','2/2/2001','MRN-11111','3/3/2009','diabetes'
'jane','jones','2/2/2001','MRN-11111','1/3/2009','asthma'
'jane','jones','5/5/1975','MRN-88888','2/17/2009','flu'
'tom','smith','4/12/2002','MRN-22222','3/3/2009','diabetes'
'tom','smith','4/12/2002','MRN-33333','1/3/2009','asthma'
'tom','smith','4/12/2002','MRN-33333','2/7/2009','asthma'
'jack','thomas','8/10/1991','MRN-44444','3/7/2009','asthma'
You can assume that no two patients have the same firstname, lastname, and date of birth combination. However one patient might have several visits on different days. These should all have the same medical record number.
The problem is this: Tom Smith has 2 different medical record numbers. Write a query that would always show all the patients
who are like Tom Smith – patients with more than one medical record number.
I came up with below query. It works perfectly fine, but wanted to know if there is a better way to write this query using Oracle Analytical function's. Thank you in advance
SELECT a.firstname,
a.lastname,
a.date_of_birth,
a.medical_record_number
FROM data a, data b
WHERE a.firstname = b.firstname
AND a.lastname = b.lastname
AND a.date_of_birth = b.date_of_birth
AND a.medical_record_number <> .medical_record_number
GROUP BY a.firstname,
a.lastname,
a.date_of_birth,
a.medical_record_number
It is possible to do via analytic functions, but whether it's faster than doing the join in your query* or not depends on what data you have. You'd need to test.
with data (firstname, lastname, date_of_birth, medical_record_number, diagnosis_date, diagnosis_code)
as (select 'jane','jones','2/2/2001','MRN-11111',to_date('3/3/2009', 'mm/dd/yyyy'),'diabetes' from dual union all
select 'jane','jones','2/2/2001','MRN-11111',to_date('1/3/2009', 'mm/dd/yyyy'),'asthma' from dual union all
select 'jane','jones','5/5/1975','MRN-88888',to_date('2/17/2009', 'mm/dd/yyyy'),'flu' from dual union all
select 'tom','smith','4/12/2002','MRN-22222',to_date('3/3/2009', 'mm/dd/yyyy'),'diabetes' from dual union all
select 'tom','smith','4/12/2002','MRN-33333',to_date('1/3/2009', 'mm/dd/yyyy'),'asthma' from dual union all
select 'tom','smith','4/12/2002','MRN-33333',to_date('2/7/2009', 'mm/dd/yyyy'),'asthma' from dual union all
select 'jack','thomas','8/10/1991','MRN-44444',to_date('3/7/2009', 'mm/dd/yyyy'),'asthma' from dual),
-- end of mimicking your table and its data
res as (select firstname,
lastname,
date_of_birth,
medical_record_number,
count(distinct medical_record_number) over (partition by firstname, lastname, date_of_birth) cnt_med_rec_nums
from data)
select distinct firstname,
lastname,
date_of_birth,
medical_record_number
from res
where cnt_med_rec_nums > 1;
*btw, the group by in your example query is not necessary; it would make much more sense to switch it out for a distinct - it makes your intent much clearer, since you're wanting to get a distinct set of records.
You can probably simplify the query a bit using a HAVING clause rather than doing a self-join
SELECT a.firstname,
a.lastname,
a.date_of_birth,
MIN(a.medical_record_number) lowest_medical_record_number,
MAX(a.medical_record_number) highest_medical_record_number
FROM data a
GROUP BY a.firstname,
a.lastname,
a.date_of_birth
HAVING COUNT( DISTINCT a.medical_record_number ) > 1
I'm returning the smallest and largest medical record number for each patient here (that's what I'd do if most of the patients with this problem have just two numbers rather than having dozens). You could return just one or you could return a comma-separated list of all the medical record numbers if you'd rather (which would probably make more sense if most of the bad folks have dozens of numbers).
Let's say I have a database of Amazon customers who made purchases in the last year. It is pretty detailed and has columns like name, age, zip code, income level, favorite color, food, music, etc. Now, let's say I run a query such that I return all Amazon customers who bought Book X.
SELECT NAME, AGE, ZIPCODE, INCOME, FAVECOLOR, FAVEFOOD, FAVEMUSIC
FROM [Amazon].[dbo].[Customers]
WHERE BOOK = "X"
This query will return a bunch of customers who bought Book X. Now, I want to iterate through each of those results (iterate through each customer) and create a query based on each customer's individual age, zipcode, and income.
So if the first result is Bob, age 32, lives in zipcode 90210, makes $45,000 annually, create a query to find all others like Bob who share the same age, zipcode, and income. If the second result is Mary, age 41, lives in zipcode 10004, makes $55,000 annually, create a query to find all others like Mary who share the same age, zipcode, and income.
How do I iterate through customers who bought Book X and run multiple queries whose values (age, zipcode, income) are changing? In terms of viewing the results, it'd be great if I could see Bob, followed by all customers who are like Bob, then Mary, and all customers who are like Mary.
Is this even possible in SQL? I know how to do this in C# (for/next loops with if/then statements inside) but am new to SQL, and the data is in SQL.
I use SQL Server 2008.
If i understood your requirement correctly then a nested quesry should do the job. SOmething like this:
SELECT distinct NAME, AGE, ZIPCODE, INCOME, FAVECOLOR, FAVEFOOD, FAVEMUSIC
FROM [Amazon].[dbo].[Customers] a, (SELECT NAME, AGE, ZIPCODE, INCOME, FAVECOLOR, FAVEFOOD, FAVEMUSIC
FROM [Amazon].[dbo].[Customers]
WHERE BOOK = "X" and name = 'Bob') b
WHERE BOOK = "X" and a.age=b.age and a.zipcode= b.zipcode and a.income=b.income
EDIT: A generic query will be [This will have list of all users]:
SELECT distinct NAME, AGE, ZIPCODE, INCOME, FAVECOLOR, FAVEFOOD, FAVEMUSIC
FROM [Amazon].[dbo].[Customers] a, (SELECT distinct NAME, AGE, ZIPCODE, INCOME, FAVECOLOR, FAVEFOOD, FAVEMUSIC
FROM [Amazon].[dbo].[Customers]
WHERE BOOK = "X" ) b
WHERE a.BOOK = b.book and a.age=b.age and a.zipcode= b.zipcode and a.income=b.income
order by name
Something like this can do it in one query:
;WITH cteSource as
(
SELECT NAME, AGE, ZIPCODE, INCOME, FAVECOLOR, FAVEFOOD, FAVEMUSIC
FROM [Amazon].[dbo].[Customers]
WHERE BOOK = "X"
)
SELECT sr.NAME AS SrcName, cu.NAME AS LikeName
FROM [Amazon].[dbo].[Customers] AS cu
JOIN cteSource As sr
ON cu.AGE = sr.AGE
And cu.ZIPCODE = sr.ZIPCODE
And cu.INCOME = sr.INCOME
Something like this will let you chase related customers to an arbitrary, e.g. 5 here, degree of separation. By constructing the JOINs correctly you can do things like match income within a range, ... .
with Book as (
select Id, Name, Age, ZIPCode, Income -- ...
from Amazon.dbo.Customers
where Book = 'X' ),
RelatedCustomers as (
select C.Id, C.Name, C.Age, C.ZIPCode, C.Income, 1 as Depth -- ...
from Amazon.dbo.Customers as C inner join
Book as B on B.Id <> C.Id and Abs( B.Income - C.Income ) < 2000 -- and ...
union all
select C.Id, C.Name, C.Age, C.ZIPCode, C.Income, RC.Depth + 1-- ...
from Amazon.dbo.Customers as C inner join
RelatedCustomers as RC on RC.Id <> C.Id and Abs( RC.Income - C.Income ) < 2000 -- and ...
where Depth < 5 )
select *
from RelatedCustomers
I think you need two separate queries. First one to bring back the customers, once a customer such as Bob is selected a second query is performed based on Bob's attributes.
A simple example would be a forms application that has two grids. The first displays a list of the users. When you select one of the users the second grid is populated with the results of the second query.
The second query would be something like:
SELECT NAME, AGE, ZIPCODE, INCOME, FAVECOLOR, FAVEFOOD, FAVEMUSIC
FROM [Amazon].[dbo].[Customers]
WHERE Age = #BobsAge AND ZipCode = #BobsZipCode AND Income = #BobsIncome
It sounds like you want a simple self-join:
SELECT
MatchingCustomers.NAME,
MatchingCustomers.AGE,
MatchingCustomers.ZIPCODE,
MatchingCustomers.INCOME,
MatchingCustomers.FAVECOLOR,
MatchingCustomers.FAVEFOOD,
MatchingCustomers.FAVEMUSIC
FROM
[Amazon].[dbo].[Customers] SourceCustomer
LEFT JOIN [Amazon].[dbo].[Customers] MatchingCustomers
ON SourceCustomer.Age = MatchingCustomer.Age
AND SourceCustomer.ZipCode = MatchingCustomer.ZipCode
AND SourceCustomer.Income = MatchingCustomer.Income
WHERE
SourceCustomer.Book = 'X'
If you want to see the all source customers and all of their matches in a single result set you can remove the where clause and select data SourceCustomer also:
SELECT
SourceCustomer.Name SourceName,
SourceCustomer.Age SourceAge
SourceCustomer.ZipCode SourceZipCode,
SourceCustomer.Income SourceIncome,
MatchingCustomers.NAME,
MatchingCustomers.AGE,
MatchingCustomers.ZIPCODE,
MatchingCustomers.INCOME,
MatchingCustomers.FAVECOLOR,
MatchingCustomers.FAVEFOOD,
MatchingCustomers.FAVEMUSIC
FROM
[Amazon].[dbo].[Customers] SourceCustomer
LEFT JOIN [Amazon].[dbo].[Customers] MatchingCustomers
ON SourceCustomer.Age = MatchingCustomer.Age
AND SourceCustomer.ZipCode = MatchingCustomer.ZipCode
AND SourceCustomer.Income = MatchingCustomer.Income
WHERE
SourceCustomer.Book = 'X'
The database I'm accessing has two tables I need to query using DB2 SQL, shown here as nametable and addresstable. The query is for finding all of the people with a certain balance due. The addresses are stored in a separate table to keep track of address changes. In addresstable, the latest address is determined by a sequence number (ADDRSEQUENCE). The AddressID field is present in both tables, and is what ties each person to specific addresses. The highest sequence number is the current address. I need that current address for each person and only that one. I know I'm going to have to use MAX somewhere for the sequence number, but I can't figure out how to position it given the join. Here's my current query, which of course returns all addresses...
SELECT NAMETABLE.ACCTNUM AS ACCOUNTNUMBER,
NAMETABLE.NMELASTBUS AS LASTNAME,
NAMETABLE.NAME_FIRST AS FIRSTNAME,
NAMETABLE.BALDUE AS BALANCEDUE,
ADDRESSTABLE.STREETNAME AS ADDR,
ADDRESSTABLE.ADDRLINE2 AS
ADDRLINE2,ADDRESSTABLE.CITYPARISH AS CITY,
ADDRESSTABLE.ADDRSTATE AS STATE,
ADDRESSTABLE.ZIPCODE AS ZIP,
ADDRESSTABLE.ADDIDSEQNO AS ADDRSEQUENCE
FROM NAMETABLE JOIN ADDRESSTABLE ON NAMETABLE.ADDRESSID = ADDRESSTABLE.ADDRESSID
WHERE NAMETABLE.BALANCEDUE >= '50.00'
You can do a sub-select on the MAX(ADDRSEQUENCE) like so:
SELECT
N.ACCTNUM AS ACCOUNTNUMBER
,N.NMELASTBUS AS LASTNAME
,N.NAME_FIRST AS FIRSTNAME
,N.BALDUE AS BALANCEDUE
,A.STREETNAME AS ADDR,
,A.ADDRLINE2 AS
,A.ADDRLINE2
,A.CITYPARISH AS CITY,
,A.ADDRSTATE AS STATE,
,A.ZIPCODE AS ZIP,
FROM NAMETABLE AS N
JOIN ADDRESSTABLE AS A
ON N.ADDRESSID = A.ADDRESSID
WHERE N.BALANCEDUE >= '50.00'
AND A.ADDRSEQUENCE = (
SELECT MAX(ADDRSEQUENCE)
FROM ADDRESSTABLE AS A2
WHERE A.ADDRESSID = A2.ADDRESSID
)
This is pretty quick in DB2.
You can use a row_number and partition by to do this. Something like this:
with orderedaddress as (
select row_number() over (partition by ADDRESSID order by ADDRSEQUENCE desc) as rown,
STREETNAME,ADDRESSID, ... from ADDRESSTABLE
)
select NAMETABLE.ACCTNUM AS ACCOUNTNUMBER,
...
oa.STREETNAME
...
from NAMETABLE JOIN orderedaddress oa on NAMETABLE.ADDRESSID = oa.ADDRESSID
where oa.rown = 1
and NAMETABLE.BALANCEDUE >= '50.00'
I have 2 tables, Student and Supervisor:
STUDENT(supervisorid(pk),name,email....)
SUPERVISOR(supervisorid(pk),name,email....)
Now I need to print supervisor name, email and the # of students under the supervisor (they will have same supervisor id). Something like:
select supervisorname,
supervisoremail,
tot_stud as (select count(*)
Phd_Student s
where s.supervisor_id = r.supervisor_id)
from Phd_Supervisor r
Can you please tell me the SQL query for this.
You will want to use the group by clause for this query. You can specify all of the fields that you want to display, as well as the count(*), join the tables, relate the tables , and then put in your group by clause, listing all of the display fields,(without the count(*)), as those are the fields you are grouping the students by to get their count.
select supervisorname,
supervisoremail,
(select count(*)
from Phd_Student s
where s.supervisor_id = r.supervisor_id) as tot_stud
from Phd_Supervisor r