Getting an error when using joins in query - mysql

I have this query:
SELECT * FROM `employee_activities` a
LEFT JOIN `activity` b ON a.activity_code = b.code
LEFT JOIN `employees` c ON a.employee_code = c.code
WHERE b.type = "Design"
AND c.code NOT IN(
SELECT * FROM `employee_activities` a
LEFT JOIN `activity` b ON a.activity_code = b.code
LEFT JOIN `employees` c ON a.employee_code = c.code
WHERE b.type = "Testing"
)
GROUP BY c.code
I get this error:
#1241 - Operand should contain 1 column(s)
I'm tying to get all employees that have at least one activity of type "Design" and None activity of type "Testing".
I have a query that works but I would like it to work with joins.
This works:
SELECT c.name FROM `employee_activities` a, `activity` b, `employees` c
WHERE a.activity_code = b.code
AND a.employee_code = c.code
AND b.type = "Design"
AND c.code NOT IN(
SELECT c.code FROM `employee_activities` a, `activity` b, `employees` c
WHERE a.activity_code = b.code
AND a.employee_code = c.code
AND b.type = "Testing"
)
GROUP BY c.code
What did I do wrong on the sql with joins?

For the not in sub query - it should contain only one column - for example
SELECT * FROM `employee_activities` a
LEFT JOIN `activity` b ON a.activity_code = b.code
LEFT JOIN `employees` c ON a.employee_code = c.code
WHERE b.type = "Design"
AND c.code NOT IN(
SELECT b.employee_code FROM `employee_activities` a
LEFT JOIN `activity` b ON a.activity_code = b.code
LEFT JOIN `employees` c ON a.employee_code = c.code
WHERE b.type = "Testing"
)
GROUP BY c.code

Your query
AND c.code NOT IN(
SELECT * FROM `employee_activities` a
...
tries to compare c.code to all columns in the subquery. What you want is probably;
AND c.code NOT IN(
SELECT c.code FROM `employee_activities` a
Also, you have a problem in your LEFT JOIN;
LEFT JOIN `activity` b ON a.activity_code = b.code
...
WHERE b.type = "Design"
When you compare a column that is left joined in into a WHERE clause, it basically turns the whole join into an INNER JOIN. Since your original query seems to use an inner join, that should be ok, but you may as well change it to;
SELECT * FROM `employee_activities` a
JOIN `activity` b ON a.activity_code = b.code AND b.type='Design'
LEFT JOIN `employees` c ON a.employee_code = c.code

Your problem is in this part:
AND c.code NOT IN(
SELECT * FROM
You can't have an * here as its looking to see if c.code is in the list of field values returned and must specify just a single field in the select.

The problem is in this section:
AND c.code NOT IN(
SELECT * FROM `employee_activities` a
You can't SELECT * in that nested query. You need to select exactly one column that will be compared to the c.code. You need this instead:
AND c.code NOT IN(
SELECT c.code FROM `employee_activities` a

DO this:
SELECT * FROM `employee_activities` a
LEFT JOIN `activity` b ON a.activity_code = b.code
LEFT JOIN `employees` c ON a.employee_code = c.code
WHERE b.type = "Design"
AND c.code NOT IN(
SELECT c.code FROM `employee_activities` a
LEFT JOIN `activity` b ON a.activity_code = b.code
LEFT JOIN `employees` c ON a.employee_code = c.code
WHERE b.type = "Testing"
)
GROUP BY c.code
As here you should compare the data with code column and * will fetch all the records.

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
)

Ordering clauses of a left join

I'm trying to join some tables with a query like below. Because I want to get the c.name ideally that the b table refers to. If the b table doesn't have rows in the result set or the b row doesn't refer to c, then just get the c.name that a table refers to.
SELECT a.*, c.name
FROM a
LEFT JOIN b ON a.b_id = b.id
LEFT JOIN c ON (b.c_id IS NOT NULL AND b.c_id = c.id) OR a.c_id = c.id
However mysql is always joining c with a.c_id = c.id and getting the less-favored c.name. Is it possible to avoid this, or is mySQL trying to get a full result set as quick as it can?
Try this may help:
SELECT a.*, c.name
FROM a
LEFT JOIN b ON a.b_id = b.id
LEFT JOIN c ON (b.c_id IS NOT NULL OR b.c_id = c.id) OR a.c_id = c.id
I think this should help:
SELECT a.*, c.name
FROM a
LEFT JOIN b ON a.b_id = b.id
LEFT JOIN c ON c.id = COALESCE(b.c_id, a.c_id)
When b.c_id is NULL, then a.c_id will be used. Otherwise b.c_id will be used.
It's not about speed. OR will give you all possible result rows including both b.c_id and a.c_id mappings for each row in a.
If you're not familiar with COALESCE(), the long form of this is almost exactly like your query but using IF() instead of OR.
SELECT a.*, c.name
FROM a
LEFT JOIN b ON a.b_id = b.id
LEFT JOIN c ON IF(b.c_id IS NOT NULL, b.c_id = c.id, a.c_id = c.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'

Table identifiers with LEFT JOIN

How does one join say, on tableD.id = tableC.id AND tableD.id = tableE.id? both tableD and E may have 0 rows and I need to count them ie. SELECT COUNT(E.id). The problem is I don't know where to declare the table identifiers.
I've tried:
FROM tableB B, tableD D, tableE E ...
LEFT JOIN (tableC C, tableD D) ON ...
SELECT B.*, COUNT(C.id) AS cCount
FROM tableB B
LEFT JOIN (tableC C)
ON (B.id = C.id)
GROUP BY B.id
It is a little difficult to tell from your question what you're looking for, but I believe this is it:
SELECT B.*, COUNT(C.id) AS cCount
FROM tableB AS B
LEFT JOIN tableC AS C ON B.id = C.id
LEFT JOIN tableD AS D ON C.Id = D.Id
LEFT JOIN tableE AS E ON D.Id = E.Id
GROUP BY B.id

MySQL Multiple Aggregates

Without the third join D.cid = C.id, this query gives me the count of C. With the third join it corrupts the count and gets unwanted tuples into the count of C's join. So how can I get the count of C and D without having the C count effected? Is there a form of parenthesis I can use to make sure I get the correct count?
SELECT A.*, B.*, COUNT(C.aid) AS cCount
FROM tableA A
LEFT JOIN tableC AS C ON A.id = C.aid
INNER JOIN tableB AS B ON A.id = B.aid
LEFT JOIN tableD AS D ON D.cid = C.id
GROUP BY A.id
I would have the counts from the other tables pre-aggregated unto themselves and joined... something like...
SELECT
A.*,
B.*,
COALESCE( PreAggC.CCount, 0 ) as CCount,
COALESCE( PreAggC.WithDCount, 0 ) as WithDCount
FROM
tableA A
JOIN tableB B
on A.ID = B.aID
LEFT JOIN ( select aID,
count( distinct id ) CCount,
count(*) as WithDCount
from tableC
left join tableD D
on c.ID = D.cID
group by aID ) PreAggC
on A.id = PreAggC.aID
Now, do you really want how many entries actually have "D" records? so I included both counts... distinct "C" entries, and the overall count with correlation with "D"