sql join two tables with 0 values - sql

I'm having some trouble joining the contents of two tables. Here's the current situation and the table should be shown as result
Table a
id | income
----------
1 | 100
2 | 200
3 | 300
4 | 400
5 | 500
Table b
id | outcome
----------
1 | 10
2 | 20
6 | 60
7 | 70
3 | 30
Result table
id | income | outcome | balance
--------------------------------
1 | 100 | 10 | 100-10=90
2 | 200 | 20 | 200-20=180
3 | 300 | 30 | 300-30=270
4 | 400 | 0 | 400-0=400
5 | 500 | 0 | 500-0=500
6 | 0 | 60 | 0-60=-60
7 | 0 | 70 | 0-70=-70
every id should be disctinct in the result table
income and outcome should be shown in the result table. if the id is not in one of the table income or outcome should be 0
calculation of balance column: income-outcome
Please provide code, without using the statement
where id not in (select id from ...)
because not in statement is not supported by the system I am using.
Thank you very much for your help

You can do what you want using a full outer join:
select coalesce(a.id, b.id) as id,
coalesce(a.income, 0) as income,
coalesce(b.outcome, 0) as outcome,
(coalesce(a.income, 0) - coalesce(b.outcome, 0)) as balance
from tablea a full outer join
tableb b
on a.id = b.id;
You don't specify the database. Full outer join is ANSI-standard and supported by most databases.

Related

How to use outer join, conditionally on a value in joined table [duplicate]

I have a table. It has a pk of id and an index of [service, check, datetime].
id service check datetime score
---|-------|-------|----------|-----
1 | 1 | 4 |4/03/2009 | 399
2 | 2 | 4 |4/03/2009 | 522
3 | 1 | 5 |4/03/2009 | 244
4 | 2 | 5 |4/03/2009 | 555
5 | 1 | 4 |4/04/2009 | 111
6 | 2 | 4 |4/04/2009 | 322
7 | 1 | 5 |4/05/2009 | 455
8 | 2 | 5 |4/05/2009 | 675
Given a service 2 I need to select the rows for each unique check where it has the max date. So my result would look like this table.
id service check datetime score
---|-------|-------|----------|-----
6 | 2 | 4 |4/04/2009 | 322
8 | 2 | 5 |4/05/2009 | 675
Is there a short query for this? The best I have is this, but it returns too many checks. I just need the unique checks at it's latest datetime.
SELECT * FROM table where service=?;
First you need find out the biggest date for each check
SELECT `check`, MAX(`datetime`)
FROM YourTable
WHERE `service` = 2
GROUP BY `check`
Then join back to get the rest of the data.
SELECT Y.*
FROM YourTable Y
JOIN ( SELECT `check`, MAX(`datetime`) as m_date
FROM YourTable
WHERE `service` = 2
GROUP BY check) as `filter`
ON Y.`service` = `filter`.service
AND Y.`datetime` = `fiter`.m_date
WHERE Y.`service` = 2

How to join a number with the range of number from another table

I have the two following tables:
| ID | Count |
| --- | ----- |
| 1 | 45 |
| 2 | 5 |
| 3 | 120 |
| 4 | 87 |
| 5 | 60 |
| 6 | 200 |
| 7 | 31 |
| SizeName | LowerLimit | UpperLimit |
| -------- | ---------- | ---------- |
| Small | 0 | 49 |
| Medium | 50 | 99 |
| Large | 100 | 250 |
Basically, one table specifies an unknown number of range names and their associated integer ranges. So a count range of 0 to 49 from the person table gets a small designation. 50-99 gets 'medium' etc. I need it to be dynamic because I do not know the range names or integer values.
Can I do this in a single query or would I have to write a separate function to loop through the possibilities?
One way to do this would be to join the tables, depending on if you want to keep values outside of your "range names", or not, you could use LEFT, or INNER join respectively.
SELECT A.id, A.Count, B.SizeName
FROM tableA A
LEFT JOIN tableB B ON A.id >= B.LowerLimit AND A.id < B.UpperLimit
You can also use the BETWEEN operator in a JOIN like this:
SELECT a.id, a.Count, b.SizeName
FROM tableA a
JOIN tableB b ON a.id BETWEEN b.LowerLimit AND b.UpperLimit

Return result from incomplete data?

I have two huge tables avg_rent and avg_sale. They contain average prices for apartments broken down by location, apartment size and other factors. The data in those tables may be incomplete.
For example in table avg_sale I may have:
id | apartment_size_id | county | city | median_sale
100 | 1 | 1 | 4 | 800
101 | 4 | 1 | 4 | 600
102 | 6 | 1 | 4 | 650
And in table avg_rent I may have:
id | apartment_size_id | county | city | median_rent
300 | 1 | 1 | 4 | 300
301 | 2 | 1 | 4 | 250
302 | 3 | 1 | 4 | 200
303 | 4 | 1 | 4 | 250
305 | 6 | 1 | 4 | 200
I want to create a SQL query or plpqsql function that would aggregate median_sale, median_rent and apartment_size_id columns and fill in missing data with -1 or something. In case of the example would return this (there are total of 6 size categories):
apartment_size_id | median_rent | median_sale
1 | 300 | 800
2 | 250 | -1
3 | 200 | -1
4 | 250 | 600
5 | -1 | -1
6 | 200 | 650
How can I do this?
You would use left join for this, assuming you have a table of apartment sizes:
select a.apartment_size_id, coalesce(r.median_rent, -1) as median_rent,
coalesce(s.median_sales, -1) as median_sales
from apartment_sizes a left join
avg_rent r
on a.apartment_size_id = r.apartment_size_id and
r.county = 1 and r.city = 4 left join
avg_sale s
on a.apartment_size_id = s.apartment_size_id and
s.county = 1 and s.city = 4;
This also assumes that you want the information for a single county/city pair.
I would recommend that you represent the missing values using NULL rather than -1, unless you have a good reason for choosing -1.
You can do this with full outer join and COALESCE
select
r.apartment_size_id,
COALESCE(r.median_rent, -1) as median_rent,
COALESCE(s.median_sale, -1) as median_sale
from avg_rent r
FULL OUTER JOIN avg_sale s
on r.apartment_size_id = s.apartment_size_id
This query definitely gives only those apartment_size_id present in either avg_rent and avg_sale
If you have a apartment table which is having all apartment_size_id info then you can do the same with left join and COALESCE
select
a.apartment_size_id,
COALESCE(r.median_rent, -1) as median_rent,
COALESCE(s.median_sale, -1) as median_sale
from apartment a
LEFT JOIN avg_rent r on a.apartment_size_id = r.apartment_size_id
LEFT JOIN avg_sale s on a.apartment_size_id = s.apartment_size_id
sql fiddle demo

Subtract the value of a row from grouped result

I have a table supplier_account which has five coloumns supplier_account_id(pk),supplier_id(fk),voucher_no,debit and credit. I want to get the sum of debit grouped by supplier_id and then subtract the value of credit of the rows in which voucher_no is not null. So for each subsequent rows the value of sum of debit gets reduced. I have tried using 'with' clause.
with debitdetails as(
select supplier_id,sum(debit) as amt
from supplier_account group by supplier_id
)
select acs.supplier_id,s.supplier_name,acs.purchase_voucher_no,acs.purchase_voucher_date,dd.amt-acs.credit as amount
from supplier_account acs
left join supplier s on acs.supplier_id=s.supplier_id
left join debitdetails dd on acs.supplier_id=dd.supplier_id
where voucher_no is not null
But here the debit value will be same for all rows. After subtraction in the first row I want to get the result in second row and subtract the next credit value from that.
I know it is possible by using temporary tables. The problem is I cannot use temporary tables because the procedure is used to generate reports using Jasper Reports.
What you need is an implementation of the running total. The easiest way to do it with a help of a window function:
with debitdetails as(
select id,sum(debit) as amt
from suppliers group by id
)
select s.id, purchase_voucher_no, dd.amt, s.credit,
dd.amt - sum(s.credit) over (partition by s.id order by purchase_voucher_no asc)
from suppliers s
left join debitdetails dd on s.id=dd.id
order by s.id, purchase_voucher_no
SQL Fiddle
Results:
| id | purchase_voucher_no | amt | credit | ?column? |
|----|---------------------|-----|--------|----------|
| 1 | 1 | 43 | 5 | 38 |
| 1 | 2 | 43 | 18 | 20 |
| 1 | 3 | 43 | 8 | 12 |
| 2 | 4 | 60 | 5 | 55 |
| 2 | 5 | 60 | 15 | 40 |
| 2 | 6 | 60 | 30 | 10 |

Getting Sum of MasterTable's amount which joins to DetailTable

I have two tables:
1. Master
| ID | Name | Amount |
|-----|--------|--------|
| 1 | a | 5000 |
| 2 | b | 10000 |
| 3 | c | 5000 |
| 4 | d | 8000 |
2. Detail
| ID |MasterID| PID | Qty |
|-----|--------|-------|------|
| 1 | 1 | 1 | 10 |
| 2 | 1 | 2 | 20 |
| 3 | 2 | 2 | 60 |
| 4 | 2 | 3 | 10 |
| 5 | 3 | 4 | 100 |
| 6 | 4 | 1 | 20 |
| 7 | 4 | 3 | 40 |
I want to select sum(Amount) from Master which joins to Deatil where Detail.PID in (1,2,3)
So I execute the following query:
SELECT SUM(Amount) FROM Master M INNER JOIN Detail D ON M.ID = D.MasterID WHERE D.PID IN (1,2,3)
Result should be 20000. But I am getting 40000
See this fiddle. Any suggestion?
You are getting exactly double the amount because the detail table has two occurences for each of the PIDs in the WHERE clause.
See demo
Use
SELECT SUM(Amount)
FROM Master M
WHERE M.ID IN (
SELECT DISTINCT MasterID
FROM DETAIL
WHERE PID IN (1,2,3) )
What is the requirement of joining the master table with details when you have all your columns are in Master table.
Also, isnt there any FK relationhsip defined on these tables. Looking at your data it seems to me that there should be FK on detail table for MasterId. If that is the case then you do not need join the table at all.
Also, in case you want to make sure that you have records in details table for the records for which you need sum and there is no FK relationship. Then you could give a try for exists instead of join.