Need to select only patients who have not specific codes - sql

I have here an issue in this sql - it will give me the rows that are not these diagnosis codes. But a patient can have them as well.
I need patients who do not have these 3 codes
V72.31', 'Z01.411', 'Z01.419' at all.
SELECT distinct "Vouchers"."Patient_ID"
FROM (("Ntier_70751"."PM"."Service_Diagnoses" "Service_Diagnoses"
INNER JOIN "Ntier_70751"."PM"."Services" "Services"
ON "Service_Diagnoses"."Service_ID"="Services"."Service_ID")
INNER JOIN "Ntier_70751"."PM"."Diagnosis_Codes" "Diagnosis_Codes"
ON "Service_Diagnoses"."Diagnosis_Code_ID"="Diagnosis_Codes"."Diagnosis_Code_ID")
INNER JOIN "Ntier_70751"."PM"."Vouchers" "Vouchers" ON "Services"."Voucher_ID"="Vouchers"."Voucher_ID"
WHERE "Diagnosis_Codes"."Diagnosis_Code" not in ('V72.31', 'Z01.411', 'Z01.419')

Your desired outcome is unclear.
This will show patients who have your "invalid" codes, so long as they have at least one code that is not in your exclusion list:
SELECT DISTINCT
v.Patient_ID
FROM
Ntier_70751.PM.Service_Diagnoses sd
INNER JOIN Ntier_70751.PM.Services s
ON sd.Service_ID = s.Service_ID
INNER JOIN Ntier_70751.PM.Diagnosis_Codes dc
ON sd.Diagnosis_Code_ID = dc.Diagnosis_Code_ID
AND dc.Diagnosis_Code not in ('V72.31', 'Z01.411', 'Z01.419')
INNER JOIN Ntier_70751.PM.Vouchers v
ON s.Voucher_ID= v.Voucher_ID
And this one will exclude patients who have at least one of the "invalid" codes (regardless of what other "valid" codes they may have):
SELECT DISTINCT
v.Patient_ID
FROM
Ntier_70751.PM.Vouchers v
WHERE
v.Patient_ID NOT IN
(
SELECT DISTINCT
v.Patient_ID
FROM
Ntier_70751.PM.Service_Diagnoses sd
INNER JOIN Ntier_70751.PM.Services s
ON sd.Service_ID = s.Service_ID
INNER JOIN Ntier_70751.PM.Diagnosis_Codes dc
ON sd.Diagnosis_Code_ID = dc.Diagnosis_Code_ID
AND dc.Diagnosis_Code in ('V72.31', 'Z01.411', 'Z01.419')
INNER JOIN Ntier_70751.PM.Vouchers v
ON s.Voucher_ID= v.Voucher_ID
)

Why are you attempting to do this umpteen-way join? I'm sure there's a far more efficient way to do things. We need, however, a more precise description of what you're trying to achieve.
The problem is the scope to which the restricting WHERE clause is applied. You are essentially saying
SELECT . . .
FROM (THIS
JOIN THAT
JOIN (THEOTHERONE WHERE . . .))
when what you actually need is:
SELECT . . .
FROM (THIS JOIN THAT JOIN THEOTHERONE)
WHERE . . .
By constructing the multi-way join, you are picking up and tacking on other tuples that may contain those unwanted diagnosis codes. Do your selection ("selection" means WHERE; SELECT is actually "projection" [poor choice of keywords from the creators of SQL) at the last possible moment.
Here's a suggestion. Today, you want to reject records where the diagnosis codes match, say, A, B, or C; tomorrow you may want to ALSO reject records where the diagnosis codes match P or Q. You should GENERALIZE this to make it TABLE-DRIVEN: create a second table
CREATE TABLE REJECTION_CATEGORY( CATEG, DIAGN_CODE )
PRIMARY KEY( CATEG, DIAGN_CODE )
and initialize it with the following query:
INSERT INTO REJECTION_CATEGORY VALUES( 1, 'V72.31 ),
VALUE ( 1, 'Z01.411' ),
VALUES( 1, 'Z01.419' )
then you can change your first query to:
SELECT . . .
FROM (join-of-this-and-that)
WHERE DIAGNOSIS_CODE NOT IN (
SELECT DIAGN_CODE
FROM REJECTION_CATEGORY
WHERE CATEG = 1)
and tomorrow's query, which rejects a COMPLETELY DIFFERENT set of diagnosis codes, just changes the last line but one to read WHERE CATEG = 2.

Related

Minus operation gives wrong answer

I am trying to get the result of the minus operation of join tables which means that I am finding unmatched records.
I tried:
SELECT count(*) FROM mp_v1 mp
left join cm_v1 sop
on mp.study_name=sop.study_name and
sop.site_id=sop.site_id
--where mp.study_name='1101'
MINUS
SELECT count(*) FROM iv_mpv1 mp
inner join cm sop
on mp.study_name=sop.study_name and
sop.site_id=sop.site_id
--where mp.study_name='1101'
output: the count of this gives me 171183251
but when I run the first query individually I get 171183251 for left outer join and 171070345 for inner join so the output needs to be 112906. I am not sure where my query is wrong. Could anyone please give your opinion.
If you want unmatched records you wouldn't use MINUS on the counts. The query would look more like:
SELECT COUNT(*)
FROM ((SELECT *
FROM mp_v1 mp LEFT JOIN
cm_v1 sop
USING (study_name, site_id)
) MINUS
(SELECT *
FROM iv_mpv1 mp LEFT JOIN
iv_cmv1 sop
USING (study_name, site_id)
)
) x;
Also note that MINUS removes duplicates, so if you have duplicates within each set of tables, then they only count as one row.
The SELECT * assumes that the tables have the same columns and compatible types -- which makes sense given the gist of the question. You may need to list the particular columns you care about.

LEFT JOIN help in sql

I have to make a list of customer who do not have any invoice but have paid an invoice … maybe twice.
But with my code (stated below) it contains everything from the left join. However I only need the lines highlighted with green.
How should I make a table with only the 2 highlights?
Select paymentsfrombank.invoicenumber,paymentsfrombank.customer,paymentsfrombank.value
FROM paymentsfrombank
LEFT OUTER JOIN debtors
ON debtors.value = paymentsfrombank.value
You only want to select columns from paymentsfrombank. So why do you even join?
select invoice_number, customer, value from paymentsfrombank
except
select invoice_number, customer, value from debtors;
(This requires exact matches as in your example, i.e. same amount for the invoice/customer).
There are two issues in your SQL. First, you need to join on Invoice number, not on value, as joining on value is pointless. Second, you need to only pick those payments where there are no corresponding debts, i.e. when you left-join, the table on the right has "null" in the joining column. The SQL would be something like this:
SELECT paymentsfrombank.invoicenumber,paymentsfrombank.customer,paymentsfrombank.value
FROM paymentsfrombank
LEFT OUTER JOIN debtors
ON debtors.InvoiceNumber = paymentsfrombank.InvoiceNumber
WHERE debtors.InvoiceNumber is NULL
in mysql we usually have this way to flip the relation and extract the rows that dosen't have relation.
Select paymentsfrombank.invoicenumber,paymentsfrombank.customer,paymentsfrombank.value
FROM paymentsfrombank
LEFT OUTER JOIN debtors
ON debtors.value = paymentsfrombank.value where debtors.value is null
You can use NOT EXISTS :
SELECT p.*
FROM paymentsfrombank p
WHERE NOT EXISTS (SELECT 1 FROM debtors d WHERE d.invoice_number = p.invoice_number);
However, the LEFT OUTER JOIN would also work if you add filtered with WHERE Clause to filtered out only missing customers that haven't any invoice information :
SELECT p.invoicenumber, p.customer, p.value
FROM paymentsfrombank P LEFT OUTER JOIN
debtors d
ON d.InvoiceNumber = p.InvoiceNumber
WHERE d.InvoiceNumber IS NULL;
Note : I have used table alias (p & d) that makes query to easier read & write.

SQL query returning more than one result

This is my SQL query
SELECT
room.*, reservation.rn, reservation.cin
FROM
room, reservation
But it is returning 4 instances of each row.
I just want to get the corresponding reservation from another table where they have same room number and at the same time display the remaining rooms
Room Table
Id,r_num,r_price,in_date,out_date
reservation_table
id,r_num,cIn,cOut
You have a cross join between the two tables as there is no join condition.
Assuming the reservation table has room_id FK which references id from room table, you can join like this:
select r.*,
s.rn,
s.cin
from room r
join reservation s on r.id = s.room_id
Always use proper explicit join syntax instead of comma based joins.
Your select statement results in a cross join / cartesian product.
Use a WHERE-clause!
SELECT room.*,reservation.rn,reservation.cin FROM room,reservation WHERE room.room_no = reservation.room_no
For more complicated joins I recommend using an explicit syntax with the appropriate keywords, although your implicit join is perfectly fine for this case (and performance wise implicit and explicit joins are the same).
To display unreserved rooms as well (so to keep results that do not satisfy the where-clause) you'll have to use an OUTER JOIN (LEFT or RIGHT depending on what you want to keep) like this:
SELECT room.*,reservation.rn,reservation.cin
FROM room LEFT OUTER JOIN reservation
ON room.room_no = reservation.room_no
For the first part you need a join and for second condition you need union :
select r.Id,r.r_num,r.r_price,r.in_date,r.out_date,s.id as resId,s.r_num,s.cIn,s.cOut
from room r
join reservation s on r. r_num = s. r_num
union
select r.Id,r.r_num,r.r_price,r.in_date,r.out_date,null as resId, null as r_num, null as cIn,null as cOut
from room r
where r.in_date is null
union by default distinct the result if you need repeated rows just use UNION ALL

Join Fields if another field equals something specific

I am trying to join several fields in one table to a field in another table. They will be differentiated by the values in another field. I am unsure how this join should work and would appreciate advice. Here's examples of the tables
Bldg|EMeter|GMeter|HMeter|CMeter
Bld1_1______1______1______1_____
Bld2_2______3______328____2_____
Bld2_NULL___4______NULL___NULL
Utility|Meter|TotCost|
E_______5_____20______
C_______5_____80______
H_______5_____123______
G_______5_____654______
E_______6_____999______
E_______7_____205______
G_______6_____200______
G_______7_____40______
These tables obviously just representative of what I'm dealing with. But I'm trying to write a query to match all the meter columns from the first table to the meter column in the second table, but only if the utility value matches the first letter in the meter (EMeter goes with E, GMeter goes with G, etc.) Also the building's can have one more than one record, because they can have more than one meter of a type. I'm trying to get a cost for each building, for each type of meter, so I add all the separate meters together for each building (ie. How bldg2 has two meters for G, those will each have total costs that need to be added together. I'm trying to run a group by buildings in my query to total all those together). I'm trying to use a select query to join the meters in the first one with the meters in the second one where the utilities match, but I can't really get the syntax or design of how to do that at all. Eventually I want to have a select by query displaying something like this
Bldg|ECost|GCost|HCost|CCost
Bld1_2000__39483_20____3829_
Bld2_2034__385___2839__3010_
Since the names of columns in the first table are fixed, you can build a query that uses an outer join, and then group by with SUM(), like this:
SELECT
m.Bldg
, SUM(e.TotCost) AS ECost
, SUM(g.TotCost) AS GCost
, SUM(h.TotCost) AS HCost
, SUM(c.TotCost) AS CCost
FROM tblBldgMeters m
LEFT OUTER JOIN qryMtrHistory e ON e.Utility='E' AND m.EMeter=e.Meter
LEFT OUTER JOIN qryMtrHistory g ON g.Utility='G' AND m.GMeter=g.Meter
LEFT OUTER JOIN qryMtrHistory h ON h.Utility='H' AND m.HMeter=h.Meter
LEFT OUTER JOIN qryMtrHistory c ON c.Utility='C' AND m.CMeter=c.Meter
GROUP BY m.Bldg
It is saying JOIN expression not supported
This is a problem with MS-Access SQL engine. You can rewrite this into a different query:
SELECT
m.Bldg
, SUM(e.TotCost * SWITCH(e.Utility='E', 1, true, 0)) AS ECost
, SUM(g.TotCost * SWITCH(g.Utility='G', 1, true, 0)) AS GCost
, SUM(h.TotCost * SWITCH(h.Utility='H', 1, true, 0)) AS HCost
, SUM(c.TotCost * SWITCH(c.Utility='C', 1, true, 0)) AS CCost
FROM tblBldgMeters m
LEFT OUTER JOIN qryMtrHistory e ON AND m.EMeter=e.Meter
LEFT OUTER JOIN qryMtrHistory g ON AND m.GMeter=g.Meter
LEFT OUTER JOIN qryMtrHistory h ON AND m.HMeter=h.Meter
LEFT OUTER JOIN qryMtrHistory c ON AND m.CMeter=c.Meter
GROUP BY m.Bldg
This moves the condition from the JOIN into a SWITCH.

Getting all records including non matching records

I have the following tables
Payment
PayTypeId, Description
0 , Credit
1, Debit
2,Master
ActualPayment
Id,PayTypeId,Amount
1,1,10
Here is the output i am looking at
Id,PayTypeId,Amount
1,0,NULL
1,1,10
1,2,NULL
Basically I want all the records of ActualPayment including all payment types.
Here is the query i have used but am not getting any records
select
*
from #ActualPayments ap
left join #Payment p on ap.paytypeid = p.paytypeid
where p.paytypeid is null
If you want one record for each of the three PayTypeID values, then you need those three records on the left-hand side of the LEFT JOIN.
Equally, if you want the ActuallPaymentID on each output line, that value needs to be on the left hand side.
It's all leading you down the wrong avenue with the data that you have, and the tables that you have described.
With just those two tables in your question, I would use this layout instead...
SELECT
ap.ActualPaymentID,
p.PayTypeID,
SUM(CASE WHEN ap.PayTypeID = p.PayTypeID THEN ap.Amount END) AS Amount
FROM
ActualPayments AS ap
CROSS JOIN
Payment AS p
GROUP BY
ap.ActualPaymentID,
p.PayTypeID
You aren't receiving any records because you are filtering everything out with the WHERE clause p.paytypeid is null
Try running it without the WHERE clause.
Edit: The below SQL should return the correct information. I've used a CROSS JOIN to create an in-line view. This should remove the unwanted NULLs.
SELECT t1.id, t1.paytypeid, t2.amount
FROM (
SELECT id, payment.paytypeid
FROM #ActualPayments
CROSS JOIN #Payment
) t1
LEFT OUTER JOIN #ActualPayments t2
ON t1.paytypeid = t2.paytypeid
;
I think you want a FULL OUTER JOIN:
select
*
from #ActualPayments ap
full outer join #Payment p
on ap.paytypeid = p.paytypeid
;
This will return all rows from the ActualPayments table along with their corresponding values from Payment - if there is any. Additional it will return all rows from Payment for which no ActualPayments exist.
Please note: The where clause of your sample query must not be used!
I'm confused why you would want to do this, but here's one way
select ap.Id, pt.PayTypeId, ap2.Amount
from #ActualPayments ap
cross join #PaymentTypes pt
left join #ActualPayments ap2
on pt.PayTypeId = ap2.PayTypeId
and ap.Id = ap2.id