Reduce rows in results of joined query - mysql

Is it possible to reduce the number of rows returned from a joined query by having results from individual tables returned in some array-like container
E.g. for a query:
SELECT a.col, b.col, c.col FROM
a INNER JOIN b ON a.id = b.id
INNER JOIN c ON a.id = c.id
where it is known a.id uniquely matches 1 row in a, b.id matches 2 rows in b and c.id matches 3 rows in c, the results would then be
+-------+-------+-------+
| a.col | b.col | c.col |
+-------+-------+-------+
| A0 | B1 | C1 |
| A0 | B2 | C1 |
| A0 | B1 | C2 |
| A0 | B2 | C2 |
| A0 | B1 | C3 |
| A0 | B2 | C3 |
+-------+-------+-------+
6 rows in set
So question is if it some how would be possible to reduce output to something like (or similar)
+-------+----------+--------------+
| A.col | b.... | c.... |
+-------+----------+--------------+
| A0 | [B1, B2] | [C1, C2, C3] |
+-------+----------+--------------+
Perhaps instead of array-like structure form strings (array like to me...), e.g. "B1,B2", "C1,C2,C3"
Note, can't use DISTINCT as b.col may contain identical values.
What if one wishes more than one column from table b, e.g.
SELECT a.col, b.col, b.col2, c.col FROM
a INNER JOIN b ON a.id = b.id
INNER JOIN c ON a.id = c.id
```
how to get a result on form (or similar)
```
+-------+------------------------+--------------+
| A.col | b.... | c.... |
+-------+------------------------+--------------+
| A0 | [[B1, B11], [B2, B21]] | [C1, C2, C3] |
+-------+------------------------+--------------+
```
Would it perhaps be possible/wise to prior to `JOIN` table a and b create a temporary table btmp with the desired output (as a string or similar).
At the end of the day I wish to access a mysql data base remotely over a line that sometimes may be quite bad, so wish to reduce the amount of data sent as far as possible without loosing required information.

This is a job for GROUP_CONCAT(). It doesn;t create arrays of values, but it does create delimited string lists. MySQL doesn't of course, have array-valued columns either in tables or result sets, so you're stuck with strings.
I believe this will do something close to what you want.
SELECT a.col,
GROUP_CONCAT(DISTINCT b.col) b
GROUP_CONCAT(DISTINCT c.col) c
FROM a
INNER JOIN b ON a.id = b.id
INNER JOIN c ON a.id = c.id -- you had a.id = b.id but that won't work.
GROUP BY a.col

Related

left join and count doesn't work properly in MYSQL

I have two tables named A and B, here you can See A and B
A | id | title
-------------------------
1 | A1
2 | A2
3 | A3
B | A_id | title
-------------------------
1 | B1
1 | B2
1 | B3
what i want to do is to count The number of A_id's in B, i've tried the following query :
select A.title as "title",count(title) as "count" from A left join B on A.id = B.A_id
This will give me the following result :
title | count
---------------
A1 | 3
A2 | 1
A3 | 1
A4 | 1
what i need to have is :
title | count
---------------
A1 | 3
A2 | 0
A3 | 0
A4 | 0
Issues with your query:
You query is missing a GROUP BY clause. You seem to be using a version of MySQL where option ONLY_FULL_GROUP_BY is disabled, so, instead of throwing a proper error at you, MySQL happily executes your query.
count(title) is ambiguous, since the column exists in both tables. This should generate an error. You actually want to count on a column that is coming from table B; I would use B.A_id, just in case some records in B have an null title.
Consider:
select A.title, count(B.A_id) as `count`
from A
left join B on A.id = B.A_id
group by A.title
Side notes:
in MySQL, I would suggest using backticks for identifiers rather than double quotes; that's the MySQL way does it
A.title as title is redondant: it is equivalent to just A.title
try like below by adding B.A_id inside count
select A.title as "title",count(B.A_id) as "count"
from A left join B on A.id = B.A_id
group by A.title

MySQL select all left entries from a table which is joined from another table

I have the following MySQL-Statement:
SELECT norm.NormID, norm.NormName
FROM (assignment
INNER JOIN norm
ON assignment.NID = norm.NormID )
INNER JOIN wire
ON assignment.LID = wire.WireID
WHERE wire.WireID= 109
ORDER BY norm.NormName;
Now what I got are the entries from the table assignment with the NormID and NormName for that WireID.
What I want to get are the entries from the table norm, which are not setted for this WireID.
E.g.:
WireID has the norm assignment A, B, D, G.
The table norm has the entries A, B, C, D, E, F, G, H.
What I want to get from the MySQL-Statment are the entries C, E, F, H.
How can I select those left norm entries for this WireID?
With the above statement I would get:
-----------------------
| NormID | NormName |
-----------------------
| 1 | A |
| 2 | B |
| 4 | D |
| 7 | G |
-----------------------
I want to have this Table:
-----------------------
| NormID | NormName |
-----------------------
| 3 | C |
| 5 | E |
| 6 | F |
| 8 | H |
-----------------------
I think (if I understood what you asked) you can try this :
SELECT norm.NormID, norm.NormName
FROM assignment
INNER JOIN norm ON assignment.NID = norm.NormID
LEFT JOIN wire ON assignment.LID = wire.WireID
WHERE assignment.LID= 109
AND wire.wireID IS NULL
ORDER BY norm.NormName;
Edit after your comments.
I think you could use:
SELECT A.NormID, A.NormName
FROM norm A
LEFT JOIN (SELECT NID FROM assignment WHERE LID = 109) B ON B.NID = A.NormID
WHERE B.NID IS NULL
ORDER BY A.NormName;
OR
SELECT A.NormID, A.NormName
FROM norm A
WHERE NOT EXISTS (SELECT 1 FROM assignment WHERE LID = 109 AND ASSIGNMENT.NID = A.NormID)
ORDER BY A.NormName;
Try this:
select norm.NormID,norm.NormName from norm
Inner JOIN
assignment on assignment.NID = norm.NormID
where assignment.LID in(select wireID from Wire where WireID = 109)
Im not so sure coz i dont have your data
after you added a sample data the entries that are not setted with 109 wireid are these:
SELECT norm.NormID, norm.NormName
FROM assignment
inner JOIN norm
ON assignment.NID = norm.NormID
INNER JOIN wire
ON assignment.LID = wire.WireID
WHERE wire.WireID <> 109
ORDER BY norm.NormName;

How do I inner join multiple tables?

I have tables A, B and C and I want to get matching values for from all tables (tables have different columns).
Table A (primary key = id)
+------+-------+
| id | name |
+------+-------+
| 1 | Ruby |
| 2 | Java |
| 3 | JRuby |
+------+-------+
Table B (pid is reference to A(id) - No primary key)
+------+------------+
| pid | name |
+------+------------+
| 1 | Table B |
+------+------------+
Table C (primary key = id, pid is reference to A(id))
+------+------+------------+
| id | pid | name |
+------+------+------------+
| 1 | 2 | Table C |
+------+------+------------+
So my below query returned nothing. Whats wrong here? Is it treated as AND when multiple inner joins present?
Select A.* from A
inner join B ON a.id = b.pid
inner join C ON a.id = c.pid;
As you first join
1 | Ruby | Table B
and then try to join Table C, there is no match for pid 2 in the aforementioned result, the result is therefore empty.
An inner join excludes everything that doesn't match. So after you joined against B, you were left with only one record (id=1). Your inner join against C doesn't have any matches from what's left, so you get nothing.
I suppose a union would do the trick:
select A.* from A join B on a.id = b.pid
union
select A.* from A join C on a.id = c.pid
Or there are other ways, like where a.id in (select pid from b) or a.id in (select pid from c)
When you inner-join like this, a single row from A needs to exist such that a.id = b.pid AND a.id = c.pid are true at the same time. If you examine the rows in your examples, you would find that there is a row in A for each individual condition, but no rows satisfy both conditions at once. That is why you get nothing back: the row that satisfies a.id = b.pid does not satisfy a.id = c.pid, and vice versa.
You could use an outer join to produce two results:
select *
from A
left outer join B ON a.id = b.pid
left outer join C ON a.id = c.pid;
a.id a.name b.pid b.name c.id c.pid c.name
1 | Ruby | 1 | Table B | NULL | NULL | NULL
2 | Java | NULL | NULL | 1 | 2 | Table C
Of course you return nothing!
Table A and B inner join returns 1st record of Table A (table A ID = 1)
then you join table C, there is no matching rows to join table C, and vice versa.

Select unrelated ids on relate / lookup table

I have seen various examples for this, but I can't find a definitive example of how to return all rows from table_A that are not related to a row in table_B using relate table_A_B without a given value for either table_A.id or table_B.id. The closest matchs I can find to this are:
Get a list of A not related to one or more rows in B but without knowing which value in B is the issue.
Get a list of A not related to given B.
Get list of A with comma-separated field of unrelated B (not sure I saw that, but it seemed like one example could expand as such).
A list of As and Bs not related to each other but no indication of which is not related to the other.
I can also get a list of all potential A_B tuples with:
SELECT A.id, B.id FROM A
INNER JOIN B ON A.id <> B.id
And I could hypothetically use one of the EXCEPT workarounds (I think) against the relate table, but all attempts were unsuccessful and I imagine that once there are millions of potential combinations returned by that join it will be much less efficient anyway.
So given the table values:
A
id | name
1 | X
2 | y
3 | z
B
id | name
7 | e
8 | f
9 | g
A_B
id | a_id | b_id
1 | 1 | 7
2 | 1 | 8
3 | 1 | 9
1 | 2 | 7
2 | 2 | 8
1 | 3 | 7
Is there a query that would return:
A | B
2 | 9
3 | 8
3 | 9
Or even better:
A | B
y | g
z | f
z | g
Or is this asking for trouble?
Start with a cross join between A and B to get all possible pairs. Then do a left join to the relation table and choose where there is no match:
select driver.aID, driver.bID
from (select a.id as aID, b.id as bID
from table_A A cross join table_B B
) driver left outer join
table_A_B ab
on ab.aID = driver.aID and ab.bID = bID
where ab.aID is null
This works, assuming that the ids are never NULL.
I haven't tested the SQL so it might have syntax errors.
This version gets you the names:
select driver.aName, driver.bName
from (select a.id as aID, b.id as bID, a.name as aName, b.name as Bname
from table_A A cross join table_B B
) driver left outer join
table_A_B ab
on ab.aID = driver.aID and ab.bID = bID
where ab.aID is null

How to create left joins without repetition of rows on the left side

I have a scenario where there are two tables (tables A and B) linked in a one to many relationship. For a row in table A, the maximum number of linked rows in B is two, and these two rows (if they exist) are different from each other through a type column whose value is either x or y.
Aid | Name Bid | type | Aid
1 | name1 1 | x | 1
2 | name2 2 | x | 2
3 | name3 3 | y | 2
Now, what I want is to have a join query for the two tables in such a way that all rows in A will be displayed (no repetition) and two columns called type x and type y will hold a boolean / integer value to show the existence of types x and y for each row in A. i.e,
Aid | Name | Type X | Type Y |
1 | name1 | X | NULL |
2 | name2 | X | Y |
3 | name3 | NULL | NULL |
My DBMS is MySql.
Thanks.
You have to use two joins:
SELECT A.*, b1.type AS typeX, b2.type as typeY
FROM A
LEFT JOIN B b1
ON A.aid = b1.aid
AND b1.type = 'x'
LEFT JOIN B b2
ON a.aid = b2.aid
AND b2.type = 'y'
Well, this happens because your second table uses the EAV-model. If you had two tables, one for type_x and one for type_y, your relational schema would be a lot cleaner.
Offcourse, EAV does work, be it more clumsily:
SELECT a.aid, a.name, bx.type, by.type
FROM table_a a
LEFT JOIN table_b bx
ON a.aid = bx.aid
AND bx.type = 'x'
LEFT JOIN table_b by
ON a.aid = by.aid
AND by.type = 'y'