MySQL join for this specific scenario - mysql

I am having a problem with MySQL joins.
Table_A:
A_id Cost1 A1_id Cost2
1 500 0 200
1 100 1 100
1 50 2 60
1 10 3 50
2 5 0 10
Table_B (Refers B_id: from Table_A A1_id):
B_id FName LName
1 X A
2 Y B
3 Z C
Table_C (Refers C_id: from Table_A A_id):
C_id Towns
1 Atlanta
2 NewYork
I need to combine all three tables, like the following output:
I extract the Towns that match (Table_A.A_id=Table_C.C_id).
I extract the Fname,Lname that match (table_A.A1_id=Table_b.b_id).
I need to skip the Towns if A1_id != 0.
I need to skip the Fname,Lname if A1_id == 0.
The remaining data may either be a value or null, which I specify as '#'.
What would be an efficient MySQL query for the given scenario?
Output:
A_id Cost1 A1_id cost2 Fname Lname Towns
1 500 0 200 # # Atlanta
1 100 1 100 X A #
1 50 2 60 Y B #
1 10 3 50 Z C #

I think it should be something like this.
select A_id, Cost1, A1_id, cost2, Fname, Lname, Towns
from Table_A
left join Table_B on table_A.A1_id = Table_b.b_id
left join Table_C on Table_A.A_id = Table_C.C_id

This looks like a UNION of two distinct queries to me. I'm going to assume that the ID columns never contain negative values.
SELECT A.A_id, A.Cost1, A.A1_id, A.Cost2, B.Fname, B.Lname, C.Town
FROM Table_A AS A
INNER JOIN Table_B AS B ON A.A1_id = B.B_id
LEFT OUTER JOIN Table_C AS C ON A.A_id = C.C_id
WHERE A.A1_id != 0
AND C.C_id < 0
UNION
SELECT A.A_id, A.Cost1, A.A1_id, A.Cost2, B.Fname, B.Lname, C.Town
FROM Table_A AS A
LEFT OUTER JOIN Table_B AS B ON A.A1_id = B.B_id
INNER JOIN Table_C AS C ON A.A_id = C.C_id
WHERE A.A1_id = 0
AND B.B_id < 0;
I'm not completely confident about that formulation. There's a chance that the conditions on B.B_id < 0 and C.C_id < 0 need to be associated with the corresponding ON clauses.
There's also a decent chance that using two left outer joins in a single SELECT with appropriate OR'd filters would achieve the correct result.

If I understand correctly, it should be:
select fname, lname, towns
from table_a, table_b table_c
where table_a.a_id = table_c.c_id
and table_a.a1_id = table_b.b_id
and table_a.a1_id <> 0;

Related

Issue getting while fetching data using joins

I have four table and structure as below :
1)Budget
id Budget_name
1 test1
2 test2
3 test3
2)Yearly Budget
id amount_yearly budgetid
1 1000 1
2 2000 2
3 5000 3
ri_spent
id Spent_amount budgetid
1 100 2
2 100 2
3 200 3
4)FI_spent
id Spent_amount budgetid
1 100 2
2 100 3
3 200 3
i want to fetch data accourding to or based on first budget table id
below is the query i was trying:
select d.centers as Cost_Center,
ud.BUDGET_ANNUAL_AMOUNT as Annual_Budget,
l.LEAD_ID as Lead_Id,
l.AMOUNT as Lead_Amount,
f.FINANCEADD_ID as Finance_Id,
f.AMOUNT as Finance_Amount
from Cost_centers as d
inner join ANNUAL_BUDGET_BUDGET_CENTER as ud on d.id = ud.BUDGET_ID
inner join RI_DETAILS as l on l.COST_CENTER = d.id
inner join F_RI_DETAILS as f on f.COST_CENTER = d.id
ORDER BY d.id DESC
I want output of the following way:
Id Name ri_id FI_ID RI_Spent_Amount FI_Spent_Amount Annual_Buget
1 test1 1000
2 test2 1 1 100 200 1000
2 test2 2 100 2000
3 test3 3 2 300 100 2000
3 test3 3 200 2000
Any way if possible then please help me.
I want to minus annual budget with spent_amount later.
If possible then help me .
Please Try This Query ,
select b.id ,
b.Budget_name,
y.id,
y.amount_yearly,
r.id,
r.ri_spent,
f.id,
f.Spent_amount
from Budget b
INNER JOIN Yearly_Budget y on y.budgetid =b.id
INNER JOIN ri_spent r on r.budgetid =b.id
INNER JOIN FI_spent f on f.budgetid =b.id
ORDER BY b.id DESC
Could you please use left join instead of inner join. Requirement and sql arent matching so i may be little off. But you can get the idea.
select d.centers as Cost_Center,
ud.BUDGET_ANNUAL_AMOUNT as Annual_Budget,
l.LEAD_ID as Lead_Id,
l.AMOUNT as Lead_Amount,
f.FINANCEADD_ID as Finance_Id,
f.AMOUNT as Finance_Amount
from Cost_centers as d -- I assume this is the budget table
left outer join ANNUAL_BUDGET_BUDGET_CENTER as ud on d.id = ud.BUDGET_ID
left outer join RI_DETAILS as l on l.COST_CENTER = d.id
left outer join
(select sum(FINANCEADD_ID) FINANCEADD_ID,sum(AMOUNT) AMOUNT,id from
F_RI_DETAILS group by id) as f
on f.COST_CENTER = l.id
ORDER BY d.id DESC

How can I accomplish the following in SQL?

I have two tables.
table_a:
id | data_x | data_y
--------------------
1 person joe
2 person bob
3 amount 200
4 addres philville
tableB:
map_id | table_a_id
-------------------
7 1
7 3
7 4
8 4
8 2
The result I want is the map_id if it has an entry in table_a for both data_x = 'person' and data_y = '200'
So with the above table B, the result should be
map_id
------
7
How can I write that query in SQL?
This situation is a perfect fit for an unusual SQL operator: INTERSECT. It is a very declarative, efficient and elegant solution for this problem.
SELECT Map.map_id
FROM Table_B AS Map JOIN Table_A AS Person ON (Person.id = Map.table_a_id) AND (Person.data_x = 'person')
INTERSECT
SELECT Map.map_id
FROM Table_B AS Map JOIN Table_A AS Amount ON (Amount.id = Map.table_a_id) AND (Amount.data_y = '200')
Formally what you are asking for is exactly the intersection of two disjoint sets: the set of map id's that are persons and the set of map id's that have a value of 200.
Please note the INTERSECT operator does not exists in MySQL, but it does in almost all advanced relational DBMS, including PostgreSQL.
This is less elegant than the INTERSECT solution #Malta posted, but it works with the limited capabilities of MySQL as well:
SELECT b1.map_id
FROM table_a a1
JOIN tableb b1 ON a1.id = b1.table_a_id AND a1.data_x = 'person'
JOIN tableb b2 ON b2.map_id = b1.map_id AND b2.table_a_id <> b1.table_a_id
JOIN table_a a2 ON a2.id = b2.table_a_id AND a2.data_y = '200';
SQL Fiddle for MySQL.
SQL Fiddle for Postgres.
Based on your input, the following should get you started using MySQL:
SELECT
map_id
FROM TableB
JOIN Table_A
ON TableB.table_a_id = Table_A.id
AND
((Table_A.data_x = 'person')
OR
(Table_A.data_y = '200')
)
GROUP BY map_id
HAVING COUNT(table_a_id) = 2
;
See it in action: SQL Fiddle.
Update
As Erwin Brandstetter made explicit: If the data can't be trusted to be inherently consistent (along the lines of your inquiry), one option is:
SELECT map_id FROM (
SELECT map_id, 'data_x' t
FROM TableB B JOIN Table_A A ON B.table_a_id = A.id AND A.data_x = 'person'
UNION
SELECT map_id, 'data_y'
FROM TableB B JOIN Table_A A ON B.table_a_id = A.id AND A.data_y = '200'
) T
GROUP BY map_id
HAVING COUNT(DISTINCT t) = 2
;
This should ensure "at least one each". (Alternatives have been suggested by others.) To get "exactly one each", you could try
SELECT map_id FROM (
SELECT map_id, 'data_x' t, data_y
FROM TableB B JOIN Table_A A ON B.table_a_id = A.id AND A.data_x = 'person'
UNION
SELECT map_id, 'data_y', data_y
FROM TableB B JOIN Table_A A ON B.table_a_id = A.id AND A.data_y = '200'
) T
GROUP BY map_id
HAVING COUNT(DISTINCT t) = 2 AND COUNT(DISTINCT data_y) = 2
;
See it in action (with additional test data): SQL Fiddle.
And it works in PostgreSQL as well: SQL Fiddle
Please comment if and as this requires adjustment / further detail.
Join the 2 tables, group by map_id, use conditional counting with either count() or sum(), and filter in having clause (I use mysql syntax below):
select map_id,
sum(
case
when a.data_x='person' or a.data_y='200' then 1
else 0
end
) as matches
from a
inner join b on a.id=b.a_id
group by b.map_id
having matches=2
The above query assumes that you cannot have more than one record for any map_id where data_x is person or data_y is 200. If this assumption is incorrect, then you need to use either exists subqueries or 2 derived tables.
Sounds like you want a standard INNER JOIN.
But I do beg to differ on your result:
map_id if it has an entry in table_a for both data_x = 'person' and data_y = '200'
There is not a record in your data set that has both 'person' and data_y = '200' and therefore no mp_id can be returned
Here is a typical INNER JOIN relating to your narrative.
SELECT DISTINCT
b.map_id
FROM
TableA a
INNER JOIN TableB b
ON a.id = b.table_a_id
WHERE
a.data_x = 'person'
AND a.data_y = '200'
If more than one map_id exists with data_x = 'person' and data_y = '200' then you will get multiple results but only 1 row per map_id
If you want the map_id(s) for records with data_x = 'person' or data_y = '200' then switch the and in the where statement to or and you will receive map_id 7 & 8.
SELECT DISTINCT
b.map_id
FROM
TableA a
INNER JOIN TableB b
ON a.id = b.table_a_id
WHERE
a.data_x = 'person'
OR a.data_y = '200'
Note this encompasses (7,1)(8,2) because 1 & 2 both have data_x = 'person' and then (7,3) because 3 has data_y = '200' therefore it would return map_id 7 & 8.
select map_id from
table_b b
left outer join table_a a1 on (b.table_a_id = a1.id and a1.data_x = 'person')
left outer join table_a a2 on (b.table_a_id = a2.id and a2.data_y = '200')
group by map_id
having count(a1.id) > 0 and count(a2.id) > 0
Lets do it simple:
SELECT * FROM
(
SELECT map_id
FROM table_a a1
inner join TableB b1 ON a1.id = b1.table_a_id
where a1.data_x = 'person'
) as p
inner join
(
SELECT map_id
FROM table_a a1
inner join TableB b1 ON a1.id = b1.table_a_id
where a1.data_y = '200'
) as q
on p.map_id = q.map_id
You may replace SELECT * FROM with SELECT p.map_id FROM.
You may add more sub-set-joins to have more conditions.
sql-fiddle

MySQL Select Distinct with Left Join?

I am trying to get a list of company_id's that have no company-level notes. The company may, however, have location-level notes.
company
-------------------------
company_id name deleted
1 Foo 0
2 Bar 0
3 Baz 0
location
-----------------------
location_id company_id
6 1
7 2
8 3
note
-----------------------------------------
note_id company_id location_id deleted
10 2 6 0 // location-level note
11 1 7 0 // location-level note
12 null 8 0 // location-level note
13 2 null 0 // company-level note
I would want my result table to be this:
company_id name
1 Foo
3 Baz
Update
Foo/company_id = 1 does not have a company-level note because the note also has a location_id, which makes it a location-level note. Company-level notes are notes that only link to a company (and not a location).
End of Update
I've tried doing something like this, but it returns an empty set, so I'm not sure if it's working and there aren't any companies without company-level notes or if I'm doing something wrong.
SELECT DISTINCT
c.company_id,
c.name
FROM company AS c
LEFT JOIN note AS n
ON c.company_id = n.company_id
WHERE
c.deleted = 0 AND
n.deleted = 0 AND
n.location_id IS NOT NULL AND
n.location_id != 0 AND
c.company_id = (SELECT MAX(company_id) FROM company)
Revised Accepted Answer by Mike
SELECT
company_id,
name
FROM company
WHERE
deleted = 0 AND
company_id NOT IN (
SELECT DISTINCT
c.company_id
FROM company AS c
INNER JOIN note AS n
ON c.company_id = n.company_id
WHERE (
n.deleted = 0 AND
(n.location_id IS NULL OR
n.location_id = 0)
)
);
The easiest way to think about this is to first find the all the companies that have company level notes, which you can do with
select distinct c.company_id
from company c
inner join notes n
on c.company_id = n.company_id
where n.location_id is null;
Then simply remove these companies from the company select:
select company_id,
name
from company
where company_id not in (select distinct c.company_id
from company c
inner join notes n
on c.company_id = n.company_id
where n.location_id is null);
*Updated to use inner join instead of comma-separated join.
SELECT DISTINCT c.*
FROM company c
LEFT
JOIN note n
ON n.company_id = c.company_id
AND n.location_id IS NULL
WHERE n.note_id IS NULL;

How can i subtract in a SQL

I know how to subtract in sql but in my case its kinda difficult.
I have following DB
Table1
id........artnumber.......price.......flag.......cid
1 12345001 1200 L 9999
2 12345002 2000 L 9999
3 12345003 500 L 7777
4 12345004 1950 L 6666
5 54321001 500 R 9999
6 54321002 1000 R 6666
7 54321003 500 R 9999
Table2
id........artnumber.......comment
1 54321001 12345001
2 54321002 12345004
3 54321003 12345001
what im trying is:
i want to select all entrys from table1 having L flag
on the result of this i want to subtract the prices with R flag from the one with L flag. but only on the one who has his artnumber in the comment of table2
Result i want is
artnumber.......price.......flag.......cid
12345001 200 L 9999 //her was 2 R flagged items substracted
12345002 2000 L 9999 //her was nothing substracted
12345003 500 L 7777 //her was nothing substracted
12345004 950 L 6666 //her was 1 R flagged item substracted
does anybody knows how to set up a sqlstatement for this?
i found some on a other thread but it doesn't really helps me :)
This seems a little funky, but here goes. I think this will get you what you're looking for:
SELECT a.artnumber, a.price - SUM(IFNULL(b.price, 0)) price, a.flag, a.cid
FROM Table1 a
LEFT JOIN (Table1 b INNER JOIN Table2 ON Table2.artnumber = b.artnumber AND b.flag = 'R')
ON Table2.comment = a.artnumber
WHERE a.flag = 'L'
GROUP BY a.artnumber, a.price, a.flag, a.cid
Let me know if you need me to explain any of this, but the joins should be pretty straightforward.
Also, depending on your types, I'm not sure if you'll have to CAST that comment field to do the match. There may be some considerations there. I'm also making assumptions about the uniqueness of your records. In other words, I assume that artnumber and flag together constitute a unique record.
This is your data for the L prices:
select l.artnumber, l.price, l.flag
from table1 l
where l.flag = 'L';
This is your data for the R prices:
select x.comment as artnumber, sum(r.price)
from table1 r
inner join table2 x on x.artnumber = r.artnumber
where r.flag = 'R';
Together:
select
l_data.artnumber,
l_data.price - (case when r_data.sum_price is null then 0 else r_data.sum_price end) as calc_price,
l_data.flag,
l_data.cid
from
(
select l.artnumber, l.price, l.flag, l.cid
from table1 l
where l.flag = 'L'
) l_data
left join
(
select x.comment as artnumber, sum(r.price)
from table1 r
inner join table2 x on x.artnumber = r.artnumber
where r.flag = 'R'
) r_data on r_data.artnumber = l_data.artnumber;

MySQL JOIN WHERE forgein Key = id1 AND forgein Key = id2

I am having a bit of a brain block with this problem and I am finding it hard to search for a solution because I cant phrase the question correctly to bring up the relevant information.
I am trying to get back "fProduct" record from the table below where it has a "fAttribute"
of 2 and 20.
id fAttribute fProduct
19 2 2967
48 2 2923
50 2 3008
51 20 3008
52 2 2295
53 20 2295
My statment below produces 0 results when I would expect to return fProduct's 2295 and 3008.
SELECT fProduct
FROM tableName
WHERE fAttribute = 2 AND fAttribute = 20
GROUP BY fProduct
Can anyone help please?
You can either use INNER JOINS or use EXISTS conditions:
INNER JOIN:
SELECT DISTINCT a.fProduct
FROM MyTable a
INNER JOIN MyTable b ON a.fProduct = b.fProduct AND b.fAttribute = 2
INNER JOIN MyTable c ON a.fProduct = c.fProduct AND c.fAttribute = 20
EXISTS:
SELECT afproduct
FROM MyTable a
WHERE EXISTS (SELECT b.id FROM MyTable b WHERE a.fProduct = b.fProduct AND b.fAttribute = 2)
AND EXISTS (SELECT c.id FROM MyTable c WHERE a.fProduct = c.fProduct AND c.fAttribute = 20)
A join should help:
SELECT distinct a.fProduct
FROM tableName as a
join tableName as b on b.product = a.product
WHERE a.fAttribute = 2 and b.fAttribute = 20
Since your are already doing a GROUP BY just change your WHERE clause to an OR or an IN and add the HAVING COUNT(fattribute) = 2 which makes sure it has both.
SELECT fproduct
FROM tablename
WHERE fattribute IN (2 , 20)
GROUP BY fproduct
HAVING COUNT(fattribute) = 2