"NOT LIKE x" and "LIKE x" doesn't sum up? - sql

I don't quite get it...
Could somebody please give me a hint on why the results of queries B + C won't add-up to A?
I first thought, that the amount of underscores (should be ten) mismatch between B and C because of a typo, but after copy/pasting I am a bit helpless. The result of A is higher than the sum of B + C.
Is there some kind of implicit distinct etc. in statement B and C that I am not aware of?
-- statement A
select count(*) from mytable;
-- statement B
select count(*) from mytable where mycolumn like '__________';
-- statement C
select count(*) from mytable where mycolumn not like '__________';

If mycolumn has some rows with NULL values, those will be excluded from both LIKE and NOT LIKE clauses.
Therefore, those 2 statements should be equal:
SELECT (select count(*) from mytable where mycolumn like '__________')
+ (select count(*) from mytable where mycolumn not like '__________')
+ (select count(*) from mytable where mycolumn IS NULL)
FROM DUAL
-- is equal to
select count(*) from mytable;

Most likely your mycolumn contains NULL values.
NULL values won't compare to LIKE or NOT LIKE.

When you add the result of this, it will add up:
select count(*) from mytable where mycolumn is null;
The reason behind this is that null is considered 'undefined'. So you can't say something you don't know is like, or not like something else. It is undefined. Comparison to null, except when using is null will always return false.

Your column contains NULL values. When you compare anything to NULL (even another NULL), the result is false.
So in your example, there are those that are like your pattern, those that are not like your pattern and the NULL values, that are neither.

Related

Why Using COALESCE or CASE keep returning null

I have the following SQL Query :
(SELECT ROUND(SUM(NBTOSUM)/1000000,1) FROM MyTable t2 WHERE t2.ELEMNAME IN ('A','B','C'))
Which works fine.
But Where there is no 'A','B','C' the result of the select is (null)
So to handle it, I did the following :
(SELECT COALESCE(ROUND(SUM(NBTOSUM)/1000000,1),0) FROM MyTable t2 WHERE t2.ELEMNAME IN ('A','B','C'))
And also try :
(SELECT
CASE
WHEN SUM(NBTOSUM)/1000000 IS NULL THEN 0
ELSE ROUND(SUM(NBTOSUM)/1000000,1)
END
FROM MyTable t2 WHERE t2.ELEMNAME IN ('A','B','C'))
But both keep returning null
What am I doing wrong ?
Move the WHERE restrictions to the CASE expression as well:
SELECT ROUND(SUM(CASE WHEN t2.ELEMNAME IN ('A','B','C')
THEN NBTOSUM ELSE 0 END) / 1000000, 1)
FROM MyTable t2;
Note that this trick solves the null problem and also avoids the need for an ugly COALESCE() call.
Your code should work as the SUM aggregation function will generate a single row of output regardless of whether the number of input rows is zero or non-zero. If there are no input rows or the values are all NULL then the output of the SUM will be NULL and then COALESCE would work.
Since you claim it does not then that suggests that there is something else going on in your query that you have not shared in the question.
You have braces around your statement suggesting that you are using it as part of a larger statement. If so, you can try moving the COALESCE to the outer query:
SELECT COALESCE(
(
SELECT ROUND(SUM(NBTOSUM)/1000000,1)
FROM MyTable
WHERE ELEMNAME IN ('A','B','C')
),
0
)
FROM your_outer_query;
That might fix the problem if you are somehow correlating to an outer query but your question makes no mention of that.
fiddle

COUNT inside CASE WHEN is causing Invalid Column error

select case
when COUNT(*)>0 THEN (select TOP 1 A.a1)
else 'none'
end
from A
where A.a1 > 10
order by A.a1
Code above is causing the following error:
Column 'A.a1' is invalid in the select list because it is not
contained in either an aggregate function or the GROUP BY clause.
I could not understand why.
My intention is as follows: If there are rows where A.a1 is greater than 10, order them and take the top rows's a1 value. If there is no such row, select 'none'.
EDIT: It is a simplified version of the actual code that is going to be used in a subquery. So, I cannot use IF..ELSE statements.
You have an aggregation query because you have count(*). These are tricky with subqueries. Instead, you can use:
select (case when count(*) > 0 then A1.a1 else 'none'
end)
from A left join
(select top 1 A.a1) A1
on 1 = 1
where A1.a1 > 10
The logic of this is a bit non-sensical. I assume your actual query is more useful.
I suggest you try with the UNION ALL to solve your Issue. Below Code is for your ref.
DECLARE #T TABLE(ID INT,NAME VARCHAR(200))
INSERT INTO #T VALUES(11,'HAI')
INSERT INTO #T VALUES(12,'H')
SELECT * FROM #T
DECLARE #VAL INT=10
SELECT TOP 1 T.NAME
FROM #T T
WHERE T.ID>#VAL
UNION ALL
SELECT 'None'
WHERE NOT EXISTS (SELECT TOP 1 ID FROM #T WHERE ID>#VAL)
RE: Column 'A.a1' is invalid in the select list because it is not contained in either an aggregate function or the GROUP BY clause.
COUNT(*) is an aggregation. SQL Server expects all columns to be in a GROUP BY.
RE: If there are rows where A.a1 is greater than 10, order them and take the top rows's a1 value. If there is no such row, select 'none'.
RE: EDIT: It is a simplified version of the actual code that is going to be used in a subquery. So, I cannot use IF..ELSE statements.
ORDER by a1 value and take TOP 1 == MAX(a1).
Assuming a1 is int orfloat, then the value must be converted to
varchar to handle the 'none' output. LOWER works and requires less typing than CAST or CONVERT.
Code:
SELECT ISNULL(LOWER(MAX(a1)), 'none') FROM A WHERE A.a1 > 10

Selecting filtered rows with SQL

I am constructing an SQL statement with some parameters. Finally, an SQL statement is created like
"select * from table where column1 = "xyz"".
But I also need the rows which are filtered with this statement. In this case they're rows which are not "xyz" valued in column1. More specifically, I am looking for something like INVERSE(select * from table where ...). Is it possible?
Edit: My bad, I know I can do it with != or operator. Here the case is, select statement may be more complex (with some ANDs and equal, greater operators). Let's assume a table has A,B,C and my SQL statement brings only A as result. But I need B and C while I only have the statement which brings A.
select * from table where column1 != 'xyz' or column1 is null;
If you want the other ones, do it like this:
select * from table where column1 <> "xyz"
column1 <> (differs from) "xyz"
To check if something is no equal you can use <> or even !=
SELECT *
FROM yourTable
WHERE <> 'xyz'
OR
SELECT *
FROM yourTable
WHERE != 'xyz'
Many database vendors support (see list) both versions of the syntax.
If you're retrieving both result sets at about the same time, and just want to process the xyz ones first, you could do:
select *,CASE WHEN column1 = "xyz" THEN 1 ELSE 0 END as xyz from table
order by CASE WHEN column1 = "xyz" THEN 1 ELSE 0 END desc
This will return all of the rows in one result set. Whilst xyz = 1, these were the rows with column1 = 'xyz'.
It was :
"select * from table where rowId NOT IN (select rowId from table where column1 = "xyz")
I needed a unique rowId column to achieve this.

Select rows that contain both English and non-English characters

Ok so I have a column in a table in SQL Server.
Each records have a string (i.e. names)
SOME of the strings have English AND NON ENGLISH characters.
I have to select ONLY the records that have English AND NON English characters.
How do I go about doing that?
My try...
Select * from Table
Where Nameofthecolumn NOT LIKE '%[A-Z]%'
Go
This will give me EMPTY table.
I know for sure that there are at least two records that have english and non-english characters.
I need those two records as output.
I was trying to do
Select * from Table
Where Nameofthecolumn NOT LIKE '%[A-Z,a-z]%' AND Like '%[A-Z,a-z]%'
Go
but turns out you can use boolean with Like/Not Like.
Please guide me the right direction.
Thanks
How about reversing your search, e.g. find anything that doesn't match A-Z:
... WHERE col LIKE '%[^A-Z]%' AND col LIKE '%[A-Z]%';
If the collation is case insensitive you shouldn't need a-z, if it is case sensitive you could add the COLLATE clause. But you may want to filter out spaces, numbers and other non-alphanumerics that are expected.
Do you mean something like this?
select 1 as field1,'1' as field2 into #data
union all select 1,'abc'
union all select 2,'abc'
union all select 3,'999'
SELECT * FROM
(
select field1,field2
,MAX(CASE WHEN field2 NOT LIKE '%[A-Z,a-z]%' THEN 1 ELSE 0 END) OVER (PARTITION BY field1)
+ MAX(CASE WHEN field2 LIKE '%[A-Z,a-z]%' THEN 1 ELSE 0 END) OVER (PARTITION BY field1) as chars
FROM #data
) alias
WHERE chars =2

How to return multiple values using case statement in oracle

I want to return multiple values from a query in oracle. For ex:
select count(*)
from tablename a
where asofdate='10-nov-2009'
and a.FILENAME in (case
when 1 = 1 then (select distinct filename from tablename
where asofdate='10-nov-2009' and isin is null)
else null
end);
I am getting error: ora 01427 single row subquery returns more than one row
Please advice.
Thanks, Deepak
A CASE statement cannot return more than one value, it is a function working on one value.
It is not required for your statement, this statement should work:
select count(*)
from tablename a
where asofdate='10-nov-2009'
and a.FILENAME in (select distinct filename
from tablename
where asofdate='10-nov-2009'
and isin is null);
Maybe you have another usage scenario in mind? Something like this:
Select *
From aTable
Where in CASE
WHEN Then
WHEN Then
ELSE END
Then using CASE may not be the right scenario. Maybe this helps you in the right direction:
Select *
From aTable
Where <Case1> and column1 in <Subselect1>
Or <Case2> and column1 in <Subselect2>
OR Not (<Case1> Or <Case2>) and column1 in <Subselect3>
But this will probably be quite some work for the optimizer ...
The distinct in your Case statement is attempting to return multiple values when only one is allowed, and your SELECT statement will only return one value in one row currently. If you're trying to get the count of each filename, do
SELECT FileName, Count(*)
FROM tablename
WHERE asofdate='10-nov-2009' and isin is null
GROUP BY FileName
Run this query:
select distinct filename from tablename
where asofdate='10-nov-2009' and isin is null
You'll see that it returns more than a single row which causes the ORA-01427.
For all I can tell, you're looking for something like:
select a.filename, count(*)
from tablename a
where a.asofdate = '10-nov-2009'
and exists (
select *
from tablename b
where b.isin is null
and a.asofdate = '10-nov-2009'
and a.filename = b.filename
)
group by a.filename
This would find the count of filenames for a day, for which there exists at least one row where isin is null.
If you edit your question and add an explanation of what you're looking for, you might get better answers.