Issue with NOT IN - sql

SELECT *
FROM table -> 35 records
SELECT *
FROM table
WHERE x IN (SELECT x
FROM table1) -> 34 records
SELECT *
FROM table
WHERE x NOT IN (SELECT x
FROM table1) -> 0 records
Any ideas as to how this could be possible?

The simple fix for the NULL value is:
SELECT *
FROM table
WHERE x NOT IN (SELECT x
FROM table1
WHERE x is not null);
However, it is recommended to use not exists rather than not in because of the NULL issue:
select t.*
from table t
where not exists (select 1 from table1 t1 where t1.x = t.x);

One of your x values is NULL. NULL values will never evaluate to true in any comparison (since the value is unknown).

As others have said, 1 of your values is likely to be NULL. You can test for this using IS NULL in your WHERE clause. For example:
SELECT *
FROM MyTable
WHERE MyColumn IS NULL
This simple example would find all records in MyTable that have NULL in MyColumn.

"Any null values returned by subquery or expression that are compared to test_expression using IN or NOT IN return UNKNOWN. Using null values in together with IN or NOT IN can produce unexpected results.
"
NOT/IN MSDN
for a Note, NOT IN is appropriate when you know the number of rows coming from sub-query will remain always small and finite values.
it is not advisable if that list items are uncertain. instead use the LEFT JOIN type query.
EDIT-1 For #John Gibb
DECLARE #t TABLE
(
id INT NULL
)
DECLARE #t1 TABLE
(
id INT NULL
)
INSERT INTO #t (id) SELECT 1 UNION ALL SELECT NULL UNION ALL SELECT 2
INSERT INTO #t1 (id) SELECT 1 UNION ALL SELECT NULL
SELECT *
FROM #t
WHERE id NOT IN (SELECT id FROM #t1)
SELECT *
FROM #t t
LEFT JOIN #t1 t1
ON t.id=t1.id
WHERE t1.id IS NULL
AND t.id IS NOT NULL

Related

Selecting records with identical categories, but opposing integers

This is the nature of the table I am working with:
IF OBJECT_ID('TEMPDB..#TEMP') IS NOT NULL
DROP TABLE #TEMP
CREATE TABLE #TEMP (
CategoryA NVARCHAR(10),
CategoryB NVARCHAR(10),
CategoryC NVARCHAR(10),
IntegerA INT,
);
INSERT INTO #TEMP(CategoryA,CategoryB,CategoryC,IntegerA)
VALUES
('A','H','G',20),
('A','H','G',-15),
('F','L','C',10),
('N','U','X',12),
('K','G','G',15),
('K','G','G',-10);
SELECT * FROM #TEMP
Notice that the top 2 rows and the bottom 2 rows have identical categories, however they have integers of opposite polarity. The middle 2 rows are distinct with positive integers.
I need a way to select all of the records that are not duplicated (Such as the middle 2 rows). And I need to select the records with negative integers, without selecting their positive counter-parts.
The desired output in this case would be:
I have tried seeing if I can make my own table which inserts only the records I want, but I run into the same problem again where I cannot figure out how to distinguish between the records where all of the categories are the same.
For this dataset, you could just use row_number():
select categoryA, categoryB, categoryC, integerA
from (
select
t.*,
row_number() over(partition by categoryA, categoryB, categoryC order by integerA) rn
from temp t
) t
where rn = 1
Hmmm . . . I think you want:
select t.*
from #temp t
where t.integerA < 0 or
not exists (select 1
from #temp t2
where t2.A = t.A and t2.B = t.B and
t2.C = t.c and t2.integerA < 0
);
Here is a db<>fiddle.

Update a table in SQL Server 2008 R2 by a column in another table and also need a sum value

I am trying to update a table in SQL Server 2008 R2.
Table1:
id name value1
a 34 3
a 32 2
a - -
c 90 9
Table2:
id
a
expected table1:
id name value1
a 34 3
a 32 2
a - 5
c 90 9
I need to sum all value1 group by id that exists in table2.
My SQL query:
update table1
set value1 = cast(SUM(cast ([value1] as float)) as varchar(50))
GROUP BY id
where name = '-' and id in
(
select distinct id
from table2
)
I got error:
Incorrect syntax near the keyword 'GROUP'.
Any help would be appreciated.
UPDATE
update table1
set value1 = cast(SUM(cast ([value1] as float)) as varchar(50))
where name = '-' and id in
(
select distinct id
from table2
)
GROUP BY id
still :
Incorrect syntax near the keyword 'GROUP'.
You can't use that construct afaik. You need a subquery to calculate your values based on id, and update from that table:
UPDATE table1
SET value1 = SumTable.val
FROM (
SELECT T1.id, cast(SUM(cast (T1.[value1] as float)) as varchar(50)) as val
FROM table1 T1
WHERE T1.id in
(
select distinct T2.id
from table2 T2
)
GROUP BY T1.id
) AS SumTable
WHERE table1.id = SumTable.id
If you just want to return the grouped result, you can do this by running the following
SELECT ID, SUM(Value)
FROM Table1
WHERE ID IN (SELECT ID FROM Table2) GROUP BY ID
If, however, you want to replace the contents of table1, there's no trivial way of doing this in a single operation. You need 'cache' the intermediate result before recreating the original table:
DECLARE #TEMPTABLE TABLE (ID int, Value int);
INSERT INTO #TEMPTABLE SELECT ID, SUM(Value)
FROM Table1
WHERE ID IN (SELECT ID FROM Table2) GROUP BY ID
DROP TABLE Table1
SELECT * INTO Table1 FROM #TempTable
SELECT * FROM TABLE1
(note, that was from your first edit)
If you just want to add additional 'sum' lines to the table, then you should be able to avoid the temp table and instead issue an INSERT INTO statement using the SELECT statement I gave first as a sub-query.
I do think what you're doing is quite strange, however, and you may want to think about what you're doing with your tables.
Your casting back to a VARCHAR is also a little strange - if they're always going to be an integer, keep them as integers. If they're truly VARCHARS then SUM won't always work

missing number or max number from the list

I am looking for missing number in the list, it works perfectly fine but when it start from 2, would it be possible to get 1. in below insertion
it should provide 1 not 4. please help thanks
drop table #temp
create table #temp
(
Number INT
)
insert into #temp
(Number)
select 2 union all
select 3 union all
select 5
SELECT MIN(t1.Number) + 1 AS MissingNumber
FROM #temp t1
LEFT OUTER JOIN #temp t2 ON (t1.Number + 1 = t2.Number)
WHERE t2.Number IS NULL
I will suggest you to create a separate numbers table to do this.
There a many ways to create number table. Check this link for more info
SELECT TOP (1000) n = Row_number()OVER (ORDER BY number)
INTO #numbers
FROM [master]..spt_values
ORDER BY n;
CREATE TABLE #temp
(Number INT)
INSERT INTO #temp(Number)
SELECT 2
UNION ALL
SELECT 3
UNION ALL
SELECT 5
SELECT Min(t1.n) AS MissingNumber
FROM #numbers t1
LEFT OUTER JOIN #temp t2
ON ( t1.n = t2.Number )
WHERE t2.Number IS NULL
I think its not possible because join doesn't knows that number starts from 1. it will search for min value.
we can use while loop to solve problem
use this
IF OBJECT_ID('Tempdb..#temp') IS NOT NULL
DROP TABLE #temp
CREATE TABLE #temp ( Number INT )
INSERT INTO #temp
( Number )
VALUES ( 2 ),
( 3 ),
( 5 );
WITH cte
AS ( SELECT n = 1
UNION ALL
SELECT n + 1
FROM cte
WHERE n <= 100 --can be increased with OPTION ( MAXRECURSION {iteration value} ) at the end of the query
)
SELECT MIN(cte.n) AS MissingNumber
FROM cte
LEFT JOIN #temp t ON ( cte.n = t.Number )
WHERE t.Number IS NULL

How to remove common fields in two tables

I have two tables , table 1 and table 2.
The fields of table 1 are :
book,pen,pencil,bag
The fields of table 2 are :
car,van,book,bike,pencil
When I run the query I want the query to ignore the duplicate or common fields and return the other field.
The output should be as follows,
car,van,bike,pen,bag
Perhaps:
SELECT x.thing FROM
(
SELECT thing FROM dbo.Table1
UNION ALL
SELECT thing FROM dbo.Table2
) X
GROUP BY x.thing
Having Count(*) = 1
Demo
However, this will also remove items that are duplicates in their table which might or might not be desired.
Have you tried sth like this:
delete form X
where (car =
Select distinct car
from X
where x);
distinct--> return the differents values.
try this one:
declare #table1 table (col1 varchar(max))
declare #table2 table (col1 varchar(max))
insert into #table1 values
('book'),('pen'),('pencil'),('bag')
insert into #table2 values ('car'),('van'),('book'),('bike'),('pencil')
;with cte
as (
select COUNT(1) as total_item, col1 from (
select col1 from #table1
union all
select col1 from #table2
)a group by col1
)
select col1 from cte where total_item = 1
WITH uniontables AS (
SELECT NULL AS car,
NULL AS van,
book,
NULL AS bike,
pen,
pencil,
bag
FROM [Table 1 ]
UNION
SELECT car,
van,
book,
bike,
NULL AS pen,
pencil,
NULL AS bag
FROM [Table 2 ] )
SELECT DISTINCT * FROM uniontables

TSQL Comparing two Sets

When two sets are given
s1 ={ a,b,c,d} s2={b,c,d,a}
(i.e)
TableA
Item
a
b
c
d
TableB
Item
b
c
d
a
How to write Sql query to display "Elements in tableA and tableB are equal". [Without using SP or UDF]
Output
Elements in TableA and TableB contains identical sets
Use:
SELECT CASE
WHEN COUNT(*) = (SELECT COUNT(*) FROM a)
AND COUNT(*) = (SELECT COUNT(*) FROM b) THEN 'Elements in TableA and TableB contains identical sets'
ELSE 'TableA and TableB do NOT contain identical sets'
END
FROM (SELECT a.col
FROM a
INTERSECT
SELECT b.col
FROM b) x
Test with:
WITH a AS (
SELECT 'a' AS col
UNION ALL
SELECT 'b'
UNION ALL
SELECT 'c'
UNION ALL
SELECT 'd'),
b AS (
SELECT 'b' AS col
UNION ALL
SELECT 'c'
UNION ALL
SELECT 'd'
UNION ALL
SELECT 'a')
SELECT CASE
WHEN COUNT(*) = (SELECT COUNT(*) FROM a)
AND COUNT(*) = (SELECT COUNT(*) FROM b) THEN 'yes'
ELSE 'no'
END
FROM (SELECT a.col
FROM a
INTERSECT
SELECT b.col
FROM b) x
Something like this, using FULL JOIN:
SELECT
CASE
WHEN EXISTS (
SELECT * FROM s1 FULL JOIN s2 ON s1.Item = s2.Item
WHERE s1.Item IS NULL OR s2.Item IS NULL
)
THEN 'Elements in tableA and tableB are not equal'
ELSE 'Elements in tableA and tableB are equal'
END
This has the virtue of short-circuiting on the first non-match, unlike other solutions that require 2 full scans of each table (once for the COUNT(*), once for the JOIN/INTERSECT).
Estimated cost is significantly less than other solutions.
Watch out, I'm gonna use a Cross Join.
Declare #t1 table(val varchar(20))
Declare #t2 table(val varchar(20))
insert into #t1 values ('a')
insert into #t1 values ('b')
insert into #t1 values ('c')
insert into #t1 values ('d')
insert into #t2 values ('c')
insert into #t2 values ('d')
insert into #t2 values ('b')
insert into #t2 values ('a')
select
case when
count(1) =
(((Select count(1) from #t1)
+ (Select count(1) from #t2)) / 2.0)
then 1 else 0 end as SetsMatch from
#t1 t1 cross join #t2 t2
where t1.val = t2.val
My monstrocity:
;with SetA as
(select 'a' c union
select 'b' union
select 'c')
, SetB as
(select 'b' c union
select 'c' union
select 'a' union
select 'd'
)
select case (select count(*) from (
select * from SetA except select * from SetB
union
select * from SetB except select * from SetA
)t)
when 0 then 'Equal' else 'NotEqual' end 'Equality'
Could do it with EXCEPT and a case
select
case
when count (1)=0
then 'Elements in TableA and TableB contains identical sets'
else 'Nope' end from (
select item from s1
EXCEPT
select item from s2
) b
Since this thread was very helpful to me, I thought I'd share my solution.
I had a similar problem, perhaps more generally applicable than this specific single-set comparison. I was trying to find the id of an element that had a set of multi-element child elements that matched a query set of multi-element items.
The relevant schema information is:
table events, pk id
table solutions, pk id, fk event_id -> events
table solution_sources, fk solutionid -> solutions
columns unitsourceid, alpha
Query: find the solution for event with id 110 that has the set of solution_sources that match the set of (unitsourceid, alpha) in ss_tmp. (This can also be done without the tmp table, I believe.)
Solution:
with solutionids as (
select y.solutionid from (
select ss.solutionid, count(ss.solutionid) x
from solutions s, solution_sources ss
where s.event_id = 110 and ss.solutionid = s.id
group by ss.solutionid
) y where y.x = ( select count(*) from ss_tmp )
)
select solutionids.solutionid from solutionids where
(
select case
when count(*) = ( select count(*) from ss_tmp ) then true
else false
end
from
( SELECT unitsourceid, alpha FROM solution_sources
where solutionid = solutionids.solutionid
INTERSECT
SELECT unitsourceid, alpha FROM ss_tmp ) x
)
Tested against a test query of 4 items and a test db that had a matching solution (same number of child elements, each that matched), several completely non-matching solutions, and 1 solution that had 3 matching child elements, 1 solution that had all 4 matching child elements, plus an additional child, and 1 solution that had 4 child elements of which 3 of the 4 matched the query. Only the id of the true match was returned.
thanks a lot
-Linus
Use EXCEPT statement
When using the EXCEPT statement to test if two sets contain the same rows, you will need to do the EXCEPT in both directions (A EXCEPT B and B EXCEPT A). If either comparison returns any records, then the sets are different. If no records are returned by either, they are the same.
The nice thing about this is that you can do this comparison with any number of specific columns and NULL values are handled implicitly without having to jump through hoops to compare them.
A good use case for this is verifying that saving a set of records happened correctly, especially when affecting an existing set.
SELECT IsMatching = (1 ^ convert(bit, count(*)))
FROM (
SELECT Mismatched = 1 -- Can be any column name
FROM (
SELECT Item -- Can have additional columns
FROM TableA
EXCEPT
SELECT Item -- Can have additional columns
FROM TableB
) as A
UNION
SELECT Mismatched = 1 -- Can be any column name
FROM (
SELECT Item -- Can have additional columns
FROM TableB
EXCEPT
SELECT Item -- Can have additional columns
FROM TableA
) as A
) as A