How to delete based on select query - mysql

I want to delete the rows of that are the result of this query
SELECT *
FROM a
LEFT JOIN b
ON a.id = b.id
WHERE b.id IS NULL;
How to do it?
I have tried:
DELETE
FROM a
LEFT JOIN b
ON a.id = b.id
WHERE b.id IS NULL;
But it is not working

Use not exists:
delete a from a
where not exists (select 1 from b where b.id = a.id);

Related

MySQL find NON-matching values in 2 columns of different tables (in different DBs) with one efficient query

I have to update col_Foo and col_Bar in table B with the data in col_Foo and col_Bar in table A. The 2 tables have the same primary index id.
This need to be as efficient as possible, as unfortunately I'm compelled to poll the database very ofter and cannot rely on any event system.
This was my first attempt, which doesn't work but I think it makes it easy to understand what I'd like to achieve:
select B.*, A.foo, B.foo, A.bar, B.bar
from DB2.B-name as B
inner join DB1.A-name as A
on B.id = A.id
and (A.bar <> B.bar or A.foo <> B.foo)
The following does work, but I wonder if it could be made more efficient.
Or if maybe I'm better off just updating the whole column without checking for any mismatch.
SELECT A.id, A.foo, B.foo, A.bar, B.bar
FROM DB1.A-name AS A
WHERE A.id NOT IN (
(
SELECT A.id as idt
FROM DB1.A-name AS A
INNER JOIN DB2.B-name AS B
ON B.id = A.id
AND B.foo = A.foo
)
UNION ALL
(
SELECT A.id as idt
FROM DB1.A-name AS A
INNER JOIN DB2.B-name AS B
ON B.id = A.id
AND B.bar = A.bar
) t
GROUP BY idt
HAVING COUNT (*) = 2
)
EDIT: adding UPDATE version:
UPDATE DB2.B-name AS B
INNER JOIN DB1.A-name AS A
ON B.id = A.id
SET
B.foo = A.foo,
B.bar = A.bar
WHERE A.id NOT IN (
(
SELECT A.id as idt
FROM DB1.A-name AS A
INNER JOIN DB2.B-name AS B
ON B.id = A.id
AND B.foo = A.foo
)
UNION ALL
(
SELECT A.id as idt
FROM DB1.A-name AS A
INNER JOIN DB2.B-name AS B
ON B.id = A.id
AND B.bar = A.bar
) t
GROUP BY idt
HAVING COUNT (*) = 2
)

How can I return 1 row from a subquery in select statement if subquery has more than 1 result?

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;

SQL find children that have multiple parent records

I have a situation where we have inserted duplicated data into some tables.
Given the following database schema, I want to find all records with s_id and co_id combinations associated to more than 1 record from table A. The highlighted rows are the rows I'm looking for, based off of finding the duplicates I need to find the id's from table A associated to the duplicate records.
I'm able to group by s_id & co_id to determine potential duplicates, but because Table B is a 1:M, this isn't entirely accurate.
Select c.s_id, c.co_id, Count(*)
from c
INNER JOIN b on c.b_id = b.id
INNER JOIN a on a.id = b.a_id
Group By c.s_id, c.co_id
Having count(*) > 1;
I think you just want count(distinct):
Select c.s_id, c.co_id, Count(distinct a.id)
from c join
b
on c.b_id = b.id join
a
on a.id = b.a_id
Group By c.s_id, c.co_id
having count(distinct a.id) > 1;
Gordon's answer will get you the s_id and co_id values. If you need to trace those back to a then try this:
select distinct a.id
from
a inner join b on b.a_id = a.id inner join c on c.b_id = b.id inner join
(
select c.s_id, c.co_id
from a inner join b on b.a_id = a.id inner join c on c.b_id = b.id
group by c.s_id, c.co_id
having count(distinct a.id) > 1
) as dups
on dups.s_id = c.s_id and dups.co_id = s.co_id

can't specify target table for UPDATE in FROM clause

I am trying to execute the following query:
update table3 d set status = 'Complete'
where d.id in
(
select b.id from table1 a, table3 b, table2 c
where a.id = b.table1_id
and c.id = b.table2_id
and c.examId = 16637 -- will be passed in by user
and a.id in (46,47,48,49) -- will be passed in by user
);
So, I'm trying to update multiple rows of table3.
table3 is a join table between table1 and table2.
wrap it in a subquery, (thus creating a temporary table for the result). I'm also recommending to use ANSI SQL-92 format.
update table3 d
set status = 'Complete'
where d.id in
(
SELECT ID
FROM
(
select b.id
from table1 a
INNER JOIN table3 b
ON a.id = b.table1_id
INNER JOIN table2 c
ON c.id = b.table2_id
where c.examId = 16637 and
a.id in (46,47,48,49)
) xx
);
or by using JOIN
update table3 d
INNER JOIN
(
SELECT ID
FROM
(
select b.id
from table1 a
INNER JOIN table3 b
ON a.id = b.table1_id
INNER JOIN table2 c
ON c.id = b.table2_id
where c.examId = 16637 and
a.id in (46,47,48,49)
) xx
) y ON d.id = y.id
set status = 'Complete'

SQL LEFT JOIN only newest right column entry?

So I have two tables like this:
create table A
{
id int;
...
}
create table B
{
id int;
a_id int;
t timestamp;
...
}
A is one-to-many with B
I want to:
SELECT * FROM A LEFT JOIN B ON A.id = B.a_id ???
But I want to return exactly one row for each entry in A which has the B with the newest t field (or null for Bs fields if it has no B entry).
That is rather than returning all A-B pairs, I want to only select the newest one with respect to A (or A-null if no B entry).
Is there some way to express this in SQL? (I'm using MySQL 5.5)
LEFT JOIN is only concerned with ensuring every row in A is returned, even if there is no corresponding joined row in B.
The need for just one row needs another condition. MySQL is limitted in its options, but one could be:
SELECT
*
FROM
A
LEFT JOIN
B
ON B.id = A.id
AND B.t = (SELECT MAX(lookup.t) FROM B AS lookup WHERE lookup.id = A.id)
Another could be...
SELECT
*
FROM
A
LEFT JOIN
(
SELECT id, MAX(t) AS t FROM B GROUP BY id
)
AS lookup
ON lookup.id = A.id
LEFT JOIN
B
ON B.id = lookup.id
AND B.t = lookup.t
You could do the following:
SELECT A.*, B.*
FROM
A
LEFT JOIN
(SELECT B.a_id, MAX(t) as t FROM B GROUP BY B.a_id) BMax
ON A.id = BMax.a_id
JOIN B
ON B.a_id = BMax.a_id AND B.t = BMax.t
you first need to get the newest t from tableB in a subquery, then join it with tableA and tableB.
SELECT a.*, c.*
FROM tableA a
LEFT JOIN
(
SELECT a_ID, max(t) maxT
FROM tableB
GROUP BY a_ID
) b on a.a_id = b.a_ID
LEFT JOIN tableB c
ON b.a_ID = c.a_ID AND
b.maxT = c.t
try this:
SELECT *
FROM tableA A LEFT JOIN
(select a_id ,max(t) as max_t
from tableB
group by a_id )b
on A.id = b.a_id
and A.t=b.max_t