I try to execute the following query but it doesn't get any data although it should get one row :
select * from [DB1.table1] where col1 not in (select col2 from DB2.table2)
col1,col2 are of type varchar
why it doesn't work ?
"Doesn't work" isn't exactly a good description of your problem, but in nearly all cases this is caused by the sub-select returning NULL values.
You probably want this:
select * from [DB1.table1]
where col1 not in (select col2 from DB2.table2 where col2 is not null);
The comparison with NULL always yield "undefined" and thus if at least one row from the sub-select contains a NULL in the col2 column the whole expression is "undefined". As undefined not "true", the whole query doesn't return anything.
If you have NULLs in col2 in table2, you'll get the behaviour you describe:
create table table2 (
col2 varchar(10) null
)
insert into table2 (col2) values ('abc'),(null)
create table table1 (
col1 varchar(10) null
)
insert into table1 (col1) values ('abc'),('def')
select * from table1 where col1 not in (select col2 from table2)
Produces no rows. This is because the result of NOT IN becomes UNKNOWN once a NULL comparison occurs.
You can fix it with:
select * from table1 where col1 not in (select col2 from table2 where col2 is not null)
If that's the correct logic for your situtation.
As others have already pointed to the reason that cause this issue, you can achieve the same results, using LEFT JOIN and it safe than the predicate IN with the NULL vlaues:
select t1.*
from [DB1.table1] AS T1
LEFT JOIN DB2.table2 AS t2 ON t1.col1 = t2.col2
where t1.col2 IS NULL;
Related
I have a query like:
SELECT col1,col2 from table1 where col1 in (:var);
and this :var has a value like "'1234', '5678'" which is a string consisting of single quotes and commas in it. I want to convert this string to a type which can be given as input to the SQL 'in' operator, something like this:
SELECT col1, col2 from table1 where col1 in (STRING_SPLIT(:var));
This is the code as solution to achieve desired result in SQL server query.
DECLARE #var AS NVARCHAR(100) = '''1234'', ''5678''';
SELECT col1, col2 FROM table1 WHERE col1 IN (SELECT LTRIM(RTRIM(value)) FROM STRING_SPLIT(#var, ','))
You can't use STRING_SPLIT to expand a delimited literal string into multiple delimited literal strings. STRING_SPLIT('abc,def',',') doesn't result in 'abc','def', it results in a data set of 2 rows, containing the values 'abc' and 'def'.
If you want to pass a delimited string, you need to either JOIN/CROSS APPLY to STRING_SPLIT or use a subquery:
SELECT T1.Col1,
T1.Col2
FROM dbo.table1 T1
JOIN STRING_SPLIT(#YourVariable,',') SS ON T1.Col1 = SS.Value;
SELECT T1.Col1,
T1.Col2
FROM dbo.table1 T1
WHERE T1.Col1 IN (SELECT SS.Value
FROM STRING_SPLIT(#YourVariable,',') SS);
You may, however, find even better performance with an indexed temporary table, if you are dealing with large data sets:
CREATE TABLE #temp (Value varchar(30) PRIMARY KEY); --Use an appropriate data type. I assume unique values in the delimited string
INSERT INTO #temp (Value)
SELECT SS.Value
FROM STRING_SPLIT(#YourVariable,',') SS;
SELECT T1.Col1,
T1.Col2
FROM dbo.table1 T1
JOIN #Temp T ON T1.Col1 = T.Value;
Finally, which may be better again, you could use a table type parameter. Then you would, like the above, just JOIN to that or use an EXISTS.
I have the below sample code. Assuming I do not know if a particular column exists in a table or not, how could I write the query in a way that I can default the column value to 'Not available' if the column doesn't exist in the table?
Example:
select COL1, COL2,
CASE
WHEN OBJECT_ID('COL3') IS NULL THEN 'Not Available'
ELSE COL3
END AS COL3
from TABLE1
Thanks in advance.
This is quite tricky to do (without dynamic SQL), but there is a way by playing with the scoping rules in SQL. You can do this assuming you have a unique or primary key in the table:
select t1.col1, t1.col2,
(select col3 -- no alias!
from table1 tt1
where tt1.id = t1.id -- the primary/unique key
) col3
from table1 t1 cross join
(values ('Not Available')) v(col3) -- same name
The subquery will fetch col3 from the table1 in the subquery if it exists. Otherwise it will reach out and find col3 from the values() clause.
I have an insert statement like the following which gets sytax error of "the multi-part identifier "t2.Col1" could not be bound.". I over simplified the statement and it looks like below:
INSERT INTO dbo.T1
(
Col1,
Col2,
Col3
)
SELECT
t2.Col1,
SUBSTRING(aCase.CaseColumn, 0, CHARINDEX('%', aCase.CaseColumn)), --I expect this line gets the value "2"
SUBSTRING(aCase.CaseColumn, CHARINDEX('%', aCase.CaseColumn) + 1, LEN(aCase.CaseColumn) - CHARINDEX('%', aCase.CaseColumn)) --I expect this line gets the value "3"
FROM
dbo.T2 t2
LEFT JOIN
(
SELECT
CASE --I have hundreds of WHEN conditions below and need to access the parent T2 tables' properties
WHEN t2.Col1 = 1 THEN '2%3' --This line has a syntax error of "the multi-part identifier "t2.Col1" could not be bound."
END AS CaseColumn
)
AS aCase ON 1 = 1
The reason I use LEFT JOIN with CASE is that I have hundreds of conditions for which I need to select different values for different columns. I don't want to repeat the same CASE statements over and over again for all of the columns. Therefore, I use a single CASE which concatenates the values with a delimiter and then I parse that concatenated string and put the appropriate values in it's place.
What you could do is use OUTER APPLY, as it allows your dbo.T2 and the aCase resultset to be related, like this:
INSERT INTO dbo.T1
(
Col1,
Col2,
Col3
)
SELECT
1,
SUBSTRING(aCase.CaseColumn, 0, CHARINDEX('%', aCase.CaseColumn)), --I expect this line gets the value "2"
SUBSTRING(aCase.CaseColumn, CHARINDEX('%', aCase.CaseColumn) + 1, LEN(aCase.CaseColumn) - CHARINDEX('%', aCase.CaseColumn)) --I expect this line gets the value "3"
FROM
dbo.T2 t2
OUTER APPLY
(
SELECT
CASE --I have hundreds of WHEN conditions below and need to access the parent T2 tables' properties
WHEN t2.Col1 = 1 THEN '2%3'
END AS CaseColumn
)
AS aCase ON 1 = 1
That is because the result of the subquery is not indipendent itself, it has to be defined based on the values of the dbo.T2 table.
Read more about OUTER APPLY and CROSS APPLY on this thread.
Number 3, "Reusing a table alias" is similiar to your case and the article linked to it perfectly explains how to use cross apply/outer apply in these cases.
When using a join to a subquery, inside that subquery it doesn't know what t2 is, unless you select from a table aliased as t2 in that subquery.
And you could change that LEFT JOIN to an OUTER APPLY.
But you don't really need to JOIN or OUTER APPLY in this case.
Just select from T2 with the CASE in the subquery.
INSERT INTO dbo.T1
(
Col1,
Col2,
Col3
)
SELECT
Col1,
SUBSTRING(CaseColumn, 1, CHARINDEX('%', CaseColumn)-1),
SUBSTRING(CaseColumn, CHARINDEX('%',CaseColumn)+1, LEN(CaseColumn))
FROM
(
SELECT
Col1,
CASE Col1
WHEN 1 THEN '2%3'
-- more when's
END AS CaseColumn
FROM dbo.T2 t2
) q
Note how the CASE and the SUBSTRING's were changed a little bit.
Btw, personally I would just insert the distinct Col1 into T1, and just update Col2 and Col3 in that reference table manually. That could prove to be faster than writing those hundreds conditions. But then again, you did say this was simplified a lot.
Need to check OR condition between multiple columns.
select * from tableA where (COL1 or Col2) is not null
how to fetch this?
My actual query is something like :
select b.empname,b.hours,b.minutes from table1 a,table2 b
where a.pk_id=b.fk_id and a.type='UT'
and a.SOME_NUMBER='123' and b.hours is not null or b.minutes is not null;
but
b.hours is not null or b.minutes is not null;
is fetching repeated records
select b.empname,b.hours,b.minutes from table1 a,table2 b
where a.pk_id=b.fk_id and a.type='UT'
and a.SOME_NUMBER='123' and COALESCE(b.hours,b.minutes) is not null;
worked fine for me.
Thanks everyone for the input
Just use
select * from tableA where COL1 is not null or Col2 is not null
Its just select * from tableA where COL1 IS NOT NULL or Col2 IS NOT NULL.
A table called table1 has a field called field1 with null values in it. This query does not return any rows:
SELECT *
FROM table1
WHERE field1 NOT IN
(
SELECT field1
FROM table1
)
I know there are better ways of writing this query. What causes this behaviour i.e. using not in with a field that contains null values.
OK, I might be missing the point here, but one way to rephrase your query is:
Step 1: Take all values of table1.field1.
Step 2: Return all table1 rows whose field1's value isn't among the values obtained in Step 1.
Surely this always returns the empty set?
In assumption that there is primary key field exists in table, you may use this script:
select
t1.*
from
dbo.Table1 as t1
where
t1.field1 = 'Value1'
and not exists( select 1 from Table1 as t2 where t1.ID != t2.ID and t2.field1 = 'Value2')