T-SQL using 2 parameters and isNull - sql-server

Could someone help me to understand the logic of this query (T-SQL in SQL Server 2014) in simple way?
Select
c.ContractID
From
dba.contract as c
Inner Join
dba.Person as r on (c.ContractID = r.ContractID
and IsNull(isPrimary, 0) = 1)
The part that I dont understand is the isNull(isPrimary, 0) = 1.
What does that mean? Btw isPrimary is one of the columns in dba.person
Thank you so much!

isNull(isPrimary, 0) = 1
isNull is a function of SQL which is used to verify null variable
and above snippet describe as if isPrimary variable is null then replace this null value with 0. the purpose of this method to handle null pointer exception.

If you want to watch how it works, you can create table in your database:
use [your_database_name];
create table dbo.test_table
(
t int null
);
insert into dbo.test_table
values (0), (1), (2), (NULL);
select t, isnull(t, 0) as function_result
from dbo.test_table

Sample script for you to understand IsNull(isPrimary, 0) = 1 condition gives
the result and helps in handling Null pointer exception.
DECLARE #table AS TABLE (Id int, isPrimary varchar(20))
INSERT INTO #table
SELECT 1,1001 UNION ALL
SELECT 2,1002 UNION ALL
SELECT 3,NULL UNION ALL
SELECT 4,1004
SELECT Id,ISNULL(isPrimary,0) UIdnum FROM #table
SELECT * FROM #table WHERE ISNULL(isPrimary,0)=1
SELECT * FROM #table WHERE ISNULL(isPrimary,0)=0

The IS_NULL function is only replacing the value of isPrimary to 0, in case the isPrimary is equal to NULL.
Your check is only true when isPrimary is not null (because if it is, it will be replaced by 0) AND isPrimary = 1.
SELECT c.contractid
FROM dba.contract AS c
INNER JOIN dba.person AS r ON (c.contractid = r.contractid AND isprimary = 1)
WHERE isprimary IS NOT NULL

Related

SQL Server Conditional CASE JOIN query

Can someone help me out on Conditional join?
I want to join on ID and [Name] from both tables and next condition is like
CASE WHEN a.Count = b.Count THEN a.Rn1 = b.Rn1 WHEN a.Count <>
b.Count THEN a.Rn2 = b.Rn2 and a.Rn3 = b.Rn3 END
DROP TABLE IF EXISTS #Test1
DROP TABLE IF EXISTS #Test2
CREATE TABLE #Test1 (ID int, [name] varchar(50),[Count] int, Rn1 int, Rn2 int, Rn3 int)
CREATE TABLE #Test2 (ID int, [name] varchar(50),[Count] int, Rn1 int, Rn2 int, Rn3 int)
Insert Into #Test1
Values
(123123,'Hours',6,1,1,1)
,(123123,'Hours',6,2,1,2)
,(123123,'Hours',6,3,2,1)
,(123123,'Hours',6,4,3,1)
,(123123,'Hours',6,5,3,2)
,(123123,'Hours',6,6,4,1)
,(123123,'NI1',1,1,1,1)
,(123123,'NI2',1,1,1,1)
,(123123,'PAY',1,1,1,1)
,(123123,'Teachers1',1,1,1,1)
,(123123,'Teachers2',1,1,1,1)
,(123124,N'Hours',5,1,1,1)
,(123124,N'Hours',5,2,2,1)
,(123124,N'Hours',5,3,3,1)
,(123124,N'Hours',5,4,3,2)
,(123124,N'Hours',5,5,4,1)
,(123124,N'NI1',1,1,1,1)
,(123124,N'NI2',1,1,1,1)
,(123124,N'PAY',1,1,1,1)
--SELECT * FROM #Test1
Insert Into #Test2
Values (123123,N'Hours',6,1,1,1)
,(123123,N'Hours',6,2,1,2)
,(123123,N'Hours',6,3,2,1)
,(123123,N'Hours',6,4,3,1)
,(123123,N'Hours',6,5,3,2)
,(123123,N'Hours',6,6,4,1)
,(123123,N'NI1',1,1,1,1)
,(123123,N'NI2',1,1,1,1)
,(123123,N'PAY',1,1,1,1)
,(123123,N'Teachers1',1,1,1,1)
,(123123,N'Teachers2',1,1,1,1)
,(123124,N'Hours',6,1,1,1)
,(123124,N'Hours',6,2,2,1)
,(123124,N'Hours',6,3,3,1)
,(123124,N'Hours',6,4,3,2)
,(123124,N'Hours',6,5,3,3)
,(123124,N'Hours',6,6,4,1)
,(123124,N'NI1',1,1,1,1)
,(123124,N'NI2',1,1,1,1)
,(123124,N'PAY',1,1,1,1)
--SELECT * FROM #Test2
Thanks for your help in advance.
The predicates of a CASE expression (i.e. what follows THEN and ELSE) always has to be a value, not another logical expression. Without seeing your entire query, I can suggest the following refactor:
WHERE
(a.Count = b.Count AND a.Rn1 = b.Rn1) OR
(a.Count <> b.Count AND a.Rn2 = b.Rn2 AND a.Rn3 = b.Rn3)
You're trying to use a CASE expression like a CASE statement. A CASE expression returns a scalar value not a boolean result. So, for example the following syntax is valid:
WHERE CASE [Column] WHEN 'A' THEN 1 ELSE 2 END = OtherColumn;
This is because the CASE expression returns the scalar value 1 or 2. The following statement, would be invalid:
WHERE CASE [Column] WHEN 'A' THEN OtherColumn = 1 ELSE OtherColumn = 2 END;
A CASE in a WHERE in T-SQL needs to be part of a boolean expression ({CASE expression} {=|!=|>|<|etc} {Expression}), not return one.
Using a CASE expression in a WHERE clause, however, makes your query non-SARGable, you are far better off righting out some boolean logic. For what yoiu have that would likely be:
WHERE (A.[Count] = b.[Count]
AND A.RN1 = B.RN1)
OR (A.[Count] != b.[Count]
AND A.RN2 = B.RN2
AND A.RN3 = B.RN3);
It's possible to do something like this with CASE expressions:
WHERE 1 = (CASE WHEN a.Count = b.Count THEN CASE WHEN a.Rn1 = b.Rn1 THEN 1 END
WHEN a.Count <> b.Count THEN CASE WHEN a.Rn2 = b.Rn2 and a.Rn3 = b.Rn3 THEN 1 END
END)
That's a literal translation of the original request without any optimization.

Joining multiple tables returns NULL value

I have 3 different outcomes of CTE that I need to LEFT JOIN each other:
Main Table #Policies contains all VehiclePolicyLimitsID values:
#LiabilityPremium:
#HiredPremium:
As an example I mimic the results of CTE's into 3 table variables:
declare #Policies table (VehiclePolicyLimitsID int)
insert into #Policies values (2101891),
(2101892),
(2101893),
(2101894),
(2119235),
(2119236),
(2119237),
(2119238),
(2190860),
(2190861),
(2190862),
(2190863)
--select * from #Policies
declare #LiabilityPremium table (Quoteid int, ClassCode int, VehiclePolicyLimitsID int, LiabilityPremium money)
insert into #LiabilityPremium values (728436,3199,2101892,1723),
(728436, 23199,2101893,1855),
(728436,68199,2101894,133),
(741626,3199,2119236,0),
(741626,23199,2119237,0),
(741626,68199,2119238,0),
(774168,3199,2190861,0),
(774168,23199,2190862,0),
(774168,68199,2190863,0)
--select * from #LiabilityPremium
declare #HiredPremium table (Quoteid int, ClassCode int, VehiclePolicyLimitsID int, LiabilityPremium money)
insert into #HiredPremium values ( 728436, NULL, 2101891, 25),
(741626, NULL, 2119235, 0),
(774168, NULL, 2190860, 0)
--select * from #HiredPremium
select
COALESCE(l.Quoteid,h.QuoteID,'') as QuoteID,
COALESCE(l.ClassCode,h.ClassCode,'') as ClassCode,
COALESCE(l.VehiclePolicyLimitsID,h.VehiclePolicyLimitsID,'') as VehiclePolicyLimitsID,
l.LiabilityPremium + h.LiabilityPremium as LiabilityPremium
from #Policies p
left join #LiabilityPremium l ON l.VehiclePolicyLimitsID = p.VehiclePolicyLimitsID
left join #HiredPremium h ON h.VehiclePolicyLimitsID = p.VehiclePolicyLimitsID
But for some reason the outcome of LiabilityPremium is all NULL's:
I would expect the result looks like this with total LiabilityPremium = $3,736
Is any way to join somehow to receive desirable result?
That is because null on either side of the addition operator will yield a result of null. You can use ISNULL(LiabilityPremium, 0) Example:
ISNULL(l.LiabilityPremium,0) + ISNULL(h.LiabilityPremium,0) as LiabilityPremium
or you can use COALESCE instead of ISNULL.
COALESCE(l.LiabilityPremium,0) + COALESCE(h.LiabilityPremium,0) as LiabilityPremium
Edit
I am not sure if this is coincidence with this small data set or expected but if it is always expected that either #LiabilityPremium.LiabilityPremium or #HiredPremium.LiabilityPremium will always be null then there is no need to perform addition. Instead use COALESCE directly on those 2 columns.
COALESCE(l.LiabilityPremium, h.LiabilityPremium) as LiabilityPremium
COALESCE(l.LiabilityPremium,0) + COALESCE(h.LiabilityPremium,0) as LiabilityPremium
That's because of
l.LiabilityPremium + h.LiabilityPremium
If any of the two is NULL, the expression is NULL.
This expression should fix it
COALESCE(l.LiabilityPremium, 0.00) + COALESCE(h.LiabilityPremium, 0.00)

Sql Server UDF behaves differently when a variable with value null is passed than when constant null is passed

I'm writing a stored procedure with quite a lot of expensive work to do that may or may not take a filter parameter. Doing the filtering is itself quite expensive, and the table being filtered is large. I just tried to change the inner filtering function so throw an error if called with invalid parameters, as a warning to developers not to use it that way.
BUT - If I call my outer test function with NULL, it works as I'd expect, not calling the inner function and not throwing the error. If I call my outer test function with a variable with the VALUE of NULL, then it calls the filter function with a null parameter, and throws the error, even thought the code only says to call the function when the value is not null.
What's going on here?
Much simplified example:
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[MyTable]') AND type in (N'U')) DROP TABLE MyTable
GO
CREATE TABLE MyTable (Pk int, Field int)
GO
INSERT INTO MyTable VALUES (1, 1)
INSERT INTO MyTable VALUES (2, 4)
INSERT INTO MyTable VALUES (3, 9)
INSERT INTO MyTable VALUES (4, 16)
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[FilterRows]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) DROP FUNCTION FilterRows
GO
CREATE FUNCTION FilterRows(#searchParameter int)
RETURNS #Pks TABLE
(
Pk int
)
AS
BEGIN
IF (#searchParameter IS null)
BEGIN
-- This is bad news. We don't want to be here with a null search, as the only thing we can do is return every row in the whole table
-- RAISERROR ('Avoid calling FilterRows with no search parameter', 16, 1)
-- we can't raise errors in functions!
-- Make it divide by zero instead then
INSERT INTO #Pks SELECT Pk FROM MyTable WHERE 1/0 = 1
END
ELSE
BEGIN
INSERT INTO #Pks SELECT Pk FROM MyTable WHERE Field > #searchParameter
END
RETURN
END
GO
IF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[OuterFunction]') AND type in (N'FN', N'IF', N'TF', N'FS', N'FT')) DROP FUNCTION OuterFunction
GO
CREATE FUNCTION OuterFunction(#searchParameter int)
RETURNS TABLE AS
RETURN
SELECT *
FROM
MyTable
WHERE
(#SearchParameter IS NULL) OR (#searchParameter IS NOT NULL AND Pk IN (SELECT Pk FROM dbo.FilterRows(#searchParameter)))
GO
SELECT * FROM dbo.OuterFunction(2) -- Returns filtered values
SELECT * FROM dbo.OuterFunction(null) -- returns everything, doesn't call FilterRows
DECLARE #x int = null
SELECT * FROM dbo.OuterFunction(#x) -- WTF! Throws error!
The difference when a value null is passed than when constant null is passed is the same difference between using (is Null) and (= null)
#var = null -- considered as false
#var is null -- considered as unknown
for more details : SQL is null and = null
so if you want to make behavior of both (calling constant null & pass Null value) is the same, use the following tricky although I don't prefer this one.
Alter FilterRows function to be
IF (#searchParameter = null)
--IF (#searchParameter is null)
Note: sorry for typing this answer here, it is supposed to be comment instead of answer, the rule is "You must have 50 reputation to comment" and I have only 22 :(
I think what's going on is that in
SELECT * FROM MyTable WHERE (#SearchParameter IS NULL) OR
(#searchParameter IS NOT NULL AND Pk IN (SELECT Pk FROM dbo.FilterRows(#searchParameter)))
The query analyzer can see that the subquery
(SELECT Pk FROM dbo.FilterRows(#searchParameter))
does not depend on any values from MyTable. As it's constant for all rows, it runs that subquery first, in order to join MyTable to the results. So it executes it before evaluating the WHERE clause where it tests whether #searchParameter IS NULL or not.
When #searchParameter is just "NULL" and not a variable with value NULL, then the analyzer can short-circuit the whole where clause in the execution plan and so knows not to pre-calculate the subquery.
Or, something like that.

Check if a SQL Select statement returns no rows

I have a select statement
SELECT QBalance
FROM dbo.CustomerBalance
WHERE (CustomerID = 1) AND (MarchentID = #MerchantId)
I want to check if that statement returns 0 rows. I tried to use the ISNULL and IFNULL but it seems that I'm missing something.
To find out whether no matching rows exist you can use NOT EXISTS. Which can be more efficient than counting all matching rows
IF NOT EXISTS(SELECT * FROM ...)
BEGIN
PRINT 'No matching row exists'
END
If this is SQL Server, try ##ROWCOUNT.
SELECT COUNT(*)
FROM dbo.CustomerBalance
WHERE (CustomerID = 1) AND (MarchentID = #MerchantId)
If you get 0, you got 0. :)
You can use ##ROWCOUNT. For e.g.
SELECT QBalance
FROM dbo.CustomerBalance
WHERE (CustomerID = 1) AND (MarchentID = #MerchantId)
--This will return no of rows returned by above statement.
SELECT ##ROWCOUNT
You will get 0 if first statement will not return any rows. You can also use if statement to check that just after first statement. e.g.
IF ##ROWCOUNT <> 0
PRINT 'Select statement is returning some rows'
ELSE
PRINT 'No rows returned'
try this:
SELECT ISNULL(QBalance, 'ReplaceValue')
FROM dbo.CustomerBalance
WHERE (CustomerID = 1) AND (MarchentID = #MerchantId)
Could also use an outer ISNULL check?
SELECT ISNULL((
SELECT QBalance
FROM dbo.CustomerBalance
WHERE (CustomerID = 1) AND (MarchentID = #MerchantId)), 0)

SQL return true if criteria match

I have an assignment table in my database. I also have a assignment_notes table, which has a reference to the assignment table. There can exists multiple rows in this table.
When I select all my assignments, I want to check if there exists some notes to this assignment. And all I want is a true/false return.
Is it possible to do something like (pseudo):
Select all assignments; if assignment
has assignment_notes HasNotes = true; else
HasNotes = false.
I hope I made this clear enough - I'm not so good at explaining programming stuff ;-)
DECLARE #Assignments TABLE
(
Id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
Name VARCHAR(30) NOT NULL
)
DECLARE #AssignmentNotes TABLE
(
Id INT NOT NULL IDENTITY(1,1) PRIMARY KEY,
AssignmentId INT NOT NULL,
Note VARCHAR(MAX)
)
INSERT INTO #Assignments(Name) VALUES('Biology')
INSERT INTO #Assignments(Name) VALUES('Chemistry')
INSERT INTO #AssignmentNotes (AssignmentId, Note) VALUES(1, 'Studies on DNA')
INSERT INTO #AssignmentNotes (AssignmentId, Note) VALUES(1, 'Evolution notes from Darwin')
SELECT
A.*,
CASE WHEN COUNT(AN.Id) > 0 THEN 1 ELSE 0 END AS HasNotes
FROM
#Assignments AS A
LEFT JOIN
#AssignmentNotes AS AN
ON A.Id = AN.AssignmentId
GROUP BY
A.Id,
A.Name
Don't have SQL Server ready to test, but a query like this should work:
SELECT A.*,
CAST(
CASE (SELECT TOP 1 AssignmentNotes_ID
FROM AssignmentNotes AN
WHERE AN.AssignmentID = A.AssignmentID)
WHEN NULL THEN 0 ELSE 1 END
AS BIT) AS HasNotes
FROM Assignments A
SELECT a.*,
(SELECT COUNT(*)
FROM assignment_notes an
WHERE an.assignmentid = a.id) as NumNotes
FROM Assignment a
That will give you the number of notes with that assignment.
You can translate your pseudo code to SQL if you use the case statement. On MSDN: http://msdn.microsoft.com/en-us/library/ms181765.aspx
And another article just in case: http://www.devx.com/tips/Tip/15633
This approach means that you don't need a huge GROUP BY statement as a result of returning lots of Assignment fields.
SELECT Assignment.*,
CAST(CASE WHEN NotesQty>0 THEN 1 ELSE 0 END as bit) AS HasNotes
FROM Assignment
LEFT JOIN
(SELECT AssignmentId,COUNT(*) AS NotesQty
FROM assignment_notes
GROUP BY AssignmentId) as Assignment_NotesQty
ON Assignment_NotesQty.AssignmentId=Assignment.AssignmentId

Resources