SQL: Joining results of 2 select queries into 1 row - mysql

I have this query that will work if there is data in both tables
SELECT a.location, b.location, a.refno, b.note
FROM (
SELECT location, refno
FROM tableA WHERE refno = '1234'
) a, (
SELECT location, note FROM tableB WHERE note = LN1234567
) b
but some of the time there may not be data in either one of the tables for the specific match in the WHERE clauses
I've also tried this which does work but i need the data on one row
SELECT location, refno
FROM tableA
WHERE refno = '1234'
UNION
SELECT location, note
FROM tableB
WHERE note = 'LN1234567'
My question is, is there an alternative way of querying both tables so I get one row with data from either OR both tables?

You can try with:
SELECT MAX(location_a) AS location_a,
MAX(refno_a) AS refno_a,
MAX(location_b) AS location_b,
MAX(refno_b) AS refno_b
FROM (
SELECT location AS location_a,
refno AS refno_a,
NULL AS location_b,
NULL AS refno_b
FROM tableA
WHERE refno = '1234'
UNION ALL
SELECT NULL AS location_a,
NULL AS refno_a,
location AS location_b,
location AS refno_b
FROM tableB
WHERE note = 'LN1234567') s

Assuming you want matching locations on both side, you want a left join. Here is a simplified version:
SELECT a.location, b.location, a.refno, b.note
FROM tableA a LEFT JOIN
tableB b
on a.location = b.location
WHERE a.refno = '1234' and b.note = 'LN1234567';
If you actually want a cross join (different locations on the same row) and still want results, I think you need a union all:
SELECT a.location, b.location, a.refno, b.note
FROM tableA a CROSS JOIN
tableB b
WHERE a.refno = '1234' and b.note = 'LN1234567'
UNION ALL
SELECT a.location, NULL, a.refno, NULL
FROM tableA
WHERE NOT EXISTS (SELECT 1 FROM tableB b WHERE b.note = 'LN1234567');

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 re-using sums of subqueries

I have the following simplified query:
Select
(select sum(f1) from tableA a where a.id = t.id) sum1,
(select sum(f2) from tableB b where b.id = t.id) sum2,
t.*
from Table t;
My wish is to have sum1 and sum2 re-used without calculating them again:
Select
(select sum(f1) from tableA a where a.id = t.id) sum1,
(select sum(f2) from tableB b where b.id = t.id) sum2,
sum1 + sum2 `sum3`,
t.*
from Table t;
Of course i can do the following query but this will unnecessary double the run time:
Select
(select sum(f1) from tableA a where a.id = t.id) sum1,
(select sum(f2) from tableB b where b.id = t.id) sum2,
(select sum(f1) from tableA a where a.id = t.id) +
(select sum(f2) from tableB b where b.id = t.id) `sum3`,
t.*
from Table t;
or even inserting the sum1 and sum2 results to a temporary table but can't imaging i'm overlooking something to have mysql do some efficient querying on summed fields.
Is there a better, more efficient way to re-use summed fields?
Try running this query,
select Resutl.*,Result.sum1+Result.sum2 as sum3 from(
SELECT (SELECT SUM(f1) FROM tableA a WHERE a.id = t.id) sum1,
(SELECT SUM(f2) FROM tableB b WHERE b.id = t.id) sum2,
t.*
FROM Table t)Result
)
Hope it will help.
My wish is to have sum1 and sum2 re-used without calculating them again
Whoa, steady on. The original query is not as efficient as it might be. Without knowing what the data distribution looks like its hard to advise what the most appropriate query is, but assuming that the tuples in A and B have a foreign key constraint (implicit or explicit) on table T but no implicit foreign key restraint on each other, that t.id is unique, and most of the rows in tableT have corresponding rows in tableA and tableB then....
SELECT t.*, sum_f1, sum_f2
FROM tableT t
LEFT JOIN (SELECT a.id, SUM(f1) AS sum_f1
FROM tableA a
GROUP BY a.id) AS a_agg
ON t.id=a_agg.id
LEFT JOIN (SELECT b.id, SUM(f2) AS sum_f2
FROM tableB b
GROUP BY b.id) as b_agg
ON t.id=b_agg.id
GROUP BY t.id
Will be much more efficient.
(this also assumes that ONLY_FULL_GROUP_BY is disabled - otherwise you'll need to replace 't.*' in the SELECT clause and 't.id' in the group by clause with each of the attributes you need from the table).
Note that in practice you're rarely going to be looking at allyour data in a single query. Since MySQL doesn't handle push predicates very well, simply adding a filter in the outer SELECT probably won't be the optimal solution.
Once you've structured your query as above, it's trivial to add the sums:
SELECT t.*, sum_f1, sum_f2,
IFNULL(sum_f1,0)+INFULL(sum_f2,0) AS sum3
FROM tableT t
LEFT JOIN (SELECT a.id, SUM(f1) AS sum_f1
FROM tableA a
GROUP BY a.id) AS a_agg
ON t.id=a_agg.id
LEFT JOIN (SELECT b.id, SUM(f2) AS sum_f2
FROM tableB b
GROUP BY b.id) as b_agg
ON t.id=b_agg.id
GROUP BY t.id
(but note that I've made a lot of assumptions about your data and schema)

mysql display field values from subquery

I have following query that works correctly:
select * from myTable a where a.company is null and exists (select b.company from myTable b where b.id = a.id and b.office_id = a.office_id and b.company is not null);
Now, I also want to display the field value b.company from the subquery next to the fields from myTable a.
How do I get this done?
Thank you and best regards
If you want results from multiple tables you should join the tables together.
Since you want only records from A that exist in B, you need to use an outer JOIN returning all records from A and only those matching in B. But then you want to exclude all those records from A that were not found in B.
SELECT *, b.company
FROM myTable a
LEFT JOIN myTable B
ON b.id = a.id
and b.office_id = a.office_id
and b.company_ID is not null
WHERE a.company is null
and B.ID is not null and B.office_ID is not null --this handles the exists part.

mySQL: Multi-column join on several tables part II

I am adding a 5th table to an existing join. The original query will always return a single row because the where clause specifies a unique ID. Here are the tables we are using:
Table 1
carid, catid, makeid, modelid, caryear
Table 2
makeid, makename
Table 3
modelid, modelname
Table 4
catid, catname
Table 5
id, caryear, makename, modelname
Here is the existing query I am using:
SELECT a.*, e.citympg, e.hwympg
FROM table1 a
JOIN table2 b on a.makeid=b.makeid
JOIN table3 c on a.modelid=c.modelid
JOIN table4 d on a.catid=d.catid
JOIN table5 e on b.makename = e.make
and c.modelname = e.model
and a.caryear = e.year
WHERE a.carid = $carid;
There are 2 issues that I need to solve -
When there is no match on table 5, it does not return any results. It would seem that I need to do some sort of left join or split the query and do a union.
When there is a match on table 5, it returns multiple rows. Since the criteria that would return a single row is not being used, I would settle for an average of citympg and hwympg.
Can both objectives be achieved with a single query? How?
Assuming I understand what you want correctly... This query will constrain the results from table5 to one row per combination of the join criteria, returning average city/hwy mpg.
SELECT a.*, e.citympg, e.hwympg
FROM table1 a
JOIN table2 b on a.makeid=b.makeid
JOIN table3 c on a.modelid=c.modelid
JOIN table4 d on a.catid=d.catid
LEFT JOIN (SELECT year, make, model,
AVG(citympg) as citympg,
AVG(hwympg) as hwympg
FROM table5
GROUP BY year, make, model) e on b.makename = e.make
and c.modelname = e.model
and a.caryear = e.year
WHERE a.carid = $carid;
Note that it will return NULL mpg values when no record in table5 exists.
The usual approach is to use correlated subqueries like this:
SELECT a.*
, (SELECT avg(e.citympg)
FROM table5 e
WHERE e.make = b.makename
AND e.model = c.modelname
AND e.year = a.caryear
) as citympg
, (SELECT avg(e.hwympg)
FROM table5 e
WHERE e.make = b.makename
AND e.model = c.modelname
AND e.year = a.caryear
) as hwympg
FROM table1 a
JOIN table2 b on a.makeid=b.makeid
JOIN table3 c on a.modelid=c.modelid
JOIN table4 d on a.catid=d.catid
WHERE a.carid = $carid