Given :
InsuranceCompanies (cid, name, phone, address)
Doctors (did, name, specialty, address, phone, age, cid)
Patients (pid, name, address, phone, age, gender, cid)
Visits (vid, did, pid, date, description)
Where:
cid - Insurance Company code
did - doctor code
pid - patient code
vid - code of visit
And a TASK : Find doctors (did, name) with number of visits (during this year) less than average number of visits to all doctors during this year.
My attempt is:
SELECT D.did, D. name
FROM Doctor D,Visit V
WHERE V.did = D.did and D.did = CV.did and CV.visits <
(SELECT AVG ( CV.visits)
FROM (SELECT V1.did AS did,COUNT(V1.vid) AS visits
FROM Visit V1
WHERE V1.date LIKE '%2012'
GROUP BY V1.did) AS CV)
A BIG THANKS TO Bridge Who shared the most beautifull and user freindly SQL commands visualator ever!
Databse Exemple : http://sqlfiddle.com/#!2/e85c7/3
Solution using views:
CREATE VIEW ThisYear AS
SELECT v.pid,v.vid,v.did
FROM Visits v
WHERE v.date LIKE '%2012';
CREATE VIEW DoctorsVisitCount AS
SELECT v.did, COUNT(v.vid) as c
FROM ThisYear v
GROUP BY v.did;
SELECT DISTINCT d.did,d.dname,dvc.c
FROM Doctors d,DoctorsVisitCount dvc
WHERE dvc.c < (SELECT AVG(dvc.c)
FROM DoctorsVisitCount dvc);
Related
My tables:
Musician (id, mname, aname, percentage(fee for the agemt))
Singer (id, gender)
Instrumentalist (id, instrument)
Agent (aname, street, city, zip)
Festival (title, place, sdate, edate)
Event (title, edate, etime)
Booked (id, title (Event.title), edate, etime, salary)
for each festival, find the agent , its overall profit this festival is the highest. Presented the festival's name, agent's name, and its overall profit.
Festival.title="Autumn Festival" and salary<8000,
find the date and time of the event, that taking part in only
instrumentalists, and the number of instrumentalists at this event is the lowest.
1:
select f1.title , aname , sum (salary*percentage/100) as feeAgent
from festival as f1 , booked , musician
where booked.id=musician.id and booked.edate between f1.sdate and f1.edate
group by f1.title , aname
having sum (salary*percentage/100)>any(select sum (salary*percentage/100)
from festival as f1 , booked , musician
where booked.id=musician.id and booked.edate between f1.sdate and f1.edate
group by musician.aname)
2:
select event.edate, event.etime
from event, festival as f1, booked natural join musician natural join instrumentalist
where event.title=f1.title and f1.title Like 'spring%' and booked.salary<8000 and booked.edate between f1.sdate and f1.edate
group by event.edate, event.etime
having count(booked.title)<all(select count(*)
from event,festival as f1,booked natural join musician natural join instrumentalist
where event.title=f1.title and f1.title Like 'spring%' and booked.salary<8000 and booked.edate between f1.sdate and f1.edate
group by booked.title)
The task I have been given requires me to find the average, minimum, maximum and total cost of visits made by Tiddles the cat 'P0001' and vet Trevor McCafferty 'VO4'. This will be drawn from two tables, pet and visit.
Pet table structure:
pet_id, Name, Type, Breed, Gender, Born, owner_id, Notes
Visit table structure:
visit_id, pet_id, vet_id, Visit_Date, Basic_Cost, Symptom, Treatment
Below is the command I have created so far but I'm not sure if I'm doing this correctly which is why I need help.
SELECT Name, Type, AVG(Basic_Cost), MIN(Basic_Cost), MAX(Basic_Cost), SUM(Basic_Cost)
FROM visit, pet
WHERE pet_id = 'P0001' AND vet_id = 'V04';
Any questions just ask and any help is appreciated as I'm stumped.
SELECT FIRST(pet.Name) AS PetName,
FIRST(pet.Type) AS PetType,
AVG(Basic_Cost) AS AverageCost,
MIN(Basic_Cost) AS MinCost,
MAX(Basic_Cost) AS maxCost,
SUM(Basic_Cost) AS TotalCost
FROM visit
INNER JOIN pet ON visit.pet_id = pet.pet_id
WHERE visit.pet_id = 'P0001'
AND visit.vet_id = 'V04'
I think you are missing GROUP BY
SELECT pet.Name, pet.Type, AVG(Basic_Cost), MIN(Basic_Cost), MAX(Basic_Cost), SUM(Basic_Cost)
FROM visit, pet
where visit.pet_id = pet.pet_id
and visit.pet_id = 'P0001'
and visit.vet_id = 'V04'
GROUP BY pet.Name, pet.Type;
Also you probably have to join visit and pet tables
I have the following tables
AdmittedPatients(pid, workerid, admitted, discharged)
Patients(pid, firstname, lastname, admitted, discharged)
DiagnosticHistory(diagnosisID, workerid, pid, timeofdiagnosis)
Diagnosis(diagnosisID, description)
Here is an SQL Fiddle: http://sqlfiddle.com/#!15/e7403
Things to note:
AdmittedPatients is a history of all admissions/discharges of patients at the hospital.
Patients contain all patients who have records at the hospital. Patients also lists who are currently staying at the hospital (i.e. discharged is NULL).
DiagnosticHistory contains all diagnosis made.
Diagnosis has the description of the diagnosis made
Here is my task: list patients who were admitted to the hospital within 30 days of their last discharge date. For each patient list their patient identification number, name, diagnosis, and admitting doctor.
This is what I've cooked up so far:
select pid, firstname, lastname, admittedpatients.workerid, patients.admitted, admittedpatients.discharged
from patients
join admittedpatients using (pid)
group by pid, firstname, lastname, patients.admitted, admittedpatients.workerid, admittedpatients.discharged
having patients.admitted <= admittedpatients.discharged;
This returns pid's from 0, 1, and 4 when it should 0, 1, 2, and 4.
Not sure why out need group by or having here... no aggregate...
SELECT A.pid, firstname, lastname, A.workerid, P.admitted, A.discharged
FROM patients P
INNER JOIN admittedpatients A
on P.pID = A.pID
WHERE date_add(a.discharged, interval 30 day)>=p.admitted
and p.admitted >=a.discharged
updated fiddle: http://sqlfiddle.com/#!2/dc33c/30/0
Didn't get into returning all your needed fields but as this gets the desired result set I imagine it's just a series of joins from here...
Updated to postgresql:
SELECT A.pid, firstname, lastname, A.workerid, P.admitted, A.discharged
FROM patients P
INNER JOIN admittedpatients A
on P.pID = A.pID
WHERE a.discharged+ interval '30 day' >=p.admitted
and p.admitted >=a.discharged
http://sqlfiddle.com/#!15/e7403/1/0
I didn't see any diagnostic info in the fiddle, so I didn't return any.
select pid
,p.lastname,p.firstname
,ad.lastname,ad.firstname
from AdmittedPatients as a
join AdmittedPatients as d using (pid)
join Patients as p using (pid)
join AdminDoctors as ad on ad.workerid=a.workerid
where d.discharged between a.admitted-30 and a.admitted
You have a rather basic WHERE clause error here:
Admitted cannot be both before discharged AND after discharged+30
Also you have an extra semicolon before your whole query is ended, probably throwing out the last line altogether.
I think you're looking for admitted=discharged
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'