I am creating a new table from joining two tables through below query:
select a.name, a.number, b.id, b.sub_num
from tableA a left join
tableB b
on a.number = concat(id,'-',Cast(sub_num as varchar);
Here, I want to add a new column into the new table which is select query result on tableA.
The table data would be something like below:
enter image description here
I am trying to use below query which is not correct as it is giving me multiple rows. I need sum of dext_number for condition dext_id = 17501 and group by of name and number columns.
select a.name, a.number, b.id, b.sub_num,
(select sum(dext_number) from tableA where dext_id = 17501 group by name, number) as newcol
from tableA a left join tableB b
on a.number = concat(id,'-',Cast(sub_num as varchar);
What is the best way to add this column here?
use following query
select a.name, a.number, b.id, b.sub_num, (select sum(dext_number) over(partition by name, number) from tableA
where dext_id = 'xyz' limit 1) as newcol
from tableA a
left join tableB b on a.number = concat(id,'-',Cast(sub_num as varchar);
If you want the exact output of the query image, use the following query
select L.name,L.number,L.dext_id,L.dext_number,
case when L.num > 1 then newcol
else NULL
end as newcol
from
(select *,
count(dext_number) over(partition by name, number) as num,
sum(dext_number) over(partition by name, number) as newcol
from tableA) L left join tableA R on L.dext_id = R.dext_id and L.name= R.name and L.number = R.number and L.dext_number = R.dext_number
And the combination of the first query with tableB will be as follows
select a.name, a.number, b.id, b.sub_num, (select
case when L.num > 1 then newcol
else NULL
end as newcol
from
(select *,
count(dext_number) over(partition by name, number) as num,
sum(dext_number) over(partition by name, number) as newcol
from Test) L left join Test R on L.dext_id = R.dext_id and L.name= R.name and L.number = R.number and L.dext_number = R.dext_number limit 1) as newcol
from tableA a
left join tableB b on a.number = concat(id,'-',Cast(sub_num as varchar);
Related
I have two tables (Table A & Table B) in which a single parent row in 'Table A' will have multiple rows in 'Table B'. I have to retrieve rows from 'Table A', only if all the child rows in 'Table B' satisfies a WHERE clause.
Table A
id INT
name VARCHAR
gender VARCHAR
Table B
id INT
table_A_id INT
condition INT
Now I have to fetch the rows of 'Table A' for those which all the child rows in 'Table B' satisfies the WHERE clause 'condition=100'.
This query:
select table_A_id
from tableb
group by table_A_id
having sum(case when condition = 100 then 1 else 0 end) = count(*)
returns all the table_A_ids in tableb that meet the condition.
You can use it with IN like this:
select *
from tablea
where id in (
select table_A_id
from tableb
group by table_A_id
having sum(case when condition = 100 then 1 else 0 end) = count(*)
)
Or you can join the subquery:
select a.*
from tablea a inner join (
select table_A_id
from tableb
group by table_A_id
having sum(case when condition = 100 then 1 else 0 end) = count(*)
) b on b.table_A_id = a.id
Note that for MySql the HAVING clause can be simplified to this:
having sum(condition = 100) = count(*)
THis will do it:
select * from TableA A JOIN TableA B ON B.table_A_id = A.id
where not exists (select 1 from TableB B where B.table_A_id = A.id and condition <> 100)
I would simply recommend:
select a.*
from TableA a
where not exists (select 1
from TableB B
where B.table_A_id = A.id and
condition <> 100
);
If condition can be NULL, then you want:
where not exists (select 1
from TableB B
where B.table_A_id = A.id and
(condition <> 100 or condition is null)
);
I have this query:
select a.*, b.*, (select c.* from tableC c where c.id_tableA = a.id) from tableA a inner join tableB b on a.id = b.id_tableA where b.id_user = 50;
The subquery (which is tableC) is returning me more than 1 row as expected. How can I return only 1 row from tableC so it could match with the rest of the query?
So far I have tried this:
(select c.* from tableC c where c.id_tableA = a.id limit 1)
It didn't work as mysql said:
"Operand should contain 1 column(s)"
You are mixing two things. Scalar subquery in SELECT list should return only one value (both row and column). Using LIMIT 1 will get you one row, but still many columns.
So you could specify column name:
select a.*, b.*,
(select c.col_name from tableC c where c.id_tableA = a.id order by .. limit 1)
from tableA a
inner join tableB b on a.id = b.id_tableA
where b.id_user = 50;
or use normal JOIN:
select a.*, b.*, c.*
from tableA a
inner join tableB b
on a.id = b.id_tableA
left join tableC c
on c.id_tableA = a.id
where b.id_user = 50;
if column id from table C is a primary key then it should have no problem
but if no, try to add another condition that will filter your subquery results like ,
for example here is the start_date:
SELECT a.column_1, b.column_2,
(SELECT column_3 FROM tableC
WHERE (id = a.id
AND (start_date = (SELECT MAX(b.start_date)
from tableC as c
where a.id = c.id ))) AS column_3
FROM tableA as a INNER JOIN
tableB as b ON b.id = a.id
WHERE b.id_user = 50;
Example:
SELECT SUM(SALARY) FROM (SELECT * FROM table1 WHERE id > 10) a LEFT JOIN table2 b on a.person = b.person
I want join table2 records only to (SELECT * FROM table1 WHERE id > 10) records, my example is not correct.
table1 contain 100mln records and I cant join table2 to all records I must use subquery
I'm assuming, you salary is not summing up correctly (you are getting more than you expect). This is because LEFT JOIN will leave NULL for the rowsthat doesn't have match in b.
For this SQL:
SELECT a.*, b.*
FROM (select * from (SELECT 123 AS Salary,
'Tom' AS person
UNION
SELECT 343 AS Salary,
'Bob' AS person
UNION
SELECT 877 AS Salary,
'Tom' AS person) as t where t.Salary > 123) a
LEFT JOIN (SELECT *
FROM (SELECT 'Tom' AS person,
1 AS id
UNION
SELECT 'Bob' AS person,
2 AS id) AS t
WHERE t.id = 1) AS b
ON a.person = b.person
you will have this output:
So INNER JOIN should work for you.
SELECT SUM(SALARY) FROM (SELECT * FROM table1 WHERE id > 10) a
LEFT JOIN table2 b on a.person = b.person
Hopefully this will get you going in the correct direction....
select sum(a.salary)
from table1 a
left join table2 b on a.person = b.person and b.salary_type = "something"
where a.id > 10
;
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');
I have two tables, A and B, A contains a list of entries and B for each of those entries multiple status rows (0-n, grouped by date with a status 0 for okay and 1 for failure).
Now I would like to select all rows from A with their respective most recent status and its date as well as the most recent failure and its date (failure defined as having at least one entry with 1).
I tried something with two left joins but am not convinced it is the optimal solution and also still have issues with determining the correct number of failures (SUM(b2.status))
SELECT a.id, b1.date, SUM(b1.status), b2.date, SUM(b2.status) FROM tablea a
LEFT JOIN tableb b1 ON b1.aid=a.id
LEFT JOIN tableb b2 ON b2.aid=a.id
WHERE (b1.date=(SELECT MAX(`date`) FROM tableb WHERE aid=a.id) OR b1.date IS NULL)
AND (b2.date=(SELECT MAX(`date`) FROM tableb WHERE aid=a.id GROUP BY `date` HAVING SUM(`status`)>0) OR b2.date IS NULL)
GROUP BY a.id
Whoops, I think every assumption I made to this point has been wrong. I think this will give you what you want. It will give you the total number of failures for the latest date that had a failure.
I'm not sure how performance will be on a very large database, but it works fine on a smaller one.
SELECT a.id, MAX(b1.date) status_date, SUM(b1.status) latest_status,
b2.date latest_failure,
b2.total_failures
FROM tablea a
LEFT JOIN tableb b1
ON b1.aid = a.id
AND b1.date = (SELECT MAX(date) FROM tableb WHERE aid = a.id)
LEFT JOIN
(SELECT aid, date, count(*) total_failures FROM tableb WHERE status > 0 GROUP BY aid, date) b2
ON b2.aid = a.id
AND b2.date = (SELECT MAX(date) FROM tableb WHERE aid = a.id AND status > 0)
GROUP BY a.id;
Something like this should work.
SELECT a.id, b1.date status_date, b1.status, b2.date latest_failure,
(SELECT COUNT(*) FROM tableb WHERE aid = a.id AND status > 0) total_failures
FROM tablea a
LEFT JOIN tableb b1
ON b1.aid = a.id
AND b1.date = (SELECT MAX(date) FROM tableb WHERE aid = a.id)
LEFT JOIN tableb b2
ON b2.aid = a.id
AND b2.date = (SELECT MAX(date) FROM tableb WHERE aid = a.id AND status > 0)
This is assuming that the tableb.date field is a datetime and is unique. Otherwise you may wish to use an id field instead.
You could do this also. Not sure which one is more efficient. You could try it both ways.
SELECT a.id, b1.date status_date, b1.status,
(SELECT MAX(date) FROM tableb WHERE aid = a.id AND status > 0) latest_failure,
(SELECT COUNT(*) FROM tableb WHERE aid = a.id AND status > 0) total_failures
FROM tablea a
LEFT JOIN tableb b1
ON b1.aid = a.id
AND b1.date = (SELECT MAX(date) FROM tableb WHERE aid = a.id)
This should work.
SELECT a.id, MAX(b1.date) status_date, SUM(b1.status) latest_status,
(SELECT MAX(date) FROM tableb WHERE aid = a.id AND status > 0) latest_failure,
(SELECT COUNT(*) FROM tableb WHERE aid = a.id AND status > 0) total_failures
FROM tablea a
LEFT JOIN tableb b1
ON b1.aid = a.id
AND b1.date = (SELECT MAX(date) FROM tableb WHERE aid = a.id)
GROUP BY a.id;
If you could have multiple failures for a day, and want to show the status as "1", then change this:
SUM(b1.status) latest_status
to:
MAX(b1.status) latest_status.