Adding more tables to an SQL query with a Union - sql

I am taking 2 columns (PaidGross and PaidDiscount) and putting them in a single column (Amount) for my results. (I am not trying to concatenate. My result will have ALL records from both columns in a single column)
The basic query is this:
SELECT PaidGross AS Amount, BillingID FROM APPaidInvDtl(noLock)
UNION ALL
SELECT PaidDiscount AS Amount, BillingID FROM APPaidInvDtl(noLock)
It returns 35706 records and seems to be getting what I want so I think I have that part figured out but...
Here is my expanded query as I am trying to include data from other tables as well:
SELECT APPaidInvDtl.PaidGross AS Amount, APPaidInvDtl.BillingID, DocumentLinks.DocIDInternal, DocumentLinks.LinkDocIDInternal
FROM APPaidInvDtl
INNER JOIN DocumentLinks ON APPaidInvDtl.BillingID=DocumentLinks.DocIDInternal
UNION ALL
SELECT APPaidInvDtl.PaidDiscount AS Amount, APPaidInvDtl.BillingID, DocumentLinks.DocIDInternal, DocumentLinks.LinkDocIDInternal
FROM APPaidInvDtl
INNER JOIN DocumentLinks ON APPaidInvDtl.BillingID=DocumentLinks.DocIDInternal
I get 102408 records with my expanded query so obviously I am doing something wrong. I believe I need to get exactly the same number of records in both queries to know that I'm getting the right data.
Thank you for any help or ideas on what I should try! Jordan
Update on March 16, 2018:
#Smog #shwant00 Yes, you are right. There are multiple documents linked so I am trying restrict it to a certain type of document. The links between the documents are stored in the DocumentLinks table however the Documents table is the one that has the types of documents specified. So I'm having trouble getting the right data at the right time. My latest try gives me the error: "DocumentLinks.LinkDocIDInternal" could not be bound." Here is my latest try:
SELECT APPaidInvDtl.PaidGross AS Amount, APPaidInvDtl.BillingID, Invoice.DocIDInternal, Invoice.DocYYMM, Invoice.Docseq, Checkrun.DocIDInternal, Checkrun.DocYYMM, Checkrun.Docseq, DocumentLinks.DocIDInternal, DocumentLinks.LinkDocIDInternal
FROM APPaidInvDtl(nolock)
INNER JOIN Documents Invoice ON APPaidInvDtl.BillingID=Invoice.DocIDInternal
INNER JOIN Documents Checkrun ON DocumentLinks.LinkDocIDInternal=Checkrun.DocIDInternal
INNER JOIN DocumentLinks ON APPaidInvDtl.BillingID=DocumentLinks.DocIDInternal WHERE Checkrun.Doctype=27
UNION ALL
SELECT APPaidInvDtl.PaidDiscount AS Amount, APPaidInvDtl.BillingID, Invoice.DocIDInternal, Invoice.DocYYMM, Invoice.Docseq, Checkrun.DocIDInternal, Checkrun.DocYYMM, Checkrun.Docseq, DocumentLinks.DocIDInternal, DocumentLinks.LinkDocIDInternal
FROM APPaidInvDtl(nolock)
INNER JOIN Documents Invoice ON APPaidInvDtl.BillingID=Invoice.DocIDInternal
INNER JOIN Documents Checkrun ON DocumentLinks.LinkDocIDInternal=Checkrun.DocIDInternal
INNER JOIN DocumentLinks ON APPaidInvDtl.BillingID=DocumentLinks.DocIDInternal WHERE Checkrun.Doctype=27

How about this?
with #tmp as (
SELECT PaidGross AS Amount, BillingID as BillingID FROM APPaidInvDtl(noLock)
UNION ALL
SELECT PaidDiscount AS Amount, BillingID as BillingID FROM
APPaidInvDtl(noLock)
)
select t.Amount, t.BillingID, d.DocIDInternal, d.LinkDocInternal
from #tmp t
inner join DocumnetLinks d on d.BillingID = t.BillingID

I would guess that if you run the first select in the union you will get 51204 rows. The increase from 35706 is caused by the join, but the increase from 51204 to 102408 is because you are using union all. Union all don’t filter out duplicate rows. You need to use union only wich will filter out duplicate rows and only returns distinct rows.

Related

"Can't find table" error in SQL FROM(UNION) AS table, INNER JOIN, COUNT() FROM table

I currently have a query where inside each union select, in order to get a count of occurences, I had something like:
SELECT Order_Id, Order_Date,
C.cnt AS Order_Parts
FROM table1
INNER JOIN (SELECT Order_Id, count(Order_Id) as cnt
FROM table1
GROUP BY Order_Id) C ON table1.Order_Id = C.Order_Id
UNION SELECT Order_Id, Order_Date,
C.cnt AS Order_Parts
FROM table2
INNER JOIN (SELECT Order_Id, count(Order_Id) as cnt
FROM table2
GROUP BY Order_Id) C ON table2.Order_Id = C.Order_Id
And it worked alright, but I'm reorganising it so that the UNION is inside the Query FROM, so something like this:
SELECT
Order_Id,Order_Date,C.cnt AS Order_Parts
FROM(
SELECT Order_Id, Order_Date
FROM table1
UNION SELECT Order_Id, Order_Date
FROM table2
) AS Parts
INNER JOIN (SELECT Order_Id, count(Order_Id) as cnt
FROM Parts
GROUP BY Order_Id) C ON Parts.Order_Id = C.Order_Id
But Access throws me an error saying it can't find table or query 'Parts'. I can't for the life of me figure out why it can't use it; could someone guide me to what's wrong?
The error seems pretty clear. A table alias (i.e. parts) refers to a table or reference for the purpose of defining columns. It does provide a new "source" for data.
That is, it cannot be reused as a table in the from clause.
This is true in all databases. However, just about any other database supports common table expressions -- which do what you want. MS Access does not. Your only options are:
Repeat the UNION query for the second reference.
Use a view.
Note: There might be other approaches to writing the query that you want to write. For that, I would suggest that you ask a new question with sample data, desired results, and a clear explanation of what you want to accomplish.
Also, you may not want UNION because that removes duplicates. UNION ALL is more common.

Total amount of financing already provided to each customer

I have three separate tables:
SystemA_Cash
SystemA_Loans
SystemB_CarLoans
Each table has Cash_AdvanceID field, Customer, & Funded Amount.
I need to total the amount funded for each customer.
Each customer can be present in any of the tables too.
So far I did
SELECT SystemA_CashAdvances.CashAdvanceID, SystemA_CashAdvances.Customer,
SystemA_CashAdvances.Funded_Amount
FROM
SystemA_CashAdvances
INNER JOIN
SystemA_Loans ON SystemA_Loans.CashAdvanceID = SystemA_CashAdvances.Cash_AdvanceID
INNER JOIN
SytemB_CarLoans ON SystemA_CashAdvances.Cash_AdvanceID = SystemB_CarLoans.CashAdvanceID;
It doesn't seem to return one table with the customers and the total amount each was funded.
You can use UNION and GROUP BY for this, like:
SELECT Cash_AdvanceID,
Customer,
SUM(Funded_Amount) AS "Total_Funded_Amount"
FROM (
SELECT Cash_AdvanceID,
Customer,
Funded_Amount
FROM SystemA_CashAdvances
UNION
SELECT Cash_AdvanceID,
Customer,
Funded_Amount
FROM SystemA_Loans
UNION
SELECT Cash_AdvanceID,
Customer,
Funded_Amount
FROM SytemB_CarLoans
)
GROUP BY Cash_AdvanceID,
Customer;
To do this with JOIN will be hard, as MS Access does not support a full outer join, as other database engines do. The closest is a one-sided outer join, as follows:
SELECT a.Cash_AdvanceID,
a.Customer,
a.Funded_Amount + Nz(l.Funded_Amount)
+ Nz(c.Funded_Amount) AS "Total_Funded_Amount"
FROM (SystemA_CashAdvances AS a
LEFT JOIN SystemA_Loans AS l
ON a.Cash_AdvanceID = l.Cash_AdvanceID)
LEFT JOIN SytemB_CarLoans AS c
ON a.Cash_AdvanceID = c.Cash_AdvanceID;
The above will work for customers that appear at least in the table SystemA_CashAdvances. If they only appear in another table, they will be excluded from the result. To get those in as well, you would need to use UNION again, which brings us back to the first solution above.

Join and compare 2 queries of 2 tables

This is probably a quite trivial question for many here but I am not used to write sub queries and joins, so I hope someone want to help.
I have two tables: new_road and old_roads.
These two queries sum up the length of the roads belonging to a specific road number.
SELECT new_road.nummer, SUM(new_road.length) FROM road_table.road GROUP BY new_road.nummer
SELECT old_road.nummer, SUM(ST_length(old_road.geom)) FROM old_road_table.old_road GROUP BY old_road.nummer
I wish to have a result table where these two queries are joined so I can compare the new and old summed length for each road number.
Like
old.nummer old.length new.nummer new.lenght
2345 10.3 2345 10.5
2346 578.2 2346 600
2347 54.2 NULL NULL
NULL NULL 2546 32.2
I think some version of an outer join is needed because there will be a road numbers in the old_road table that does not exist in the new.road table and i would like to see them too.
Appreciate any advice
Edit:
After advice from below did I came up with this:
SELECT * FROM
(SELECT new_road.nummer, SUM(new_road.length) FROM road_table.road GROUP BY new_road.nummer) new_table
FULL OUTER JOIN
(SELECT old_road.nummer, SUM(ST_length(old_road.geom)) FROM old_road_table.old_road GROUP BY old_road.nummer) old_table
ON new_road.nummer = old_road.nummer
But each time I run it I get missing FROM-clause entry. When I run each sub query individually they work. I have crosschecked with the documentation and it look OK to me, but clearly I am missing something here.
Consider using a FULL OUTER JOIN
This is not the exact output you requested but you don't need to display the nummer twice.
SELECT
COALESCE(new_road.nummer,old_road.nummer)nummer,
new_road.length,
old_road.length
FROM (
SELECT new_road.nummer
,SUM(new_road.length) length
FROM road_table.road
GROUP BY new_road.nummer
) new_road
FULL OUTER JOIN (
SELECT old_road.nummer
,SUM(ST_length(old_road.geom))length
FROM old_road_table.old_road
GROUP BY old_road.nummer
) old_road ON
old_road.nummer = new_road.nummer
Following query should solve the purpose. I didn't run it but the basic idea is result of a query on a table is another table on which you can query again.
Select * FROM (SELECT new_road.nummer, SUM(new_road.length) FROM road_table.road GROUP BY new_road.nummer) table1 JOIN (SELECT old_road.nummer, SUM(ST_length(old_road.geom)) FROM old_road_table.old_road GROUP BY old_road.nummer) table2 ON table1.new_road.nummer = table2.old_road.nummer
The tricky bit here is that you want to make sure you include all of the keys from both lists. My favorite way to do this kind of thing is:
select * from (
SELECT distinct new_road.nummer as nummer from road_table.road
union
SELECT distinct old_road.nummer as nummer FROM old_road_table.old_road
) allkeys
left join
(
SELECT new_road.nummer as nummer, SUM(new_road.length) as nlen
FROM road_table.road GROUP BY new_road.nummer
) n
on allkeys.nummer = n.nummer
left join
(
SELECT old_road.nummer as nummer, SUM(ST_length(old_road.geom)) as olen
FROM old_road_table.old_road GROUP BY old_road.nummer
) o
on allkeys.nummer = o.nummer
The first subquery builds a list of all keys, then you join to both of your queries from there. There's nothing wrong with an outer join, but I find this easier to manage if you have to include 3 or more tables. If you had to include another table it would just be one more union in allkeys and one more left join to that table.

SQL query from an existing list

Sql newb so im trying to figure this out:
I pull a list of customer names that purchase a certain item:
select r.name
from records r
where r.item_purchased='apple'
Now I want to take that list of customers and pull the records for everything they have purchased, but I can't get past the errors. I've tried things like:
with customer_list as
(above)
select r.*
from records r
where r.name=customer_list
If you do want to use a CTE, this should work:
with customer_list as
(
select r.name
from records r
where r.item_purchased='apple'
)
select r.*
from records r
where r.name in (select name from customer_list)
The difference between this and a JOIN (e.g. Michael's solution) is the join will produce a different result if the same customer purchased an apple more than once.
I believe a self join should solve your problem:
select distinct r2.*
from records r
join records r2
on r2.name = r.name
where r.item_purchased='apple'
EDIT: Added a DISTINCT based on #a_horse_with_no_name's insight into the difference between the results, because I doubt the duplication caused by the self join would be the desired result.
Select * from records where name in (
select name
from records
where item_purchased='apple'
)

Update using Distinct SUM

I have found a few good resources that show I should be able to merge a select query with an update, but I just can't get my head around of the correct formatting.
I have a select statement that is getting info for me, and I want to pretty much use those results to Update an account table that matches the accountID in the select query.
Here is the select statement:
SELECT DISTINCT SUM(b.workers)*tt.mealTax as MealCost,b.townID,b.accountID
FROM buildings AS b
INNER JOIN town_tax AS tt ON tt.townID = b.townID
GROUP BY b.townID,b.accountID
So in short I want the above query to be merged with:
UPDATE accounts AS a
SET a.wealth = a.wealth - MealCost
Where MealCost is the result from the select query. I am sure there is a way to put this into one, I just haven't quite been able to connect the dots to get it to run consistently without separating into two queries.
First, you don't need the distinct when you have a group by.
Second, how do you intend to link the two results? The SELECT query is returning multiple rows per account (one for each town). Presumably, the accounts table has only one row. Let's say that you wanted the average MealCost for the update.
The select query to get this is:
SELECT accountID, avg(MealCost) as avg_Mealcost
FROM (SELECT SUM(b.workers)*tt.mealTax as MealCost, b.townID, b.accountID
FROM buildings AS b INNER JOIN
town_tax AS tt
ON tt.townID = b.townID
GROUP BY b.townID,b.accountID
) a
GROUP BY accountID
Now, to put this into an update, you can use syntax like the following:
UPDATE accounts
set accounts.wealth = accounts.wealth + asum.avg_mealcost
from (SELECT accountID, avg(MealCost) as avg_Mealcost
FROM (SELECT SUM(b.workers)*tt.mealTax as MealCost, b.townID, b.accountID
FROM buildings AS b INNER JOIN
town_tax AS tt
ON tt.townID = b.townID
GROUP BY b.townID,b.accountID
) a
GROUP BY accountID
) asum
where accounts.accountid = asum.accountid
This uses SQL Server syntax, which I believe is the same as for Oracle and most other databases. Mysql puts the "from" clause before the "set" and allows an alias on "update accounts".