Aggregating strings across `group by` in SQL? - mysql

Consider the following schema:
create table A
(
id int primary id
)
create table B
(
a_id int,
s varchar(255)
)
And then the following query:
select A.id, sum(1), ??? concat_join(B.s) ???
from A left join B on A.id = B.a_id group by A.id
There is a 1-to-many relation between A and B, so multiple rows will be grouped into one. The desired behaviour of "concat_join" would be for each B.s in the group join them together into a single string by concatenating them (perhaps with a space seperator).
Is there someway to express this is MySQL 5.5?

use GROUP_CONCAT
select A.id, sum(1), GROUP_CONCAT(B.s)
from A left join B on A.id = B.a_id
group by A.id
by default, the string is separated by a comma. if you want to change it, add SEPARATOR keyword,
select A.id, sum(1), GROUP_CONCAT(B.s SEPARATOR ';')
from A left join B on A.id = B.a_id
group by A.id
SQLFiddle Demo

Related

How to use GROUP_CONCAT on multiple JOIN

I am currently retrieving data from multiple tables using a cus_id as the initial query. From here I have multiple tables that can have various rows of data that I would like to retrieve and use GROUP_CONCAT to return this data in a single row. At the moment, my query is returning duplicate data based on the max number of rows returned by one of the group concats.
SELECT a.id,c.x,c.y,c.z
GROUP_CONCAT(a.column_a) AS aca,
GROUP_CONCAT(a.column_b) AS acb,
GROUP_CONCAT(b.column_a) AS bca,
GROUP_CONCAT(b.column_b) AS bcb,
FROM `table_a` a
INNER JOIN `table_b` b ON a.id = b.id
INNER JOIN `table_c` c ON a.id = c.id
WHERE a.id = ?
Also, in this scenario, what is the correct join method to use. I am expecting all the fields I am requesting to have some sort of data.
Problem was resolved by using sub queries to isolate the GROUP_CONCAT requests. This allowed me to get only the data I wanted without duplicate results manipulated by other JOIN requests.
SELECT a.id,c.x,c.y,c.z
(SELECT GROUP_CONCAT(column_a) FROM table_a) AS aca,
(SELECT GROUP_CONCAT(column_b) FROM table_a) AS acb,
(SELECT GROUP_CONCAT(column_a) FROM table_b) AS bca,
(SELECT GROUP_CONCAT(column_b) FROM table_b) AS bcb,
FROM table_a a
INNER JOIN `table_c` c ON a.id = c.id
WHERE a.id = ?
Aggregate before joining. Somthing along the lines of:
select
a.*,
b.grp_a,
b.grp_b,
c.grp_x,
b.grp_y
from table_a a
join
(
select
a_id,
group_concat(a order by b_id) as grp_a,
group_concat(b order by b_id) as grp_b
from table_b
group by a_id
) b on b.a_id = a.id
join
(
select
a_id,
group_concat(x order by c_id) as grp_x,
group_concat(y order by c_id) as grp_y
from table_c
group by a_id
) c on c.a_id = a.a_id
order by a.a_id;

Left join is returning multiple rows from Table B corresponding to ID present in Table A

I have 2 tables in my SQL database. Let's call them Table A and Table B.
I have joined both the tables using left join on ID = AID. Table A has a field by name ID and Table B has field AID and Price.
The problem is Table B can have multiple prices for the ID present in Table A.
The requirement is - If only one price is present in Table B corresponding to ID in table A then it should return that price.
If more than one price is present in table B for an ID in table A then I should get the price as Zero/null.
Query -
SELECT DISTINCT A.ID,B.Price
from A
left join B
on A.ID = B.AID
where "some condition"
Use Count() with Over() window function to find the count of records for each ID then based on count return the price
Try this way
SELECT DISTINCT A.ID,
case when count(1) over(partition by A.ID) > 1 then NULL else B.Price end
from A
left join B
on A.ID = B.AID
For Mysql
SELECT DISTINCT a.id,
CASE
WHEN c.aid IS NULL THEN NULL
ELSE b.price
END
FROM a
LEFT JOIN b
ON a.id = b.aid
LEFT JOIN (SELECT aid
FROM b
GROUP BY aid
HAVING Count(1) = 1) c
ON a.id = c.aid
For SQL Server (should work for MySQL too). One sub-query (you can put in CTE) that shows only AIDs that have only one price:
SELECT A.ID,
B.Price
FROM A
LEFT JOIN (
SELECT AID
FROM B
GROUP BY AID
HAVING COUNT(Price) = 1
) as C
ON C.AID = A.ID
LEFT JOIN B
ON C.AID = B.AID
Use below query. It should work.
SELECT A.ID, o.Price
FROM A
OUTER APPLY
(
SELECT IIF(COUNT(B.Price)>1,NULL,MAX(B.Price)) AS Price
FROM B
WHERE B.AID = A.ID
) AS o;

MYSQL getting the difference of two results

ok so my problem is that I have a set of results:
ID CODE
1 A
1 B
3 C
I also have another table of:
CODE
A
B
C
What I want to get using SQL is a query of CODES that each result from table 1 is missing. So basically:
ID CODE
1 C
3 A
3 B
Any help would be great.
You can use:
SELECT a.id, b.code
FROM (SELECT DISTINCT id FROM idcodes) a
CROSS JOIN codes b
LEFT JOIN idcodes c ON a.id = c.id AND b.code = c.code
WHERE c.id IS NULL
ORDER BY a.id, b.code
If you have another table that stores unique entries for id, it would be better to just use that table instead rather than a DISTINCT subselect:
SELECT a.id, b.code
FROM ids a
CROSS JOIN codes b
LEFT JOIN idcodes c ON a.id = c.id AND b.code = c.code
WHERE c.id IS NULL
ORDER BY a.id, b.code
SQLFiddle Demo
You can use exists. Construct the full list of possible variations with a Cartesian join and then ensure what you have isn't in this list.
select id, code
from idcode_table x
where not exists ( select 1
from idcode_table a
cross join code_table b
where b.code = x.code
and a.id = x.id )
This can also be re-written with a not in.
select id, code
from idcode_table
where (id, code) not in ( select distinct a.id, b.code
from idcode_table a
cross join code_table b )
The distinct is optional. It will make it slower to construct the list of possibles but quicker to determine if you have one of them already. I'd test it to see which is quicker.

MySQL - Use select field value in nested SQL statement of the same SQL

I have the following SQL query
SELECT
a.id AS ID, a.title,
(
SELECT Group_Concat( title )
FROM (
SELECT title
FROM `table_b` AS b
JOIN table_c ON c.id = b.id
WHERE b.id = ID
UNION
SELECT title
FROM `table_b` AS b
JOIN table_c ON c.id = b.id
WHERE b.another_id = ID
) AS other_titles
FROM table_a
However it is not working and is having a problem with the parts WHERE b.id = ID and WHERE b.another_id = ID in the nested SQL part.
How can I use the ID from the first select (SELECT a.id AS ID) in the nested select?
I think what you have here is called correlated subquery. It looks quite promising and seems to only lack a couple of final strokes (highlighted in bold italic):
SELECT
a.id AS ID,
a.title,
(
SELECT Group_Concat( title )
FROM (
SELECT title
FROM `table_b` AS b
JOIN table_c ON c.id = b.id
WHERE b.id = a.ID
UNION
SELECT title
FROM `table_b` AS b
JOIN table_c ON c.id = b.id
WHERE b.another_id = a.ID
) AS other_titles
FROM table_a AS a
Keep in mind that what you are actually referencing in the subquery is a.id, not the ID alias. (You can't reference the alias there.)
If a.id is a complex expression and you are not very happy about repeating it in the subquery, then you might need to restructure the entire query, maybe like this:
SELECT
x.ID,
x.title,
(
SELECT Group_Concat( title )
FROM (
SELECT title
FROM `table_b` AS b
JOIN table_c ON c.id = b.id
WHERE b.id = x.ID
UNION
SELECT title
FROM `table_b` AS b
JOIN table_c ON c.id = b.id
WHERE b.another_id = x.ID
) AS other_titles
FROM (
SELECT
a.id AS ID,
a.title,
FROM table_a AS a
) x
You cannot select values into the FROM clause. You may only use variables to dynamically set the table name.

Opposite of INNER JOIN with EXIST requirement

If inner join requires that a row exists, what's the opposite of it without having to do a sub query of NOT EXISTS?
I replaced
AND NOT EXISTS (
SELECT
*
FROM topic_read_assoc
WHERE topic_id = topic.id
AND member_id = ".$this->tru->application->currentMember->getId()."
)
with
OUTER JOIN topic_read_assoc ON (
topic_read_assoc.topic_id = topic.id AND
member_id = member_id = ".$this->tru->application->currentMember->getId()."
)
and it's not producing the same results as the first query (which works)
OUTER JOIN with a WHERE field IS NULL
Example:
SELECT A.name FROM A INNER JOIN B on A.id = B.id
Select those names in A whose id fields exist in B
Opposite:
SELECT A.name FROM A OUTER JOIN B on A.id = B.id WHERE B.id IS NULL
Select those names in A whose id fields do not exist in B
i think select on outer join is slow, because dbms left join first,then right join and delete the repeated rows.So I suggest you to select on the left join,then right join,make a intersect.It is better not operate on any join,because the view doesnt have index.