SQL select database library - sql

How to print all readers, where time between last two borrows is more than 2 months?
select
name, surname, max(k1.borrow_date)
from
k_person
join
k_reader using(person_id)
join
k_rent_books k1 using(reader_id)
join
k_rent_books k2 using(reader_id)
where
months_between(add_months((k1.borrow_date),-2),k2.borrow_date) > 2
group by
name, surname, person_id
order by
surname;
But i dont know how to say that compare two last dates.
Thanks for help.

Due to some restrictions with the USING clause (e.g. ORA-25154), I had to switch the join syntax, but here's one option. Basically the way to find the last and second last borrow dates for a reader is as follows:
Join to one copy of the K_RENT_BOOKS (K_RB1) table and finds the row with the latest BORROW_DATE for the current reader (from K_READER).
Next, it joins to a second copy of K_RENT_BOOKS (K_RB2), again for
the current reader and finds the latest BORROW_DATE that is not the
one found in the first copy (K_FB1).
Keep the resulting joined record if the last borrow date is two
months after the 2nd last borrow date.
--
select k_p.name, k_rb1.borrow_date, k_rb2.borrow_date
from k_person k_p
inner join
k_reader k_r
on k_p.person_id = k_r.person_id
inner join
k_rent_books k_rb1
on k_rb1.reader_id = k_r.reader_id
inner join
k_rent_books k_rb2
on k_rb2.reader_id = k_r.reader_id
where k_rb1.borrow_date = (select max(borrow_date)
from k_rent_books k_rb3
where k_rb3.reader_id = k_r.reader_id
)
and k_rb2.borrow_date = (select max(borrow_date)
from k_rent_books k_rb4
where k_rb4.reader_id = k_r.reader_id
and k_rb4.borrow_date <> k_rb1.borrow_date
)
and months_between(k_rb1.borrow_date, k_rb2.borrow_date) > 2
There are other ways of doing this that may be faster (e.g. using a with clause that generates the last and second last borrow dates for all readers) but hopefully this provides a starting point.

Related

Join 3 tables complex condition

I have a requirement to join three tables which have the same structure as shown below.
I need to join these tables based on the store and date field values as shown below:
The complex part here is that even if the row in a table does not match with the other tables, it should create a new row with the other values as zero. For example if there is no shipped and allocated quantities for a store on a particular day but if there is a canceled quantity on that day, a new row should be created with shipped $ allocated values set to 0 and cancelled field value set to the correct value. Can anybody suggest the best method to approach this?
this should work
select alldates.procdate,
alldates.store,
st.quantity as ship_quantity,
ct.quantity as cancel_quantity,
at.quantity as allocation_quantity
from (select procdate, store from shp_table union
select procdate, store from cnl_table union
select procdate, store from aloc_table) alldates
left join shp_table st on alldates.procdate = st.procdate and alldates.store = st.store
left join cnl_table ct on alldates.procdate = ct.procdate and alldates.store = ct.store
left join aloc_table at on alldates.procdate = at.procdate and alldates.store = at.store

Show the first two diagnoses for a patient in the first episode, but on the same row

I want to show the first two diagnoses for a patient in the first episode, but on the same row. Sometimes patients do not have two diagnosis, so a null or blank would need to be returned. The diagnosis table fields are Patid, Epno (episode number), DiagNo, diagcode. I want to show the result thus: Patientno1 EpNo 1 Diagno1 DiagCode, Diagno 2 Diagcode.
Example:
Patient 123456 epno 1 DiagNo1 diagcode = W234 DiagNo2 Diagcode = e3456.
Patient 890765 epno 1 diagNo1 diagcode = R345 (No second diagnosis)
I have tried selfjoins, but cannot get past the problem of no second diagnosis for patient 2. Script below (The Spell table defines the date period for me).
SELECT distinct
d1.PatID
,d1.diagno
,d1.DiagCode
,d1.DiagDesc as d1
,d2.diagno
,d2.DiagCode
,d2.DiagDesc AS d2
FROM spell s
left JOIN diagnosis d1 ON s.AdmNo = d1.AdmNo
right JOIN diagnosis d2 ON d1.AdmNo = d2.AdmNo AND d2.EpNo = d1.epno
WHERE s.AdmDate>= '25-may-2018'
AND d1.EpNo = 1 AND d1.DiagNo = 1 AND d2.EpNo = 1
GROUP BY
d1.PatID
,d1.diagno
,d1.DiagCode
,d1.DiagDesc
,d2.diagno
,d2.DiagCode
,d2.DiagDesc
I am using SQLServer management Studio 2007. I have googled this many times! Any assistance, greatly appreciated.
You would seem to want:
SELECT d1.PatID, d1.diagno, d1.DiagCode, d1.DiagDesc as d1,
d2.diagno, d2.DiagCode, d2.DiagDesc AS d2
FROM spell s LEFT JOIN
diagnosis d1
ON s.AdmNo = d1.AdmNo AND d1.EpNo = 1 and d1.diagno = 1 LEFT JOIN
diagnosis d2
ON s.AdmNo = d2.AdmNo AND d2.EpNo = d1.epno AND d2.diagno = 2 and d2.EpNo = 1
WHERE s.AdmDate >= '2018-05-25';
You seem to be storing the multiple diagnoses in diagnosis, using diagno to distinguish them.
Notes:
RIGHT JOIN is almost never useful. Just use LEFT JOIN.
From what you describe, neither SELECT DISTINCT nor GROUP BY is necessary.
I don't think all your necessary join conditions are in your query.
Your where clause undoes outer joins.
Use standard date formats, such as YYYY-MM-DD.

Joining a selected table to a cross joined table

I have a table, flight_schedule, that consists of a bunch of flight segments. Below I have a Terradata SQL query that creates a list of two segment itineraries between Chicago and Denver. i.e. each row has two flights that eventually get the passenger from Chicago to Denver. For example, the first row contains flight information on a leg from Chicago to Omaha and then a later leg from Omaha to Denver. This query works just fine.
SELECT A.flt_num, A.dprt_sta_cd, A.arrv_sta_cd, A.sch_dprt_dtml, A.sch_arrv_dtml,
B.flt_num, B.dprt_sta_cd, B.arrv_sta_cd, B.sch_dprt_dtml, B.sch_arrv_dtml
FROM
flight_schedule A
CROSS JOIN
flight_schedule B
WHERE
A.dprt_sta_cd = 'Chicago' AND
B.arrv_sta_cd = 'Denver' AND
A.arrv_sta_cd = B.dprt_sta_cd AND
A.sch_arrv_dtml < B.sch_dprt_dtml
ORDER BY B.sch_arrv_dtml;
I have another table, flight_seat_inventory, that consists of seats available in different cabins for each flight number. The query below aggregates total available seats for each flight number. This query is also A-OK.
SELECT flt_num, SUM(seat_cnt) as avail_seats
FROM flight_seat_inventory
GROUP BY flt_num;
I want to combine these two queries with a LEFT JOIN, twice, so that each flight has a corresponding avail_seats value. How can I do this?
For added clarity, I think my desired Select statement looks like this:
SELECT A.flt_num, A.dprt_sta_cd, A.arrv_sta_cd, A.sch_dprt_dtml, A.sch_arrv_dtml, C.avail_seats
B.flt_num, B.dprt_sta_cd, B.arrv_sta_cd, B.sch_dprt_dtml, B.sch_arrv_dtml, D.avail_seats
flight_schedule is HUGE, so I suspect it's more efficient to do the LEFT JOIN after the CROSS JOIN. Again, using Teradata SQL.
Thanks!
I needed to declare the second seats query as a temporary table using a WITH command before I did the LEFT JOIN:
WITH tempSeatsTable AS (
SELECT flt_num, SUM(seat_cnt) as avail_seats
FROM flight_seat_inventory
GROUP BY flt_num
)
SELECT
A.flt_num, A.dprt_sta_cd, A.arrv_sta_cd, A.sch_dprt_dtml, A.sch_arrv_dtml, C.avail_seats
B.flt_num, B.dprt_sta_cd, B.arrv_sta_cd, B.sch_dprt_dtml, B.sch_arrv_dtml, D.avail_seats
FROM
flight_schedule A
CROSS JOIN
flight_schedule B
LEFT JOIN
tempSeatsTable C
ON A.flt_num = C.flt_num
LEFT JOIN
tempSeatsTable D
ON B.flt_num = D.flt_num
WHERE
A.dprt_sta_cd = 'Chicago' AND
B.arrv_sta_cd = 'Denver' AND
A.arrv_sta_cd = B.dprt_sta_cd AND
A.sch_arrv_dtml < B.sch_dprt_dtml
ORDER BY B.sch_arrv_dtml;

Oracle SQL, Select and compare dates against null values

I need to select and compare the last advertised date in advert, to any null values in lease to get when an un-leased property and when it was last advertised. This is the code I have so far;
SELECT YR_LEASE.PROPERTYNUM,
MAX(YR_ADVERT.DATETO),
count(YR_LEASE.RENTERNUM)
FROM YR_LEASE
JOIN YR_ADVERT
ON YR_LEASE.PROPERTYNUM=YR_ADVERT.PROPERTYNUM
GROUP BY YR_LEASE.PROPERTYNUM
This returns a count this is far too high and I'm not sure what i'm doing wrong, here's my ERD to try and give this question some context;
http://www.gliffy.com/pubdoc/4239520/L.png
I think you need to first identify unleased properties. From there you can find the latest advert date. Assuming some properties have never been advertised you'll need to go via YR_PROPERTY and do a left join to include unadvertised properties.
SELECT NVL(TO_CHAR(MAX(YR_ADVERT.DATETO),'DD/MM/YYYY'),'NO LAST ADVERT DATE') LAST_ADVERT_DATE
,YR_PROPERTY.PROPERTYNUM
FROM YR_PROPERTY LEFT JOIN YR_ADVERT ON YR_PROPERTY.PROPERTYNUM = YR_ADVERT.PROPERTYNUM
WHERE NOT EXISTS (SELECT 1
FROM YR_LEASE
WHERE YR_LEASE.PROPERTYNUM = YR_PROPERTY.PROPERTYNUM
AND YR_LEASE.RENT_FINISH > SYSDATE)
GROUP BY YR_LEASE.PROPERTYNUM;

outer query to list only if its rowcount equates to inner subquery

Need help on a query using sql server 2005
I am having two tables
code
chargecode
chargeid
orgid
entry
chargeid
itemNo
rate
I need to list all the chargeids in entry table if it contains multiple entries having different chargeids
which got listed in code table having the same charge code.
data :
code
100,1,100
100,2,100
100,3,100
101,11,100
101,12,100
entry
1,x1,1
1,x2,2
2,x3,2
11,x4,1
11,x5,1
using the above data , it query should list chargeids 1 and 2 and not 11.
I got the way to know how many rows in entry satisfies the criteria, but m failing to get the chargeids
select count (distinct chargeId)
from entry where chargeid in (select chargeid from code where chargecode = (SELECT A.chargecode
from code as A join code as B
ON A.chargecode = B.chargeCode and A.chargetype = B.chargetype and A.orgId = B.orgId AND A.CHARGEID = b.CHARGEid
group by A.chargecode,A.orgid
having count(A.chargecode) > 1)
)
First off: I apologise for my completely inaccurate original answer.
The solution to your problem is a self-join. Self-joins are used when you want to select more than one row from the same table. In our case we want to select two charge IDs that have the same charge code:
SELECT DISTINCT c1.chargeid, c2.chargeid FROM code c1
JOIN code c2 ON c1.chargeid != c2.chargeid AND c1.chargecode = c2.chargecode
JOIN entry e1 ON e1.chargeid = c1.chargeid
JOIN entry e2 ON e2.chargeid = c2.chargeid
WHERE c1.chargeid < c2.chargeid
Explanation of this:
First we pick any two charge IDs from 'code'. The DISTINCT avoids duplicates. We make sure they're two different IDs and that they map to the same chargecode.
Then we join on 'entry' (twice) to make sure they both appear in the entry table.
This approach gives (for your example) the pairs (1,2) and (2,1). So we also insist on an ordering; this cuts to result set down to just (1,2), as you described.