SELECT JOIN with conditional (OR) cross - mysql

I need to do something like this (in MySQL), my attempts using UNION won't work until now.
In theory:
SELECT * FROM
tableA A
JOIN tableB B ON A.tableAId = B.tableAId
LEFT JOIN tableC C ON C.tableAId = A.tableAId
LEFT JOIN tableD D ON D.tableAId = A.tableAId
JOIN tableE E
ON (C.tableEId = E.tableEId OR D.tableEId = E.tableEId)
The expected result is crossing A with B, optional C with A, optional D with A and get the E result if its id is on C or D.
In order to know if the record found is from C or D, I'm using an IF checking if tableCId or tableDId are null.

I think your code will work.
SELECT *
FROM tableA A JOIN
tableB B
ON A.tableAId = B.tableAId LEFT JOIN
tableC C
ON C.tableAId = A.tableAId LEFT JOIN
tableD D
ON D.tableAId = A.tableAId JOIN
tableE E
ON E.tableEId IN (C.tableEId, D.tableEId);
This will filter out rows that have no matches in C and D, as well as those whose matches in C/D are not in E. I assume that is desirable.
Also, OR and IN can have a bad impact on JOIN performance, but you don't mention performance as a concern.

Related

Need help writing an inner join query

$query= mysqli_query($db,"SELECT a.*, b.collection_id , c.contract_id,
d.customer_name FROM rentals_invoice
AS b INNER JOIN rental_collection as a ON (b.collection_id = a.collection_id)
AS c INNER JOIN rental_contract as b ON (c.contract_id = b.contract_id)
AS d INNER JOIN customer_info as c ON (d.customer_id = c.customer_id)");
I have tables a, b, c, d
'a' have fkey of 'b'
'b' have fkey of 'c'
'c' have fkey of 'd'
I want to get data from all of them, and I don't know how to get it through inner join or any other type of join in a single query.
I am a beginner.
You have misplaced your aliases. You are overwriting your aliases. You also can use the using to simplify this.
FROM rentals_invoice AS b
INNER JOIN rental_collection as a using(collection_id)
INNER JOIN rental_contract AS c using(contract_id)
INNER JOIN customer_info as d using(customer_id)");
I also would use aliases that relate to the actual table name. a, b, c, etc. aren't useful and will be hard to diagnose later.
$query = mysqli_query($db,"
SELECT a.*,b.collection_id,c.contract_id,d.customer_name
FROM `rentals_invoice` As a
INNER JOIN rental_collection As b ON b.collection_id = a.collection_id
INNER JOIN rental_contract AS c ON c.contract_id = b.contract_id
INNER JOIN customer_information AS d ON d.customer_cnic = c.customer_cnic
");
Thanks to all who helped or tried to help me, i got the understanding of JOIN.

rails query - conditional joins

I have 4 tables A, B, C & D as follows:
B belongs to A
B belongs to C
&
C has many D
As 'A' may or may not have any B's but if there is a B, then that B must have a C. And according to my logic, C will have at least one D or may be more.
Now, I want to get the list of A's with count(b) as well as count(d). Here count(b) and count(d) might be zero.
So what I was doing till now is as follows:
#a = A.joins(bs: {c: :ds}).select("a.*, count(b) as count_b, count(d) as count_d").group("a.id")
But eventually this is not working, as it INNER JOIN b with a. Which means, if there is no corresponding b for a, then that a will not be in the list #a. That's the problem.
So, is there any way to do it ?
joins has a form where it accepts a string. In that string you can put 'LEFT OUTER join bs ON bs... = as...'
Try Left outer join:
#a = A.joins("LEFT JOIN B on A.id=B.a_id INNER JOIN C on C.id=B.c_id INNER JOIN CD on C.id=CD.c_id INNER JOIN D on D.id=CD.d_id")
For more details check this question
I tried
#a = A.joins("LEFT JOIN B on A.id=B.a_id LEFT JOIN C on C.id=B.c_id LEFT JOIN CD on C.id=CD.c_id LEFT JOIN D on D.id=CD.d_id") which worked for me.

MySQL with ON condition from another table

Let's say that I have:
SELECT * FROM a
LEFT OUTER JOIN b ON b.a_id = a.id
LEFT OUTER JOIN c ON b.c_id = c.id
Now what I want to do is to select b's that are assigned to c that is e. g. active (c.active = 1). How can I do that with ON?
Note that I can't use WHERE after the whole query above, because I want a's to be returned even if 0 b's are found.
Just to make sure, I understood the question: You want all rows where either c.active equals 1 or where there is no entry in b or c, right?
It's a bit lengthy but this seems to work:
SELECT * FROM a
LEFT OUTER JOIN b ON a.aid = b.aid
LEFT OUTER JOIN c ON b.bid = c.bid
WHERE a.aid NOT IN (
SELECT a.aid FROM a
INNER JOIN b ON a.aid = b.aid
INNER JOIN c ON b.bid = c.bid
WHERE NOT c.active
);
I could also imagine a solution using UNION
Which rows are returned in each case... Note that one case is different:
FROM b JOIN c ON ... AND c.active=1 -- rows in both tables exist and active
FROM b JOIN c ON ... WHERE c.active=1 -- ditto
FROM b LEFT JOIN c ON ... WHERE c.active=1 -- ditto
FROM b LEFT JOIN c ON ... AND c.active=1 -- all b's, but NULLs for inactive/missing c's
(Caveat: I am not sure I got the cases correct; just keep in mind that ON and WHERE are not always interchangeable.)
When mixing JOIN and LEFT JOIN, you may need to add parentheses:
FROM a JOIN ( b LEFT JOIN c ON... ) ON ... WHERE ...
FROM ( a JOIN b ON ... ) LEFT JOIN c ON... WHERE ...

Query with several consecutive LEFT JOIN - unexpected results

My goal is to populate several objects of several child classes (B, C, D, E, F...), all extending a parent class which is A. All of this, in one single and only big query.
Let's say I have several tables reflecting the classes structure, including these 3 ones:
Table A /* the parent class */
id type created
--------------------------------
392 B 1377084886
Table B /* one of the child classes */
id myfield myotherfield anotherone oneagain
-------------------------------------------------------------
392 234 'foo' 'bar' 3
Table G /* not part of the structure, just here for the query to check a field */
myfield fieldtocheck evenmorefields
------------------------------------------------
234 'myvalue1' 'foobar'
Now:
/* This (the query I want): */
SELECT
a.*,
b.*,
c.*,
d.*,
e.*,
f.*
FROM A a
LEFT JOIN B b ON a.id = b.id
LEFT JOIN C c ON a.id = c.id
LEFT JOIN D d ON a.id = d.id
LEFT JOIN E e ON a.id = e.id
LEFT JOIN F f ON a.id = f.id
LEFT JOIN G g_b ON b.myfield = g_b.myfield
LEFT JOIN G g_c ON c.myfield = g_c.myfield
WHERE g_b.fieldtocheck IN (myvalue1);
/* Returns this (what I don't want): */
id->392
type->B
created->1377084886
myfield->NULL /* why NULL? */
myotherfield->NULL /* why NULL? */
anotherone->NULL /* why NULL? */
oneagain->3 /* why, whereas other fields from B are NULL, is this one correctly filled? */
Whereas:
/* This (the query I don't want): */
SELECT
a.*,
b.*
FROM A a
LEFT JOIN B b ON a.id = b.id
LEFT JOIN G g_b ON b.myfield = g_b.myfield
WHERE g_b.fieldtocheck IN (myvalue1);
/* Returns this (what I want): */
id->392
type->B
created->1377084886
myfield->234
myotherfield->'foo'
anotherone->'bar'
oneagain->3
I have no idea why. Tried different things, but this is what I come up with. Has someone an idea?
EDIT: Clarified this post and made it more straightforward.
I think the problem that you are facing is the collision of names in the query. That is, there are multiple columns with the same name and MySQL chooses one of them for the result.
You need to alias the column names for each of the tables to a different name, for example, a_created, b_created, etc.
This appears to be the classic case where you need to move your where into the join itself so that it doesn't block everything else:
SELECT
A.*,
B.*,
C.*,
D.*,
E.*,
F.*,
FROM A
LEFT JOIN B ON A.id = B.id
LEFT JOIN C ON A.id = C.id
LEFT JOIN D ON A.id = D.id
LEFT JOIN E ON A.id = E.id
LEFT JOIN F ON A.id = F.id
LEFT JOIN G ON B.myfield = G.myfield and G.anotherfield IN (myvalue1)
LEFT JOIN H ON C.myfield = H.myfield;
SELECT a.id, a.type, a.created, b.myfield, b.myotherfield FROM `A` AS a INNER JOIN `B` AS b ON a.id = b.id ;
Use this It must work fine. If you have an additional where query you can add it.

Mysql inner join twice and use WHERE on both joins

In MySQL, I want to SELECT A.* FROM A where an inner join condition is satisfied whether directly (joining table B) or through another join table (C), WHERE B.field = myvalue. Can anyone point out the proper way to get results?
I have the following tables: A, B, C, which are associated as follows (A joins B, B joins C, A joins C):
B
/ \
A --- C
It looks pretty straightforward, but I get an empty set when I run the following code, even though I get results when I restrict the search to just joining B through C:
SELECT A.* FROM A
INNER JOIN C ON C.id = A.c_id
INNER JOIN B AS B_thru_C ON B_thru_C.id = C.b_id
INNER JOIN B AS B_from_A ON B_from_A.id = A.b_id
WHERE B_thru_C.field = 'myvalue' OR B_from_A.field = 'myvalue';
# yields an empty set
SELECT A.* FROM A
INNER JOIN C ON C.id = A.c_id
INNER JOIN B AS B_thru_C ON B_thru_C.id = C.b_id
WHERE B_thru_C.field = 'myvalue';
# yields results
How about this?
SELECT A.* FROM A
LEFT OUTER JOIN C ON C.id = A.c_id
INNER JOIN B ON B.id = A.b_id OR B.id = C.b_id
WHERE B.field = 'myvalue';