Query to find if a foreign key is set - mysql

OK, I have two MySQL Tables:
TableA containing the following columns: idTableA and idTableB_FK
TableB containing the following columns: idTableB and idTableA_FK
The relationships are as follows:
One-to-Many relationship from TableA to TableB (TableA.idTableA is
the PK, and the FK for that relationship is TableB.idTableA_FK)
One-to-One relationship between TableA and TableB (TableB.idTableB
is the PK, and it is be represented in TableA.idTableB_FK). It is
a one-to-one relationship because you can only have one row in TableA to take a
foreign key value.
Assuming the following data in TableA
idTableA | idTableB_FK
-----------------------------
1 | 2
2 | 5
3 | 6
4 | 8
And the folloing in TableB
idTableB | idTableA_FK
------------------------------
1 | 1
2 | 1
3 | 2
4 | 2
5 | 2
6 | 3
7 | 3
8 | 4
Now, what I want a query that will display idTableA, idTableB, and will display is_set column. The is set is a yes/no field (or 1/0) where it is set to yes only if TableA.idTableB_FK has a corresponding set value for that FK. So for the above example:
idTableA | idTableB | is_set
---------------------------------------------
1 | 1 | no
1 | 2 | yes
2 | 3 | no
2 | 4 | no
2 | 5 | yes
3 | 6 | yes
3 | 7 | no
4 | 8 | yes
Thanks.

I think you are looking for something like this:
select
TableA.idTableA,
TableB.idTableB,
case when EXISTS(select null
from TableA TableA_1
where
TableA_1.idTableA = TableA.idTableA
and TableA_1.idTableB_FK = TableB.idTableB)
then 'yes' else 'no' end as is_set
from
TableB left join TableA
on TableB.idTableA_FK = TableA.idTableA

OK that was from memory but should do it. If it is wrong I can run it against something and fix it.
SELECT tb.idTableA_FK AS idTableA, tb.idTableB, tbb.idTableB IS NOT NULL AS is_set
FROM TableB AS tb LEFT JOIN TableA AS ta ON (tb.idTableA_FK = ta.idTableA)
LEFT JOIN TableB AS tbb ON (ta.idTableB_FK = tbb.idTableB)

I believe that this is what you're looking for:
SELECT a.idTableA, b.idTableB, c.idTableB_FK IS NOT NULL is_set
FROM TableA a, TableB b LEFT JOIN TableA c ON b.idTableB=c.idTableB_FK
WHERE b.idTableA_FK=a.idTableA

I ended up doing this:
SELECT b.idTableA_FK, b.idTableB ,
IF (
(SELECT a.idTableB_FK
FROM (TableA AS a)
WHERE a.idTableA = b.idTableA_FK
) = b.idTableB, TRUE, FALSE
) AS is_set
FROM (TableB AS b)
Which produced the correct results.
I like to thank all that responded, and I am voting up fthiella because his/her suggested solution was influential to my solution.

Related

MySQL - dont show any row from ID use

I just need to show the values that don't use the specifically ID.
I have two table
table 1
id_xx
1
2
3
4
5
table 2
id | id_xx
3 | 3
4 | 3
4 | 1
I need this results for example. I say which results from ID i need
i need this result where id=3
id_xx
1
2
4
5
or where id=4
id_xx
2
4
5
or where id=1
1
2
3
4
5
You can LEFT JOIN table1 with table2 and apply the filter in the ON clause of the JOIN, and then filter on unmatched records:
SELECT t1.id_xx
FROM table1 t1
LEFT JOIN table2 t2 ON t2.id_xx = t1.id_xx AND t2.id = ?
WHERE t2.id IS NULL
You can replace the question mark with the id that you need to check.
Demo on DB Fiddle:
Given t2.id = 3:
| id_xx |
| ----- |
| 1 |
| 2 |
| 4 |
| 5 |
Given t2.id = 4:
| id_xx |
| ----- |
| 2 |
| 4 |
| 5 |
Another approach is using a Correlated Subquery with NOT EXISTS():
SELECT t1.id_xx
FROM table1 AS t1
WHERE NOT EXISTS (SELECT 1
FROM table2 AS t2
WHERE t2.id = ? -- your input id here
AND t2.id_xx = t1.id_xx)
If you have large table(s), and are worried about performance of these queries, then you may define the following index:
For the correlated subquery, define composite index (id, id_xx) on table2.
ALTER TABLE table2 ADD INDEX(id, id_xx);
Assuming that id_xx is already a Primary Key in the table1. So, you don't need to define any index there. If not, then you can define an index on it.

How to get total "active" users using Mysql?

I have four tables:
Table A: users_table
Table B: users_account_table
Table C: fangates_table
Table D: downloads_table
Table A(users_table) contains three columns: ID, NAME and GATE_COUNT.
Table C(fangates_table) contains users gates.
Table D(downloads_table) contains fangates_table_id and account_id.
TABLE A
ID | NAME | GATE_COUNT
---------------------------
1 | Joy | 2
---------------------------
2 | Ash | 0
---------------------------
3 | Nik | 2
---------------------------
4 | Wox | 0
TABLE B
ID | A_ID | ACCOUNT_ID
---------------------------
1 | 1 | 123456
---------------------------
2 | 1 | 654321
---------------------------
3 | 3 | 5888
---------------------------
4 | 3 | 8787
TABLE C
ID | A_ID | TITLE
---------------------------
1 | 1 | ABCD
---------------------------
2 | 1 | DFCV
---------------------------
3 | 3 | FGTG
---------------------------
4 | 3 | FRGTG
TABLE D
ID | C_ID | ACCOUNT_ID
---------------------------
1 | 1 | 123456
---------------------------
2 | 2 | 123456
---------------------------
3 | 3 | 7859
---------------------------
4 | 1 | 7585
From the above tables, I am trying to get the total "active" users (where an active user is defined as a user with fangates having any downloads other not themself)
I have tried the following query, but it has failed.
SELECT COUNT(*) FROM D
WHERE (C_ID,ACCOUNT_ID)
NOT IN
(SELECT C.ID, B.ACCOUNT_ID FROM A
LEFT JOIN B ON A.ID = B.A_ID
LEFT JOIN C ON A.ID = C.A_ID
WHERE A.GATE_COUNT >0);
The error, as I see it, is that you are failing to provide a join parameter in the initial selection of your table. If I am to understand your table design correctly, the following query would retrieve all the information in your tables:
SELECT *
FROM A,B,C,D
WHERE B.A_ID = A.ID AND C.A_ID = A.ID AND D.C_ID = C.ID;
If my presumption is correct, and you are trying to get the active users, you could simply place your parameters as an appended AND and continue onward. However, doing this many joins could greatly slow down any code; joins are a very taxing process, no matter how small the tables are. Since you are looking to find the count of downloads
SELECT COUNT(*) FROM D
WHERE (C_ID)
NOT IN
(SELECT C.ID FROM A, C
WHERE GATE_COUNT >0 AND A.ID = C.A_ID);
From what I can tell, you don't need to specify the distinct ACCOUNT_ID from the B table, as the information is distinctly paired to the B table. Otherwise, your columns would mismatch.
Otherwise, you can do a second lookup and try adding the B lookup as an AND parameter
SELECT COUNT(*) FROM D
WHERE (C_ID)
NOT IN (SELECT C.ID FROM A, C
WHERE GATE_COUNT >0 AND A.ID = C.A_ID) AND ACCOUNT_ID NOT IN
(SELECT DISTINCT B.ACCOUNT_ID FROM A,B WHERE A.ID = B.A_ID AND A.GATE_COUNT >0);
SELECT COUNT(DISTINCT(B.A_ID))
FROM TABLE_B B
JOIN TABLE_C C ON (B.A_ID = C.A_ID)
JOIN TABLE_D D ON (D.C_ID = C.C_ID AND B.ACCOUNT_ID <> D.ACCOUNT_ID)

Efficient way to get DISTINCT rows of Table A when JOINing with Table B

Simple problem. Given example tables:
Table A:
id | type
---+-----
1 | A
2 | B
3 | C
Table B:
id | a_id | type
---+------+-----
1 | 1 | X
2 | 2 | Y
3 | 1 | X
4 | 3 | Z
(there are additional columns, which I omitted, in order to clarify the problem)
The query:
SELECT a.*
FROM a a
INNER JOIN b b ON b.a_id = a.id
WHERE b.type = 'X'
Result:
id | type
---+-----
1 | A
1 | A
SQL Fiddle: http://sqlfiddle.com/#!2/e6138f/1
But I only want to have distinct rows of Table A. I know, I could do SELECT DISTINCT a.*, but our Table A has about 40 columns, and this SELECT can return 100-10000 rows. Isn't that extremely slow, if the database has to compare each column?
Or is MySQL intelligent enough, to just focus on the Primary Key for the DISTINCT operation?
Thanks in advance :)
Use exists instead of an explicit join:
select a.*
from tablea a
where exists (select 1 from tableb b where b.a_id = a.id and b.type = 'x');
For performance, create an index on tableb(a_id, type).

Finding cooccuring values in MYSQL weak relation table

I have a weak relation table, called header, it is basically just three ID's: id is an autoincrement primary key, did points to the id of table D and hid points to the id of table H. D and H are irrelevant here.
I want to find for any value of hid, the other values of hid that shares did with the original hid. An example:
id | did | hid
===============
1 | 1 | 1
2 | 1 | 2
3 | 1 | 3
4 | 2 | 1
5 | 2 | 4
6 | 2 | 5
7 | 3 | 2
8 | 3 | 6
For hid = 1 I would thus like to find id = {2,3,5,6} as those are the rows that have did in common with hid = 1.
I can do this by creating some arrays in PHP and running through all possible values of hid and respective did, but this is a quite slow process for large tables. I was wondering if there is a clever kind of JOIN or similar statement that could be used to find the cooccuring values of hid.
If I have understood you correctly:-
SELECT a.hid, GROUP_CONCAT(b.id)
FROM header a
INNER JOIN header b
ON a.did = b.did
AND b.hid != 1
WHERE a.hid = 1
GROUP BY a.hid
SQL fiddle:-
http://www.sqlfiddle.com/#!2/9aa26/1
Maybe this:
SELECT d.id
FROM (
SELECT *
FROM header
WHERE header.hid =1
) AS h
JOIN header AS d ON d.did = h.did
WHERE d.hid !=1

Can I specify the order in which the joins occur?

Say I have three tables, A, B and C. Conceptually A (optionally) has one B, and B (always) has one C.
Table A:
a_id
... other stuff
Table B:
a_fk_id (foreign key to A.a_id, unique, primary, not null)
c_fk_id (foreign key to C.c_id, not null)
... other stuff
Table C:
c_id
... other stuff
I want to select All records from A as well as their associated records from B and C if present. However, the B and C data must only occur in the result if both B and C are present.
I feel like I want to do:
SELECT *
FROM
A
LEFT JOIN B on A.a_id=B.a_fk_id
INNER JOIN C on B.c_fk_id=C.c_id
But Joins seem to be left associative (the first join happens before the second join), so this will not give records from A that don't have an entry in C.
AFAICT I must use sub queries, something along the lines of:
SELECT *
FROM
A
LEFT JOIN (
SELECT * FROM B INNER JOIN C ON B.c_fk_id=C.c_id
) as tmp ON A.id = tmp.a_fk_id
but once I have a couple of such relationships in a query (in reality I may have two or three nested), I'm worried both about code complexity and about the query optimizer.
Is there a way for me to specify the join order, other than this subquery method?
Thanks.
In SQL Server you can do
SELECT *
FROM a
LEFT JOIN b
INNER JOIN c
ON b.c_fk_id = c.c_id
ON a.id = b.a_fk_id
The position of the ON clause means that the LEFT JOIN on b logically happens last. As far as I know this is standard (claimed to be ANSI prescribed here) but I'm sure the downvotes will notify me if it doesn't work in MySQL!
Edit: And that's what I get for talking faster than I think. My previous solution doesn't work because 'c' hasn't been joined yet. Let's try this again.
We can use a WHERE clause to limit the results to only those that match the criteria you're looking for, where C has a valid (IS NOT NULL) or B does not have a value (IS NULL). Like this:
SELECT *
FROM a
LEFT JOIN b ON (b.a = a.a)
LEFT JOIN c ON (c.b = b.b)
WHERE (c.c IS NOT NULL OR b.b IS NULL);
Without WHERE Results:
mysql> SELECT * FROM a LEFT JOIN b ON (b.a = a.a) LEFT JOIN c ON (c.b = b.b);
+------+------+------+------+------+
| a | a | b | c | b |
+------+------+------+------+------+
| 1 | 1 | 1 | 1 | 1 |
| 1 | 1 | 2 | NULL | NULL |
| 2 | 2 | 3 | 2 | 3 |
| 3 | NULL | NULL | NULL | NULL |
| 4 | NULL | NULL | NULL | NULL |
+------+------+------+------+------+
With WHERE Results:
mysql> SELECT * FROM a LEFT JOIN b ON (b.a = a.a) LEFT JOIN c ON (c.b = b.b) WHERE (c.c IS NOT NULL OR b.b IS NULL);
+------+------+------+------+------+
| a | a | b | c | b |
+------+------+------+------+------+
| 1 | 1 | 1 | 1 | 1 |
| 2 | 2 | 3 | 2 | 3 |
| 3 | NULL | NULL | NULL | NULL |
| 4 | NULL | NULL | NULL | NULL |
+------+------+------+------+------+
Yes, you use the STRAIGHT_JOIN for this.
When using this keyword the join will occur in the exact order that you specify.
See: http://dev.mysql.com/doc/refman/5.5/en/join.html
Well, I thought up another solution as well, and I'm posting it for completeness (Though I'm actually using Martin's answer).
Use a RIGHT JOIN:
SELECT
*
FROM
b
INNER JOIN c ON b.c_fk_id = c.c_id
RIGHT JOIN a ON a.id = b.a_fk_id
I'm pretty sure every piece I've read about JOINS said that RIGHT JOINs were pointless, but there you are.