ISNULL() returning NULL - sql-server

Why does the following simple query return null when there are no matching rows (<Condition> is not met by any row)?
SELECT ISNULL(MyField, 0) FROM [MyTable] WHERE <Condition>
I have tried COALESCE() as well, with similar results. How can I return zero when there are no matching rows?

This will work, provided you expect condition to reduce the result set to either 0 or 1 row:
SELECT ISNULL((SELECT MyField FROM [MyTable] WHERE <Condition>),0)
That is, create an outer query with no FROM clause (which will therefore always generate exactly one row) and then use a subquery to obtain your 0 or 1 row of actual data.

Use this:
SELECT ISNULL(COUNT(MyField), 0) FROM [MyTable] WHERE <Condition>
It'll return 0 if row is missing.

You cannot convert 0 rows to 1 row with a null value with any sql built-in fuction because that may cause mis-interpretation of data
But you can customize your result using the below logic (same as you do in .net).
If (select COUNT(MyField) FROM [MyTable] WHERE <Condition>)=0
select 0 as [MyField]
else
SELECT ISNULL(MyField, 0) as [MyField] FROM [MyTable] WHERE <Condition>

Related

SSIS SQL Task Unable to assign Parameter Value when using IF EXISTS

I'm using VS 2012 and SQL Server / SSIS.
I originally had a SQL task to check for duplicate values in a table:
SELECT COUNT(*) AS DupNI
FROM dbo.mytable
WHERE XMLFileID = ?
GROUP BY XMLFileID, NINumber
HAVING (COUNT(*) > 1);
The ? is because I am inserting a parameter value, and the result of the query is being assigned to a variable. It works fine if there is a duplicate.
When there are no duplicates, I get this message:
Single Row result set is specified, but no rows were returned
So, to get round this I now use an IF EXISTS, like the below:
IF EXISTS (SELECT COUNT(*) AS DupNI
FROM dbo.mytable
WHERE XMLFileID = ?
GROUP BY XMLFileID, NINumber
HAVING (COUNT(*) > 1))
SELECT COUNT(*) AS DupNI
FROM dbo.mytable
WHERE XMLFileID = ?
GROUP BY XMLFileID, NINumber
HAVING (COUNT(*) > 1)
ELSE
SELECT 0 AS DupNI;
However, now I get the error:
No value given for one or more required parameters.
It appears because I am wrapping the statement in the IF EXISTS, I can no longer inject the parameter values via the ?
Why is this? How do I get around this issue?
Your current query will return multiple rows if there are duplicates, one for each duplicate (XMLFileID, NINumber) pair. If you only want to return a value which indicates whether there are any duplicates in the table, you could use your EXISTS clause as an expression:
SELECT CASE WHEN EXISTS(
SELECT COUNT(*) AS DupNI
FROM dbo.mytable
WHERE XMLFileID = ?
GROUP BY XMLFileID, NINumber
HAVING (COUNT(*) > 1)
) THEN 1 ELSE 0 END AS [Duplicates Exist]
Demo on dbfiddle

Execution of ISNULL in SQL Server

Here is how i am using ISNULL condition to check for student address.
It works fine but how ISNULL function treat the null codition i.e the second parameter which is display if first condition is null.
Will it calculate Value for second parameter when first condition is not null?
select
...
...
(CASE
WHEN st.ADDRESS='Y' THEN st.LOCATION
ELSE
ISNULL(
(SELECT TOP 1 STDLOC.LOCATION FROM STDLOC
INNER JOIN COMLOC ON STKLOC.LOCATION=COMLOC.CODE AND COMLOC.ADDRESS='Y'
WHERE STDLOC.ZIBCODE=st.ZIBCODE)
,(SELECT TOP 1 COMLOC.LOCATION FROM COMLOC COMLOC.ZIBCODE=st.ZIBCODE))
END
) AS STDUDENTLOCATION
FROM STUDENT st
Both queries inside the ISNULL will be executed, even if the first query will return a value.
Here is a simple test I've made:
Create and populate sample table:
DECLARE #T AS TABLE
(
Col int
)
INSERT INTO #T Values(1),(2)
SELECT ISNULL(
(SELECT TOP 1 Col FROM #T ORDER BY Col DESC),
(SELECT TOP 1 Col FROM #T ORDER BY Col )
)
Execution plan image:
As you can clearly see, the execution plan includes both queries.
I also was looking for an answer. After some reading I came out with my own way to check it.
Deviding by zero will give an error, so we can try:
SELECT ISNULL( (SELECT TOP 1 object_id FROM sys.columns), 5 / 0)
This will give correct result. BUT
SELECT ISNULL( (SELECT TOP 0 object_id FROM sys.columns), 5 / 0)
It will throw an error, because result of first query gives NULL so it tries the second query which fails
ISNULL is a T-SQL specific function that will use the specified second parameter as the return value if the first parameter is NULL(https://msdn.microsoft.com/en-us/library/ms184325.aspx).
Use COALESCE function if you want to return the first non-null value from multiple arguments, and this is a standard function that is supported by all types of relational databases.
This POST provide a good answer for the question:
Is Sql Server's ISNULL() function lazy/short-circuited?

TSQL query optimizer view on non-nullable ISNULL()

As part of some dynamic SQL (ick), I've implemented the 'sort NULLs last' solution described here: Sorting null-data last in database query
ORDER BY CASE column WHEN NULL THEN 1 ELSE 0 END, column
My question is: On non-nullable columns that have ISNULL() applied to them, will the query optimizer strip this out when it realises that it will never apply?
It's not clear why your question mentions the ISNULL function when that isn't in your code.
ORDER BY CASE column WHEN NULL THEN 1 ELSE 0 END, column
First of all this code doesn't work, it is equivalent to CASE WHEN column = NULL which is not what you need.
It would need to be
ORDER BY CASE WHEN column IS NULL THEN 1 ELSE 0 END, column
The optimisation question is easy to test.
CREATE TABLE #T
(
X INT NOT NULL PRIMARY KEY
)
SELECT *
FROM #T
ORDER BY X
SELECT *
FROM #T
ORDER BY CASE WHEN X IS NULL THEN 1 ELSE 0 END, X
DROP TABLE #T
The plan shows a sort operation in the second plan indicating that this was not optimised out as you hoped and the pattern is less efficient than ORDER BY X.

Aggregate Function Error on an Expression

What could be wrong with this query:
SELECT
SUM(CASE
WHEN (SELECT TOP 1 ISNULL(StartDate,'01-01-1900')
FROM TestingTable
ORDER BY StartDate Asc) <> '01-01-1900' THEN 1 ELSE 0 END) AS Testingvalue.
The get the error:
Cannot perform an aggregate function on an expression containing an aggregate or a subquery.
As koppinjo stated what your current (broken) query is doing is checking if you have a NULL-value (or StartDate = '01-01-1900') in your table, return either a 1 or a 0 depending on which, and then attempting to SUM that single value.
There are 2 different logical things you want.
Either getting the amount of rows that has a StartDate or checking if any row is missing StartDate.
SELECT --Checking if there is a NULL-value in table
(
CASE WHEN
(SELECT TOP 1 ISNULL(StartDate,'01-01-1900')
FROM TestingTable
ORDER BY StartDate Asc) <> '01-01-1900' THEN 1
ELSE 0
END
) AS TestingValue
SELECT SUM(TestingValue) TestingValue --Give the count of how many non-NULLs there is
FROM
(
SELECT
CASE WHEN
ISNULL(StartDate,'01-01-1900') <> '01-01-1900' THEN 1
ELSE 0
END AS TestingValue
FROM TestingTable
) T
Here is a SQL Fiddle showing both outputs side by side.
Hard to say, but you probably want something like this:
SELECT
SUM(TestingValue)
FROM
(SELECT
CASE
WHEN ISNULL(StartDate,'01-01-1900') <> '01-01-1900'
THEN 1
ELSE 0
END AS TestingValue
FROM TestingTable) t
As your original query is written now, your subquery will return 1 value overall, so your sum would be 1 or 0 always, not to mention it is illegal. To get around that, this SQL will apply the case statement to every row in the TestingTable and insert the result into a derived table (t), then the 'outer' select will sum the results. Hope this helps!

JOIN ON subselect returns what I want, but surrounding select is missing records when subselect returns NULL

I have a table where I am storing records with a Created_On date and a Last_Updated_On date. Each new record will be written with a Created_On, and each subsequent update writes a new row with the same Created_On, but an updated Last_Updated_On.
I am trying to design a query to return the newest row of each. What I have looks something like this:
SELECT
t1.[id] as id,
t1.[Store_Number] as storeNumber,
t1.[Date_Of_Inventory] as dateOfInventory,
t1.[Created_On] as createdOn,
t1.[Last_Updated_On] as lastUpdatedOn
FROM [UserData].[dbo].[StoreResponses] t1
JOIN (
SELECT
[Store_Number],
[Date_Of_Inventory],
MAX([Created_On]) co,
MAX([Last_Updated_On]) luo
FROM [UserData].[dbo].[StoreResponses]
GROUP BY [Store_Number],[Date_Of_Inventory]) t2
ON
t1.[Store_Number] = t2.[Store_Number]
AND t1.[Created_On] = t2.co
AND t1.[Last_Updated_On] = t2.luo
AND t1.[Date_Of_Inventory] = t2.[Date_Of_Inventory]
WHERE t1.[Store_Number] = 123
ORDER BY t1.[Created_On] ASC
The subselect works fine...I see X number of rows, grouped by Store_Number and Date_Of_Inventory, some of which have luo (Last_Updated_On) values of NULL. However, those rows in the sub-select where luo is null do not appear in the overall results. In other words, where I get 6 results in the sub-select, I only get 2 in the overall results, and its only those rows where the Last_Updated_On is not NULL.
So, as a test, I wrote the following:
SELECT 1 WHERE NULL = NULL
And got no results, but, when I run:
SELECT 1 WHERE 1 = 1
I get back a result of 1. Its as if SQL Server is not relating NULL to NULL.
How can I fix this? Why wouldn't two fields compare when both values are NULL?
You could use Coalesce (example assuming Store_Number is an integer)
ON
Coalesce(t1.[Store_Number],0) = Coalesce(t2.[Store_Number],0)
The ANSI Null comparison is not enabled by default; NULL doesn't equal NULL.
You can enable this (if your business case and your Database design usage of NULL requires this) by the Hint:
SET ansi_nulls off
Another alternative basic turn around using:
ON ((t1.[Store_Number] = t2.[Store_Number]) OR
(t1.[Store_Number] IS NULL AND t2.[Store_Number] IS NULL))
Executing your POC:
SET ansi_nulls off
SELECT 1 WHERE NULL = NULL
Returns:
1
This also works:
AND EXISTS (SELECT t1.Store_Number INTERSECT SELECT t2.Store_Number)

Resources