MS Access - WHERE IN works, but WHERE NOT IN fails - sql

I have the following query (simplified) on MS Access:
SELECT * FROM table1 WHERE table1.ID NOT IN (SELECT DISTINCT table1id FROM table2);
My problem is it doesn't work, but these two ones work:
SELECT * FROM table1 WHERE table1.ID IN (SELECT DISTINCT table1id FROM table2);
SELECT * FROM table1 WHERE table1.ID NOT IN (2, 3);
The first one simply returns me an empty set, while I know I have records on table1 with ids ranging from 1 to 9, and only 2 and 3 are use on table 2.
Any help?

Generally, the problem with IN and NOT in has to do with NULLs in the subselect. Try this and see if it works:
SELECT *
FROM table1
WHERE table1.ID NOT IN (SELECT DISTINCT table1id FROM table2 where tableid is not null);

Related

How to use count() correlated sub select

I have this SQL query
SELECT table1.*
FROM table1 table1
WHERE table1.table2_id IN (SELECT table2.id
FROM table2
WHERE table2.locked = 0)
I get the result and it works fine, but now I want to count how many rows exist.
I tried something like this:
SELECT table1.count(*)
FROM table1 table1
WHERE table1.table2_id IN (SELECT table2.id
FROM table2
WHERE table2.locked = 0)
But nothing worked…
How can I count the rows in this kind of query?
Try this
SELECT COUNT(*)
FROM table1 table1
WHERE table1.`table2_id` IN (SELECT table2.id FROM table2 WHERE table2.locked = 0)
Hope it will help you! ..

Select all from one table where some columns match another select

I have two differents tables. The have some columns in common, for this example lets say 'name' and 'id'.
By making
( SELECT name,id FROM table1
EXCEPT
SELECT name,id FROM table2)
UNION ALL
( SELECT name,id FROM table2
EXCEPT
SELECT name,id FROM table1)
I get a list of the elements that are on one tablet but not in the other one.
Up to this point everything is OK.
But now, I want to make a select all from table1 where the name and the id matches the result of the query above.
After lots of comments I think this is what you're after...
SELECT T1.*
FROM table1 t1
LEFT JOIN table2 t2
on T1.ID = T2.ID
and T1.Name = T2.Name
AND E2.event_Time_UTC between convert(datetime,'2016-02-09 00:00:20',101) and convert(datetime '2016-02-09 23:59:52',101)
WHERE T2.Name is null
AND E1.Event_Time_UTC between convert(datetime,'2016-02-09 00:00:20',101) and convert(datetime,'2016-02-09 23:59:52',101)
You may allow implicit casting to work but above is the explicit approach.
If not then you would need to cast the string dates to a date time, assuming Event_Time_UTC is a date/time datatype.
A left join lets us return all records from the 1st table and only those that match from the 2nd.
The t1.* returns only the columns from table1. The join criteria (on) allows us to identify those records which match so they can then be eliminated in the where clause by 'where t2.name is null' they will always be null when no record match in t2.
Thus you get a result set that is: all records from t1 without a matching record on name and id in table2.
Old version
The below content is no longer relevant, based on comments.
I redacted previous answer a lot because you're using SQL Server not MySQL and I know you want multiple records not table1 and table2 joined.
In the below I create two tables: table1 and table2. I then populate table1 and table2 with some sample data
I then show how to get only those records which exist in one table but not the other; returning a separate ROW for each. I then go into detail as to why I choose this approach vs others. I'll finally review what you've tried and try to explain why I don't think it will work.
create table table1 (
ID int,
name varchar(20),
col1 varchar(20),
col2 varchar(20),
col3 varchar(20));
Create table table2 (
id int,
name varchar(20));
Insert into table1 values (1,'John','col1','col2','col3');
Insert into table1 values (2,'Paul','col1','col2','col3');
Insert into table1 values (3,'George','col1','col2','col3');
Insert into table2 values (1,'John');
Insert into table2 values (4,'Ringo');
Option 1
SELECT T1.name, T1.ID, T1.Col1, T1.Col2, T1.Col3
FROM Table1 T1
LEFT JOIN Table2 T2
on T1.Name = T2.Name
and T1.ID = T2.ID
WHERE T2.ID is null
UNION ALL
SELECT T2.name, T2.ID, NULL, NULL, NULL
FROM Table1 T1
RIGHT JOIN Table2 T2
on T1.Name = T2.Name
and T1.ID = T2.ID
WHERE T1.ID is null ;
which results in...
Notice John isn't there as it's in both tables. We have the other 2 records from table1, and the ID, name from table2 you're after.
Normally I would do this as a full outer join but since I think you want to reuse the name and id fields to relate to BOTH tables in the same column we had to use this approach and spell out all the column names in table 1 and put NULL for each column in table1 when displaying records from table2 in order to make the output of the second query union to the first. We simply can't use *
Option 2: Using a full outer join... with all columns from T1
SELECT T1.*
FROM Table1 T1
FULL OUTER JOIN Table2 T2
on T1.ID = T2.ID
and T1.Name = T2.Name
WHERE (T1.ID is null or T2.ID is null)
you get this... which doesn't show Ringo...
But then I would ask why you need anything from Table 2 at all so I think you're wanting to still show the ID, Name from table2 when it doesn't exist in table1.
Which is why What I think you're after is the results from the 1st query using the union all.
Option 3 I suppose we could avoid the second query in option 1 by doing...
SELECT coalesce(T1.Name, T2.name) as name, coalesce(T1.Id,T2.ID) as ID, T1.col1, T1.Col2, T1.Col3
FROM Table1 T1
FULL OUTER JOIN Table2 T2
on T1.ID = T2.ID
and T1.Name = T2.Name
WHERE (T1.ID is null or T2.ID is null)
which gives us what I believe to be the desired results as well.
This works because we know we only want the name,id from table2 and all the column values in table1 will be blank.
Notice however in all cases we simply can't use Tablename.* to select all records from table1.
This is what you tried:
( SELECT name,id FROM table1
EXCEPT
SELECT name,id FROM table2)
UNION ALL
( SELECT name,id FROM table2
EXCEPT
SELECT name,id FROM table1)
Assuming you want to reuse the ID, Name fields; you can't select *. Why? because the records in Table2 not in table1 aren't in table1. In my example if you want Ringo to show up you have to reference table2! Additionally, * gives you no ability to "Coalesce" the ID and name fields together as I did in option 3 above.
If you ONLY want the columns from table1, that means you will NEVER see data from table2. If you don't need the data from table2, (such as ringo in my example) then why do we need to do the union at all?) I'm assuming you want ringo, thus you HAVE to somewhere reference name, id from table2.
You could also do this with NOT EXISTS:
SELECT *
FROM table1
WHERE
NOT EXISTS
(SELECT 1
FROM table2
WHERE table1.id = table2.id
AND table1.name = table2.name)
;with cte as
(
( SELECT name,id FROM table1
EXCEPT
SELECT name,id FROM table2)
UNION ALL
( SELECT name,id FROM table2
EXCEPT
SELECT name,id FROM table1)
)
Select *
from table1 as tbl1
where
tbl1.id = cte.id
and tbl1.name = cte.name

Select from table with two conditions

I'm stuck with a query and I'd need some help.
I need to select values from a table which meets two conditions from other table, for example:
Select * from table1
where ID = (select ID from table2)
AND value = (select value from table2)
So, if I'd need only one value from the table, I could query:
Select * from table1 where ID = (id1) AND value = (value1)
The only solution that I know is using IN, but it wouldn't be the requested solution.
I need something similar to this, but counting that the data returned by table2 is not only one row, but multiple.
Could somebody give me some clue on how to find this?
Thanks.
One method uses exists:
Select *
from table1 t1
where exists (select 1
from table2 t2
where t2.id = t1.id and t2.value = t1.value
);
This is ANSI standard syntax, so it should work in any database. Some databases support this syntax:
select t1.*
from table1 t1
where (t1.id, t1.value) in (select t2.id, t2.value from table2 t2);
I would use AND and OR
select *
from table1
where (ID = (id1) AND value = (value1))
OR (ID = (id2) AND value = (value2))
Each check must be added in brackets. This allows all results matching the pairs ID and VALUE to be returned.
Select * from
table1 inner join table2 on
(table1.ID = table2.ID and
table1.value = table2.value) ;
Since as far as I can understand , you just need to select every rows from table1 where ID and value in table1 equals ID and value in table2. So you just need inner join which joins table1 and table2 checking the condition.

Subquery not in performance question

I have this slow query
select * from table1 where id NOT IN ( select id from table2 )
Would this be faster by doing something like (not sure if this is possible):
select * from table1 where id not in ( select id from table2 where id = table1.id )
Or:
select * from table1 where table1.id NOT EXIST( select id from table2 where table2.id = table1.id )
Or:
select * from table1
left join table2 on table2.id = table1.id
WHERE table2.id is null
Or do something else? Like break it up into two queries ...
The question is - are the field(s) in the comparison nullable (meaning, can the column value be NULL)?
If they're nullable...
...in MySQL the NOT IN or NOT EXISTS perform better - see this link.
If they are NOT nullable...
... LEFT JOIN / IS NULL performs better - see this link.
select table1.* from table1
LEFT JOIN table2 ON table1.id = table2.id
WHERE table2.id IS NULL
The object being to get rid of NOT IN

Ms Access gives rubbish when updating a join column

Updating a column that is part of your selection criteria should be no problem I think/thought.
I still have a query that gives odd results:
update table1 as t1
inner join table2 as t2 on t1.id = t2.old_id
set t1.id = t2.id
I use table2 to map id to old_id.
table1.id and table2.id are both primary keys. table2.old_id is also unique.
It follows that table1.id will still be unique after this update.
WRONG! MS Access will make rumble of this, with some duplicate table.id values.
I think however this is the correct way of updating a column that is used in a join? How could we achieve the desired result in MS Access?
Note: table2 is a mysql view accessed via ODBC.
table1.id and table2.id are both
primary keys. table2.old_id is also
unique.
It follows that table1.id will still
be unique after this update.
That ain't necessarily so.
It seems old_ID and (new) ID are of the same data type. A row in table1 that has no matching row in table2 based on the predicate (table1.ID = table2.old_ID) would not be updated. A different row could match a (new) ID value, hence get updated, with the same value as that row that didn't get updated.
Here's an example using Standard SQL (works in SQL Server 2008, not it Access/Jet) which I hope you can follow. Note I've re-written your INNER JOIN using an EXISTS construct to fit the logic of what I am trying to convey:
WITH Table1 (ID) AS
(
SELECT ID
FROM (
VALUES (1),
(2),
(3),
(4)
) AS Table2 (ID)
),
Table2 (old_ID, ID) AS
(
SELECT old_ID, ID
FROM (
VALUES (1, 55),
(2, 99),
(3, 4)
) AS Table2 (old_ID, ID)
)
-- ID of rows that will not be updated:
SELECT T1.ID
FROM Table1 AS T1
WHERE NOT EXISTS (
SELECT *
FROM Table2 AS T2
WHERE T1.ID = T2.old_ID
)
UNION ALL
-- updated IDs
SELECT T2.ID
FROM Table2 AS T2
WHERE EXISTS (
SELECT *
FROM Table1 AS T1
WHERE T1.ID = T2.old_ID
);
The resultset:
ID
---
4 --<-- duplicate
55
99
4 --<-- duplicate
In other words, even though all the following are unique:
(table1.ID)
(table2.ID)
(table2.old_ID)
...the following may contain duplicates:
table1.ID
UNION ALL
table2.ID
Using JOINs in UPDATE statements isn't consistently supported, which is why they aren't my habit to use.
UPDATE TABLE1 AS t1
SET t1.id = (SELECT t2.id
FROM TABLE2 t2
WHERE t2.old_id = t1.id)