If I have a table like this:
TABLEA:
id| object | type
1 | greg | person
2 | mary | person
3 | jared | person
4 | kelly | person
5 | melissa | person
6 | william | person
7 | skiing | hobby
8 | biking | hobby
TABLEB:
id | husband | wife
1 | greg | mary
2 | jared | kelly
3 | william | kelly
TABLEC:
id | female | hobby
1 | mary | skiing
2 | kelly | biking
Is there some way I could get a result table of:
TABLED:
id | a | b | link
1 | 1 | 2 | related
2 | 3 | 4 | related
3 | 6 | 4 | related
4 | 1 | 4 | related
5 | 2 | 7 | likes
6 | 4 | 8 | likes
Using only MySQL query(/ies)?
Logic would basically start iterating from TABLE B and all rows of table B.
The third column is related when the table being selected is TableB, and is likes when the table being processed is TABLE B.
Sample logic would be:
Looking at the first row of TABLEB (husband) greg and wife (mary), it looks up from TABLEA to see that greg is at row 1 (id 1) and mary is at row 2 (id 2) and creates a new TableD with the first row being 1 | 2.
Is there a query that can do some join or something that would be able to do this without having to programmatically iterate through all rows of TABLEB, then all rows of TABLEC to produce the desired TABLED?
You will need to JOIN the table tablea two times with the tableb:
one for husband > object and another,
one for wife > object.
So that you can get the two ids of the husband and wife in the same row.
The same with Tablec with tablea. Then use UNION(implicit distinct) or UNION ALL to union the two result sets.
Something like:
SELECT
(#rownum := #rownum + 1) AS id,
sub.*
FROM
(
SELECT
ah.id AS a,
aw.id AS b,
'related' AS link
FROM tableb AS b
INNER JOIN Tablea AS ah ON ah.object = b.husband AND ah.type = 'person'
INNER JOIN Tablea AS aw ON aw.object = b.wife AND aw.type = 'person'
UNION ALL
SELECT
a.id AS a,
a2.id AS b,
'hobby'
FROM tablec AS c
INNER JOIN tablea AS a ON a.object = c.female
INNER JOIN tablea AS a2 ON a2.object = c.hobby AND a2.type = 'hobby'
) AS Sub, (SELECT #rownum := 0) AS t;
SQL Fiddle Demo
This will give you:
| ID | A | B | LINK |
------------------------
| 1 | 1 | 2 | related |
| 2 | 3 | 4 | related |
| 3 | 6 | 4 | related |
| 4 | 2 | 7 | hobby |
| 5 | 4 | 8 | hobby |
Note that:
This query will give you only 5 rows, where the expected result set that you are looking for is 6. It is missing the row 4 | 1 | 4 | related, because there is no entry for those greg(id 1) and keyll (id 4) in the tableb. As you explained in your question.
The new column id is auto incremental id, generated from the result set, it is not selected from the tables.
If you need to create a brand new table from this select, use the following syntax:
CREATE TABLE Tabled
AS
SELECT ...
... -- the same previous select query
and you will have a new table tabled having the same structure of this SELECT.
Like in this updated fiddle.
create table tabled (
id int auto_increment,
a varchar(10),
b varchar(10),
link varchar(10));
insert into tabled (a, b, link)
select a1.id a, a2.id b, 'related' link
from tablea a1
join tableb b on a1.object = b.husband and a1.type = 'person'
join tablea a2 on a2.object = b.wife and a2.type = 'person'
union all
select a1.id a, a2.id b, 'likes' link
from tablea a1
join tablec c on a1.object = c.female and a1.type = 'person'
join tablea a2 on a2.object = c.hobby and a2.type = 'hobby';
Related
i have 3 tables: a,b and c. table a is related with table b with a one to many relaion and table c is the table that get the triggered infromation from the 2 other tables.
table a
------------------
id | name
1 | Juan
2 | Pete
3 | David
table b
----------------------
id | name | table_a_id
1 | Dog | 1
2 | Cat | 2
Table C gets the information from Table A with a "after insert on A
for each row insert into C "also gets information From table B with a "after insert on B for each row update C".
table c
----------------------
id | name a | name b | table_a_id |table_b_id
1 | juan | Dog | 1 | 1
2 | Pete | Cat | 2 | 2
The problem occurs when Pete wants to have Cat and Dog at the same time because it will just update Pete's information. What i want is a way in which i can have a new row with all the information.
table c
-------------------------------------------
id | name a | name b | table_a_id |table_b_id
1 | juan | Dog | 1 | 1
2 | Pete | Cat | 2 | 2
---->3 | Pete | Dog | 2 | 1
This 3rd row from table C wont be created it will just update row 2 from table C.
instead of having a physical copy of table c, you should create a "virtual table", or a view.
For example:
CREATE VIEW table_c AS (
SELECT
t1.id AS a_id,
t1.`name` AS a_name,
t2.id AS b_id,
t2.`name` AS b_name
FROM table_a t1
LEFT JOIN table_b t2
ON (t2.table_a_id = t1.id)
)
Now you can fetch rows from this "table" like this:
SELECT * FROM table_c
I have a table A that contains tree columns, id, users ids and vehicle id. And a table B that contains vehicleid, and vehicle name.
Table A
---------------------------
| Id | User_id |Vehicle_id|
---------------------------
| 1 | 1 | 2 |
| 2 | 1 | 3 |
| 3 | 1 | 4 |
| 4 | 2 | 2 |
| 5 | 2 | 3 |
| 6 | 4 | 5 |
---------------------------
Table B
-------------------
| Id |Vehicle_name|
-------------------
| 1 | Car |
| 2 | Bike |
| 3 | Plane |
| 4 | Boat |
| 5 | Rocket |
-------------------
Given a user id, I need to get all vehicle names, that doesn't match with table A. I've tried Outer joins, but I can't manage to do get the info that i need.
For example: Given user id 1, the query should return Car and Rocket.
thanks in advance
This is simple enough using not in or not exists:
select b.*
from b
where not exists (select 1
from a
where a.vehicle_id = b.id and a.user_id = #a_user_id
);
I also thought of using a cross join and was able to get the output in case you are more comfortable with join logic.
SELECT CJOIN.USER_ID, CJOIN.VEHICLE_ID, CJOIN.VEHICLE_NAME
FROM
(SELECT DISTINCT A.USER_ID, B.ID AS VEHICLE_ID, B.VEHICLE_NAME FROM TABLE_A A CROSS JOIN TABLE_B B) CJOIN
LEFT JOIN
TABLE_A D
ON CJOIN.USER_ID = D.USER_ID AND CJOIN.VEHICLE_ID = D.VEHICLE_ID
WHERE D.USER_ID IS NULL AND D.VEHICLE_ID IS NULL;
First, I got all possible combinations of USER_ID x VEHICLE_ID by a cross join and used this table in a left join to pull records for which there is no match.
I have three tables
Table a
+-----+-------+
| aid | value |
+-----+-------+
| 1 | A |
| 2 | B |
| 3 | C |
| 4 | D |
+-----+-------+
Table b
+-----+------+
| bid | name |
+-----+------+
| 1 | A |
| 2 | B |
| 3 | C |
+-----+------+
Table ba (mapping of table a and table b)
+-----+-----+
| bid | aid |
+-----+-----+
| 1 | 1 |
| 2 | 1 |
| 3 | 1 |
| 3 | 2 |
| 1 | 3 |
| 2 | 3 |
| 2 | 4 |
+-----+-----+
From these tables I want a query like
SELECT aid, mapped('true'-if(aid exist in ba) 'false'-otherwise)
FROM a
JOIN b
JOIN ba
WHERE bid=1
to get a result from where I can generate a list
(when bid=1)
A-mapped
B-not mapped
C-mapped
D-not mapped
(when bid=2)
A-mapped
B-not mapped
C-mapped
D-mapped
(when bid=3)
A-mapped
B-mapped
C-not mapped
D-not mapped
Right now I am generating the list in a while loop for all the rows of table 'a' and inside the loop a query is executed for each iteration to check the existence in table 'ba'.
I think this is supposed to be table b independent:
SELECT CONCAT_WS('-', a.value, IF(ba.aid IS NULL, "-not mapped", "-mapped"))
FROM a LEFT JOIN ba ON a.aid = ba.aid AND ba.bid = 1
ORDER BY a.aid
Note: I took "a" table as the base table since your samples included all values from "a" table.
This is a tricky question, but the difficult part is in figuring out how to formulate the query. Once that is out of the way, it is downhill from there. One approach is to use a cross join between the A and B tables to obtain all possible mappings. Then LEFT JOIN to the mapping table to determine which pairs are being mapped and which are not. Try the following query:
SELECT tb.bid, ta.value,
CASE WHEN ba.bid IS NOT NULL THEN 'mapped' ELSE 'not mapped' END AS label
FROM tb INNER JOIN ta -- cross join to obtain all bid/aid pairs
LEFT JOIN ba -- to determine which pairs are mapped/not mapped
ON ta.aid = ba.aid AND tb.bid = ba.bid
ORDER BY tb.bid, ta.value
Demo here:
SQLFiddle
I have three tables, I need to select email and count the relation between table A and table B, for example:
Table A:
ID | email
1 | test#test
2 | test2#test
3 | test3#test
Table B:
UID | username
11 | James
22 | Gabriel
33 | Jonas
Table C: (Relation between A and B)
ID | email_id | username_id
1 | 1 | 11
2 | 1 | 22
3 | 2 | 33
Expect Result:
Email | Totalrelation
test#test | 2
test2#test | 1
I tried:
select tableA.email,
COUNT(distinct tableC.email_id) AS total from tableA as tableA, tableC as tableC GROUP BY tableC.email_id
But it didnt work, I got total wrong. How can I do it?
Join the tables, group the data and count the number for each group
select a.email, count(c.id) as cnt
from tableA a
left join tableC c on c.email_id = a.id
group by a.email
I have table A and B with a relationship: A n<->1 B relationship.
They are joined over field A.b = B.id, where B.id is unique
I have a parameter which is a bunch of ids of B.
I want to get distinct A.id that have all given B.ids assigned.
Example:
Table B
| id | ...
| 1 |
| 2 |
| 3 |
Table A
| id | b | ...
| 1 | 1 |
| 1 | 2 |
| 1 | 3 |
| 2 | 1 |
| 2 | 2 |
<-- id=2 is not assigned to b=3 !
| 3 | 1 |
| 3 | 2 |
| 3 | 3 |
Expected result for parameter B.ids="1,2,3": 1, 3 (2 misses the required B.id=3)
How can I do this?
You can do this with aggregation and a having clause:
select id
from tableA a join
tableB b
on a.b = b.id
group by id
having count(distinct b) = (select count(distinct b) from tableB);
Note that this can possibly be simplified with some assumptions. For instance, if you know the b ids are unique, then you don't need the count(distinct) (count() is then sufficient.)
EDIT:
If you want a list of ids that you want to check, you can use:
select id
from tableA a
where find_in_set(a.b, IDLISTHERE) > 0
group by id
having count(distinct b) = (select count(distinct b) from tableB where find_in_set(a.b, IDLISTHERE) > 0);
select id from tableA a join tableB b on a.b = b.id
group by id
having count(distinct b) = (select count(distinct b) from tableB);