Which approach is better to get output of all the entries if the input is NULL or a specific record when input is NOT NULL?
This is for PL/SQL on Oracle Database. Let me know if my first approach is wrong
select * from student where (roll_no = :ROLL_NO or :ROLL_NO is NULL);
OR
select * from student where roll_no = NVL(:ROLL_NO, roll_no);
The two approaches will give different results where roll_no is null, because roll_no = roll_no will not be true for those rows.
If there is an index, the optimiser has a special case for somecol = nvl(:param,somecol), and you will see two FILTER operations and a CONCATENATION in the execution plan, representing the cases where :param is null or not null. Therefore I would use the nvl expression so long as roll_no is defined as a NOT NULL column.
In general, using operators is a better approach. This is because the optimizer can be smarter when the columns are not arguments to functions.
This answer assumes that roll_no is not null.
Although this could be a case that the Oracle optimizer sometimes catches, you might find that writing the query using union all actually produces a better execution plan when an appropriate index is present:
select s.*
from student s
where roll_no = :ROLL_NO
union all
select s.*
from student s
where :ROLL_NO is NULL;
Seconds approach would give same result with small modification(cannot compare null against null):
select * from student where NVL(roll_no,1) = NVL(NVL(:ROLL_NO, roll_no),1);
William Robertson made a good point. I understood my mistake then.
I tried the answer by Gordon linoff. It worked.
I think waldemort's answer will also be true, but i didn't try it.
This answer below is what was my solution for it and it might be similar to waldemort's answer.
{select * from student where AND (roll_no = nvl(roll_no,1) = nvl(:roll_no,1)}
Thanks for the help guys
Related
If i use a table containing at least 2 columns like this :
[User]
[FirstName]|[LastName]
Robert-Dupont
Louis-Dupont
Georges-Andre
Assuming that there is a huge amount of data and that there is much more unique result in 'FirstName' than in 'LastName', which query is the fastest :
Query 1 :
SELECT * USER WHERE LastName = 'Dupont' AND FirstName = 'Robert'
Query 2:
SELECT * USER WHERE FirstName = 'Robert' AND LastName = 'Dupont'
Is there a difference ?
Thanks for your help !
The order of the comparisons does not matter. SQL Server should optimize the WHERE clause for the data.
However, if you care about performance, you should use an index, either user(firstname, lastname) or user(lastname, firstname). The order of comparisons is not the right thing to be thinking about.
an index of below form
(firstname,lastname)
Can help in both the queries..
firstname='' and lastname=''
or
lastname='' and fastname=''
so your both queries are faster,but when you issue query of below form ,with same index
firstname>='' and lastname=''
Firstname uses index and SQLServer can't use index on second column ,but it uses residual lookup to filter out values
Both query are same faster. Because 'where' clause includes conditions. That is not important unique columns .
it's exactly the same when both are on the same level. but this is definitely better:
SELECT *
FROM (SELECT *
FROM USER
WHERE LastName = 'Dupont')
WHERE FirstName = 'Robert'
EDIT
Going back to Relational Algebra, it's better to filter any results as far down the tree as possible. Assuming that last names are rarer than first names, you 1st filter the 'Duponts' and then look for the 'Roberts' in the result set.
I use this pattern for optional filter paramaters in my SQL Stored Procedures
AND (
#OptionalParam IS NULL OR
(
Id = #OptionalParam
)
)
However the OR is not a friend of the query optimizer. Is there a more efficient way to do this without using dynamic SQL
You can try using COALESCE. Not sure if it will be more efficient.
AND Id = Coalesce(#OptionalParam, Id)
This will not work if Id itself is null and you are using ANSI nulls.
AND ID = ISNULL(#OptionalParam, ID)
or you if you had multiple optional parameters can use
AND ID = COALESCE(#OptionalParam1, #OptionalParam2, ID)
This is definitely faster than using an OR statement.
Like the other answerer mentioned, this will not work if the ID column is null (but then again, the original statement wouldn't either).
You could try:
AND Id = CASE WHEN #OptionalParam IS NULL THEN Id ELSE NULL END
I doubt this will optimize much better, but there's no OR in it.
Alternatively, you could break your query apart into two components -- one with just an #OptionalParam IS NULL test and another with an Id = #OptionalParam test, then UNION them together. Depending on your data topology this might yield better results. It could also be significantly worse.
Also what will be the scenarios where this query is used
select * from TableA where exists
(select null from TableB where TableB.Col1=TableA.Col1)
As the query is in an EXISTS then you can return anything. It is not even evaluated.
In fact, you can replace the null with (1/0) and it will not even produce a divide by zero error.
The NULL makes no sense. It's simply bad SQL.
The exists clause is supposed to use SELECT *.
People make up stories about the cost of SELECT *. They claim it does an "extra" metadata query. It doesn't. They claim it's a "macro expansion" and requires lots of extra parse time. It doesn't.
The EXISTS condition is considered "to be met" if the subquery returns at least one row.
The syntax for the EXISTS condition is:
SELECT columns
FROM tables
WHERE EXISTS ( subquery );
Please note that "Select Null from mytable" will return number of rows in mytable but all will contain only one column with null in the cell as the requirement of outer query is just to check whether any row fall in the given given condition like in your case it is "TableB.Col1=TableA.Col1"
you can change null to 1, 0 or any column name available in the table. 1/0 may not be a good idea :)
It's a tacky way of selecting all records in TableA, which have a matching record (Col1=Col1) in TableB. They might equally well have selected '1', or '*', for instance.
A more human-readable way of achieving the same would be
SELECT * FROM TableA WHERE Col1 IN ( SELECT Col1 IN TableB )
Please, please, all ....
EXISTS returns a BOOLEAN i.e. TRUE or FALSE. If the result set is non empty then return TRUE. Correlation of the sub-query is important as in the case above.
i.e Give me all the rows in A where AT LEAST one col1 exists in B.
It does not matter what is in the select list. Its just a matter of style.
Want to improve this post? Provide detailed answers to this question, including citations and an explanation of why your answer is correct. Answers without enough detail may be edited or deleted.
I'm using a stored procedure in MySQL, with a CASE statement.
In the ELSE clause of the CASE ( equivalent to default: ) I want to select and return an empty result set, thus avoiding to throw an SQL error by not handling the ELSE case, and instead return an empty result set as if a regular query would have returned no rows.
So far I've managed to do so using something like:
Select NULL From users Where False
But I have to name an existing table, like 'users' in this example.
It works, but I would prefer a way that doesn't break if eventually the table name used is renamed or dropped.
I've tried Select NULL Where False but it doesn't work.
Using Select NULL does not return an empty set, but one row with a column named NULL and with a NULL value.
There's a dummy-table in MySQL called 'dual', which you should be able to use.
select
1
from
dual
where
false
This will always give you an empty result.
This should work on most DBs, tested on Postgres and Netezza:
SELECT NULL LIMIT 0;
T-SQL (MSSQL):
SELECT Top 0 1;
How about
SELECT * FROM (SELECT 1) AS TBL WHERE 2=3
Checked in myphp, and it also works in sqlite and probably in any other db engine.
This will probably work across all databases.
SELECT * FROM (SELECT NULL AS col0) AS inner0 WHERE col0 IS NOT NULL;
SELECT TOP 0 * FROM [dbo].[TableName]
This is a reasonable approach to constant scan operator.
SELECT NULL WHERE FALSE;
it works in postgresql ,mysql, subquery in mysql.
How about this?
SELECT 'MyName' AS EmptyColumn
FROM dual
WHERE 'Me' = 'Funny'
SELECT * FROM (SELECT NULL) WHERE 0
In PostgreSQL a simple
SELECT;
works. You won't even get any columns labeled 'unknown'.
Note however, it still says 1 row retrieved.
A puzzler from a coworker that I cannot figure out...
update btd.dbo.tblpayroll
set empname = ( select b.Legal_Name
from ( SELECT Legal_Name,
Employee_ID
FROM Com.dbo.Workers
WHERE isnumeric(Employee_ID) = 1
) b
where b.Employee_ID = empnum
and b.Legal_name is not NULL
)
where empname is NULL
Msg 245, Level 16, State 1, Line 1
Conversion failed when converting the varchar value 'N0007 ' to data type int. The table alias b would actually be a view.
The value 'N0007 ' is in the Workers table. I don't see why it is not being filtered from the results that are being joined.
EDIT:
The alias does, in fact, return the correct rows - so isNumeric is doing the job.
I suspect that the optimizer is attempting to apply the where clause of the outer select before the inner select. Presumably it thinks it would be able to do an index lookup on Employee_ID resulting in a faster query in this case. Try:
update btd.dbo.tblpayroll
set empname = ( select Legal_Name
from Com.dbo.Workers
where isnumeric(Employee_ID) = 1
and convert(varchar,Employee_ID)
= convert(varchar,empnum)
and Legal_name is not NULL)
where empname is NULL
Converting them all to varchar should take care of it. I don't think it's much less efficient than you wanted orginally since the isnumeric, if done first, would have forced a table scan anyway.
ISNUMERIC() is famously unreliable for what you are trying to do. You'll need an alternative, which I've been asking for here like this one.
The obvious thing is to force the order of comparison, perhaps by getting then name from a view with only numeric Employee_IDs, rather than the full Workers table.
Maybe N is considered currency symbol? You can try to replace IsNumeric with
LIKE REPLICATE('[0-9]',/*length of Employee_ID*/)
or just
LIKE '[0-9]%'
if letter cannot be in the middle