Oracle Join across tables to count 'outstanding invoices' - sql

I'm trying to find "the number of outstanding invoices for a given contract rate".
SQL Fiddle here (updated with ujjwal's answer to add a distinct)
You'll notice I have 350 outstanding invoices which is a lot more than the 14 I expected so I guess I haven't restricted the INNER JOIN's correctly...
Any ideas would be great thanks (sorry I'm rusty on SQL), this is for the latest version of Oracle.
Wayne

Can you try selecting only distinct entries:
SELECT
r.id AS rate_id,
COUNT(distinct mi.id) AS outstanding_invoices **change here**
FROM
contract_rate r
INNER JOIN
contract c ON r.contract_id = c.id
INNER JOIN
contractor con ON con.id = c.contractor_id
INNER JOIN
maintenance_item item ON item.contract_rate_id = r.id
INNER JOIN
maintenance_invoice mi ON mi.contractor_id = c.contractor_id AND mi.status = 'Awaiting Approval'
GROUP BY
r.id

Related

SQL question on correlated sub-queries using windowing rank functions

I have this SQL homework assignment that tells me to list all the customers and the most recent DVD which they have rented, including the title, Genre, Rating, DVD and date of rental. This can be solved via a correlated sub-query or a window rank function
Here is a screenshot of the schema:
Here is what I have tried so far:
Select
Concat(m.MemberFirstName, ' ', m.MemberLastName) as Member
, d.DvdTitle
, g.GenreName
, rt.RatingName
, r.RentalRequestDate
From
Member m
Inner Join
RentalQueue rq on m.MemberId = rq.MemberId
Inner Join
DVD d on d.DVDId = rq.DVDId
Inner Join
Genre g on g.GenreId = d.GenreId
Inner Join
Rating rt on rt.RatingId = d.RatingId
Inner Join
Rental r on r.DVDId = d.DVDId
I am not sure how I can use correlated subqueries to answer the question above as I am quite new to correlated subqueries and I would appreciate some help on this. Thanks in advance.
Try something like this, but please try to understand it too.
Select
Concat(m.MemberFirstName, ' ', m.MemberLastName) as Member
, mostrecent.*
From
Member m
CROSS APPLY
(select top (1)
d.DvdTitle
, g.GenreName
, rt.RatingName
, r.RentalRequestDate
FROM
RentalQueue rq
Inner Join
DVD d on d.DVDId = rq.DVDId
Inner Join
Genre g on g.GenreId = d.GenreId
Inner Join
Rating rt on rt.RatingId = d.RatingId
Inner Join
Rental r on r.DVDId = d.DVDId
where m.MemberId = rq.MemberId
order by r.RentalRequestDate desc
) mostrecent

Oracle SQL GROUP BY clause containing joins

I'm having issues writing this query correctly. Below is the goal, my current query, and attached are the scripts to build and populate the database. Thanks for any assistance!
For each DVD in the catalog, display it’s title, length, release_date, and how many times it has been checked out by all customers across all libraries. Include those that have not been checked out yet (display as 0). Sort results by title.
SELECT C.TITLE, D.LENGTH, C.RELEASE_DATE, COUNT(T.TRANSACTION_ID)
FROM catalog_item C
INNER JOIN dvd D ON D.CATALOG_ITEM_ID = C.CATALOG_ITEM_ID
INNER JOIN physical_item P ON P.CATALOG_ITEM_ID = C.CATALOG_ITEM_ID
LEFT OUTER JOIN transaction T ON T.PHYSICAL_ITEM_ID = P.PHYSICAL_ITEM_ID
GROUP BY C.TITLE;
Run first: https://drive.google.com/open?id=1PYAZV4KIfZtxP4eQn35zsczySsxDM7ls
Run second: https://drive.google.com/open?id=1pAzWmJqvD3o3n6YJqVUM6TtxDafKGd3f
EDIT
I've gotten the query working but haven't figured out how to get DVDs with zero checkouts to show up. Below is my updated query.
SELECT C.TITLE, D.LENGTH, C.RELEASE_DATE, COUNT(T.TRANSACTION_ID)
FROM catalog_item C
INNER JOIN dvd D ON D.CATALOG_ITEM_ID = C.CATALOG_ITEM_ID
INNER JOIN physical_item P ON P.CATALOG_ITEM_ID = C.CATALOG_ITEM_ID
LEFT OUTER JOIN transaction T ON T.PHYSICAL_ITEM_ID = P.PHYSICAL_ITEM_ID
GROUP BY C.TITLE, D.LENGTH, C.RELEASE_DATE;
SOLVED
Figured out the issue with GMB's help. Final query below!
SELECT C.TITLE, D.LENGTH, C.RELEASE_DATE, NVL(COUNT(T.TRANSACTION_ID), 0) AS NUMBER_OF_CHECKOUTS
FROM catalog_item C
INNER JOIN dvd D ON D.CATALOG_ITEM_ID = C.CATALOG_ITEM_ID
LEFT JOIN physical_item P ON P.CATALOG_ITEM_ID = C.CATALOG_ITEM_ID
LEFT JOIN transaction T ON T.PHYSICAL_ITEM_ID = P.PHYSICAL_ITEM_ID
GROUP BY C.TITLE, D.LENGTH, C.RELEASE_DATE
ORDER BY C.TITLE;
Your second query sure looks better than the first one, as it has the correct GROUP BY clause.
It is hard to provide a 100% sure response without seeing the full tables structures, however if you are still missing records with 0 checkouts in the output, it means that one of your INNER JOINs is not matching. In other words, you have DVDs in your catalog that are either not present in the dvd table, or not present in the physical_item table. As both tables look like referential table, this could indicate a discrepency in your data. I would recommend to change all INNER JOINs to LEFT JOINs to work around this issue.
Also please note that if no checkout happened for a DVD, expression COUNT(T.TRANSACTION_ID) will yield NULL : hence you want to wrap it in a NVL function to handle this case.
New query :
SELECT C.TITLE, D.LENGTH, C.RELEASE_DATE, NVL(COUNT(T.TRANSACTION_ID), 0)
FROM catalog_item C
LEFT JOIN dvd D ON D.CATALOG_ITEM_ID = C.CATALOG_ITEM_ID
LEFT JOIN physical_item P ON P.CATALOG_ITEM_ID = C.CATALOG_ITEM_ID
LEFT JOIN transaction T ON T.PHYSICAL_ITEM_ID = P.PHYSICAL_ITEM_ID
GROUP BY C.TITLE, D.LENGTH, C.RELEASE_DATE;

SQL Server query with multiple INNER JOINs

I need to execute a query that gets everything from the table Incidents based on a CustomerID. The CustomerID comes from the MasterAccounts table, which is not directly related to the Incidents table. There is a SubAccounts table that could be INNER JOIN-ed to relate the two.
Here is what I have so far:
SELECT
*
FROM
MasterAccounts AS ma
INNER JOIN
SubAccounts AS sa
INNER JOIN
Incidents AS i
WHERE
ma.CustomerID = sa.CustomerID;
AND sa.AccountID = i.AccountID
AND IncidentTypeID = 11;
This is failing with
Incorrect syntax near the keyword 'WHERE'.
Any thoughts would be greatly appreciated!
You need to define join conditions with ON after your INNER JOIN, to link the two tables:
SELECT
*
FROM
MasterAccounts AS ma
INNER JOIN
SubAccounts AS sa ON ma.CustomerID = sa.CustomerID;
INNER JOIN
Incidents AS i ON sa.AccountID = i.AccountID
WHERE
IncidentTypeID = 11;
Those columns must exist in the tables involved, and should have the same datatype (because otherwise the JOIN might cause a "hidden" data type conversion which can be costly in terms of performance)
Most of the WHERE conditions should be in ON clauses for each join:
SELECT i.*
FROM MasterAccounts ma
INNER JOIN SubAccounts sa ON sa.CustomerID = ma.CustomerID
INNER JOIN Incidents i ON i.AccountID = sa.AccountID
WHERE IncidentTypeID = 11;
The correct syntax would be:
SELECT
*
FROM
MasterAccounts AS ma
INNER JOIN
SubAccounts AS sa ON ma.CustomerID = sa.CustomerID;
INNER JOIN
Incidents AS i ON sa.AccountID = i.AccountID
WHERE
IncidentTypeID = 11;
Hope this helps!

SQL Query on SQL Server 2008

I'm trying to get only customers that ordered both a "Gas Range" and a "Washer". I'm getting Customers who ordered a "Gas Range" and not a "Washer" and customers with both. I need the customer that meets both conditions. I'm close but a little stuck. Below is the query that I have so far. Please let me know if you need more information.
My Tables - CUSTOMER(CUST_NUM, CUST_NAME), ORDER_LINE(ORDER_NUM, PART_NUM), ORDERS(ORDER_NUM, CUST_NUM), PART(PART_NUM, PART_DESCRIPTION)
SELECT C.CUST_NAME AS [Customer(s) that ordered a Gas Range and Washer]
FROM CUSTOMER C
INNER JOIN ORDERS O
ON C.CUST_NUM = O.CUST_NUM
INNER JOIN ORDER_LINE OL
ON O.ORDER_NUM = OL.ORDER_NUM
INNER JOIN PART P
ON OL.PART_NUM = P.PART_NUM
WHERE P.PART_DESCRIPTION IN ('GasRange','Washer')
GROUP BY C.CUST_NAME
try the following
SELECT C.CUST_NAME AS [Customer(s) that ordered a Gas Range and Washer]
FROM CUSTOMER C
INNER JOIN ORDERS O
ON C.CUST_NUM = O.CUST_NUM
INNER JOIN ORDER_LINE OL
ON O.ORDER_NUM = OL.ORDER_NUM
INNER JOIN PART P
ON OL.PART_NUM = P.PART_NUM
INNER JOIN ORDERS O2
ON C.CUST_NUM = O2.CUST_NUM
INNER JOIN ORDER_LINE OL2
ON O2.ORDER_NUM = OL2.ORDER_NUM
INNER JOIN PART P2
ON OL2.PART_NUM = P2.PART_NUM
WHERE P.PART_DESCRIPTION IN ('GasRange') and P2.PART_DESCRIPTION IN ('Washer')
GROUP BY C.CUST_NAME
EDIT: Had a further look and I'm afraid that this can't be simplified in any other way than using WITH and complicated aggregate functions, which I would say would be more complicated than this - I think the other solution suggested using WITH won't work - it joins incorrectly. You definitely can't remove order line, and you have to use the order twice as well - if it was used once, it will cover only when the customer ordered it within one order, which is not what you wanted ;)
Try this...
So basically you need to join your Parts table again to ensure the same customer ordered a "Gas Range" and a "Washer". An IN, like in your current query functions as an OR therefore you are not getting the expected result.
WITH CTE AS (
SELECT DISTINCT O.CUST_NUM FROM ORDERS O
INNER JOIN ORDER_LINE OL
ON O.ORDER_NUM = OL.ORDER_NUM
INNER JOIN PART P
ON OL.PART_NUM = P.PART_NUM
INNER JOIN PART P2
ON OL.PART_NUM = P2.PART_NUM
WHERE P.PART_DESCRIPTION IN ('GasRange')
AND P2.PART_DESCRIPTION IN ('Washer')
)
SELECT C.CUST_NAME AS [Customer(s) that ordered a Gas Range and Washer]
FROM CUSTOMER C
INNER JOIN CTE O
ON C.CUST_NUM = O.CUST_NUM

Issues with joining many tables getting to many values

This is my script:
select c.rendering_id as prov_number, c.begin_date_of_service as date_of_service,
c.practice_id as group_number, v.enc_nbr as invoice, p.person_nbr as patient,
v.enc_nbr as invoice_number, c.charge_id as transaction_number,
t.med_rec_nbr as primary_mrn, p.last_name, p.first_name,
z.payer_id as orig_fsc_number, z.payer_id as curr_fsc_number,
c.location_id as location_number, c.closing_date as posting_date,
c.quantity as service_units, c.amt as charge_amount,
c.cpt4_code_id as procedure_code, r.description as procedure_name,
x.tran_code_id as pay_code_number, ISNULL([modifier_1],'') as modifier_code_1,
ISNULL([modifier_2],'') as modifier_code_2, ISNULL([modifier_3],'') as modifier_code_3,
ISNULL ([icd9cm_code_id],'') as dx_code_1, ISNULL ([icd9cm_code_id_2],'') as dx_code_2,
ISNULL ([icd9cm_code_id_3],'') as dx_code_3, ISNULL ([icd9cm_code_id_4],'') as dx_code_4
from charges c, person p, patient t, patient_encounter v, encounter_payer z, cpt4_code_mstr r, transactions x
where c.person_id = p.person_id
and c.person_id = t.person_id
and c.person_id = v.person_id
and c.person_id = z.person_id
and c.cpt4_code_id = r.cpt4_code_id
and c.person_id = x.person_id
and c.practice_id = '0001'
and c.closing_date >= GetDate() - 7
I should be getting about 14k rows but with this I am getting a couple hundred thousand. I feel like there should be an inner join here to correct it but I have read through a bunch of posts and can seem to get it working. Its by far the biggest pull I have ever done in SQL.
Any help would be greatly help.
Without knowing more about the data structures and foreign key relationships, this answer is just educated speculation. Before answering, though, you need to learn proper JOIN syntax. Your query should look like:
from charges c join
person p
on . . . .
That said, you problem is probably that you are joining along multiple dimensions at the same time. Although not explicitly clear, I am guessing that a person could have multiple patient encounters, say A, B, and C. A person might also have multiple charges, say 10, 11, and 12.
Your query will produce nine rows in this case, one for each combination.
In other words, you need to identify:
Verify the join keys between tables. Is a table called transactions really joined to encounters and costs using the person_id?
Find out where you are getting cross products, and split into two subqueries that are then appropriately joined together.
I would suggest that you start with the first two tables, and see whether you get the expected row count for:
select *
from charges c join
person p
on c.person_id = p.person_id
where c.practice_id = '0001' and
c.closing_date >= GetDate() - 7
Then build up the query one table at a time to get the results you want.
One last note, when using table aliases, I find it much clearer to use aliases that evoke the table. "C" for charges is very good. Consider something like "pe" for patient_encounters, and so on.
It should be like this or you can use left join
select c.rendering_id as prov_number, c.begin_date_of_service as date_of_service,
c.practice_id as group_number, v.enc_nbr as invoice, p.person_nbr as patient,
v.enc_nbr as invoice_number, c.charge_id as transaction_number,
t.med_rec_nbr as primary_mrn, p.last_name, p.first_name,
z.payer_id as orig_fsc_number, z.payer_id as curr_fsc_number,
c.location_id as location_number, c.closing_date as posting_date,
c.quantity as service_units, c.amt as charge_amount,
c.cpt4_code_id as procedure_code, r.description as procedure_name,
x.tran_code_id as pay_code_number, ISNULL([modifier_1],'') as modifier_code_1,
ISNULL([modifier_2],'') as modifier_code_2, ISNULL([modifier_3],'') as modifier_code_3,
ISNULL ([icd9cm_code_id],'') as dx_code_1, ISNULL ([icd9cm_code_id_2],'') as dx_code_2,
ISNULL ([icd9cm_code_id_3],'') as dx_code_3, ISNULL ([icd9cm_code_id_4],'') as dx_code_4
from charges c
inner join person p on c.person_id = p.person_id
inner join patient t on c.person_id = t.person_id
inner join patient_encounter v on c.person_id = v.person_id
inner join encounter_payer z on c.person_id = z.person_id
inner join cpt4_code_mstr r on c.cpt4_code_id = r.cpt4_code_id
inner join transactions x on c.person_id = x.person_id
where c.practice_id = '0001'
and c.closing_date >= GetDate() - 7
Now you comment one inner join at a time and execute below query and see which of these joins is causing one to many relationship...when the count gives you say around 14 K that means the commented table is causing 1 to many relationship.
Otherwise best way is to find the relationship based on unique key,primary key and FK on these tables.
select
count(c.person_id)
from charges c
inner join person p on c.person_id = p.person_id
inner join patient t on c.person_id = t.person_id
inner join patient_encounter v on c.person_id = v.person_id
inner join encounter_payer z on c.person_id = z.person_id
inner join cpt4_code_mstr r on c.cpt4_code_id = r.cpt4_code_id
inner join transactions x on c.person_id = x.person_id
where c.practice_id = '0001'
and c.closing_date >= GetDate() - 7
You can try
select count(*) from <tablename> group by person_id having count(*) > 1
and repeat above query for all tables this will give you an idea on what kind of relationship between charges table and other tables. Offcourse use cpt4_code_id for cpt4_code_mstr table but by name it looks like that this table is master table so it will have a signle vale for each cpt4-code_id value in charges table.
I hope it will help