Null value is considered an existing value - sql

I have a validation into if conditional like:
IF(EXISTS
(SELECT TOP 1 [TaskAssignationId]
FROM [Task] AS [T]
INNER JOIN #TaskIdTableType AS [TT] ON [T].[TaskId] = [TT].[Id]
))
But it returns NULL value because TaskAssignationId is NULL so in consequence IF condition it's true because it exist with NULL value, but I don't want to consider NULL as a value. How can add an exception of nulls? Regards

If you don't want to include rows where [TaskAssignationId] is null then add that to a WHERE clause.
IF(EXISTS
SELECT TOP 1 [TaskAssignationId]
FROM [Task] AS [T]
INNER JOIN #TaskIdTableType AS [TT] ON [T].[TaskId] = [TT].[Id]
WHERE [TaskAssignationId] is not null
))

Exists works like "Did the (sub)query return more than zero (correlated) rows" not "did the (sub)query return a non null value"
These are perfectly valid exists:
SELECT * FROM person p
WHERE EXISTS (SELECT null FROM address a WHERE a.personid = p.id)
SELECT * FROM person p
WHERE EXISTS (SELECT 1 FROM address a WHERE a.personid = p.id)
SELECT * FROM person p
WHERE EXISTS (SELECT * FROM address a WHERE a.personid = p.id)
It doesn't matter what values you return, or how many columns, exists cares whether the rowcount is 0 or greater when determining whether results exist
Hence you have to make sure your (sub)query returns no rows if you want the exists check to fail. If Addresses that have a null type are unacceptable, the (sub)query has to exclude them with WHERE a.type IS NOT NULL so that only rows with a non null type are considered
There's also little point doing a TOP 1 in the (sub)query; the optimiser knows that the only condition it cares about is 0 or not-0 rows, so it automatically do a TOP 1 (i.e. it will stop retrieving data when it knows there is at least one row)

If you want to check the existence then no need to assign the column name, you can use select 1
IF(EXISTS
SELECT TOP 1 1
FROM [Task] AS [T]
INNER JOIN #TaskIdTableType AS [TT] ON [T].[TaskId] = [TT].[Id]
))
begin
----code---
end

Related

JOIN AND CASE MORE AN TABLE

I have 2 tables; the first one ORG contains the following columns:
ORG_REF, ARB_REF, NAME, LEVEL, START_DATE
and the second one WORK contains these columns:
ARB_REF, WORK_STREET - WORK_NUM, WORK_ZIP
I want to do the following: write a select query that search in work and see if the WORK_STREET, WORK_ZIP are duplicate together, then you should look at WORK_NUM. If it is the same then output value ' ok ', but if WORK_NUM is not the same, output 'not ok'
I wrote this SQL query:
select
A.ARB_REF, A.WORK_STREET, A.WORK_NUM, A.WORK_ZIP
case when B.B = 1 then 'OK' else 'not ok' end
from
work A
join
(select
WORK_STREET, WORK_ZIP count(distinct , A.WORK_NUM) B
from
WORK
group by
WORK_STREET, WORK_ZIP) B on B.WORK_STREET = A.WORK_STREET
and B.WORK_ZIP = A.WORK_ZIP
Now I want to join the table ORG with this result I want to check if every address belong to org if it belong I should create a new column result and set it to yes in it (RESULT) AND show the "name" column otherwise set no in 'RESULT'.
Can anyone help me please?
While you can accomplish your result by adding a left outer join to the query you've already started, it might be easiest to just use count() over....
with org_data as (
-- do the inner join before the left join later
select * from org1 o1 inner join org2 o2 on o2.orgid = o1.orgid
)
select
*,
count(*) over (partition by WORK_STREET, WORKZIP) as cnt,
case when o.ARB_REF is not null then 'Yes' else 'No' end as result
from
WORK w left outer join org_data o on o.ARB_REF = w.ARB_REF

SQL Subquery Having COUNT(var) turns 0 to NULLs

I have written a SQL query with a subquery to include counts. When the count is 0, and I try to filter out the 0, it turns the 0's to NULLs and keeps the rows, and vice versa. The result is that I can't filter out the 0's, which was the purpose of including the counts.
SELECT distinct
a
,b
,
(SELECT
count(id)
FROM seq_stud
WHERE scs.SequenceID = seq_stud.SequenceID
and seq_stud.EndDate is null
HAVING count(id) <> 0
) As t1
FROM sp
INNER JOIN p on sp.ProgramID = p.ProgramID
...etc.
Does anyone know why this is happening and how I can filter out the 0 counts?
You don't filter in the SELECT clause. If you don't want rows that have no match in seq_stud, then use WHERE:
WHERE EXISTS (SELECT 1
FROM seq_stud ss
WHERE scs.SequenceID = ss.SequenceID and ss.EndDate is null
)
I would remove the HAVING statement altogether. You need to put that in the WHERE clause. Otherwise, it will return null, as you found.
SELECT distinct a, b,
(SELECT count(id)
FROM seq_stud
WHERE scs.SequenceID = seq_stud.SequenceID
and seq_stud.EndDate is null
) As t1
FROM sp
INNER JOIN p on sp.ProgramID = p.ProgramID
WHERE t1 > 0
I just figured this out. The Select subquery should be included as a WHERE statement
Using having count() in exists clause

SQL NOT exists conditional

I have three tables as described below:
dbo.ServiceEntry
ID RunLogEntry Reconciled
1 0 1
2 4 1
3 5 1
dbo.ServiceEntryPart
ID ServiceEntryID PartId ServiceEntryTypeID
1 1 3 1
2 2 4 2
3 2 4 1,2
dbo.Part
ID Desc Active (bitfield)
3 xyz 1
4 abc 1
Query as follows:
SELECT *
FROM ServiceEntry AS S
WHERE (S.RunLogEntryID is not null) AND (S.Reconciled=#ReconciledValue)
AND EXISTS (SELECT ServiceEntryID
FROM ServiceEntryPart SEP
JOIN Part on SEP.PartID = Part.ID
WHERE ((#ActivePart = 0 AND Part.Active is not null)
OR (#ActivePart = 1 and Part.Active = 0))
AND (#ServiceTypes is null
OR CHARINDEX(','+cast(SEP.ServiceTypeIDs as varchar(255))+',',','+#ServiceTypes+',') > 0))
OR (NOT EXISTS (SELECT ServiceEntryID
FROM ServiceEntryPart SEP
JOIN Part on SEP.PartID = Part.ID))
service entry has some records which contain runlogentry id of 0. If the runlogentryid value in service entry table is 0 then there will be no service entry part record for that service entry. Thats why I split them into two as you would notice from the query for example exists and not exists. the exists statement takes care of all service entries which have service entry parts and for these the filters will be applicable. If the filters have values then the not exist block will be not be needed because filters servicetypeids and activepart are for only records which have service entry parts.
So in other words if no params are passed the first exists block fetches service entries which have service parts and the not exists fetches service entries which have runlogentry id of 0 OR NOT null. This works great as it is. The problem is when the params are passed I would need to exclude the serviceentries which do not have service entry parts and when they are present I do not get the rigth results. I hope I did an okay job explaining the problem..Please help
Without trying to understand the rest of your SQL, if all you want to do is avoid the NOT EXISTS clause when your parameters are null you can do something like this.
SELECT *
FROM ServiceEntry AS S
JOIN
WHERE (S.RunLogEntryID is not null) AND (S.Reconciled=#ReconciledValue)
AND EXISTS (SELECT ServiceEntryID
FROM ServiceEntryPart SEP
JOIN Part on SEP.PartID = Part.ID
WHERE ((#ActivePart = 0 AND Part.Active is not null)
OR (#ActivePart = 1 and Part.Active = 0))
AND (#ServiceTypes is null
OR CHARINDEX(','+cast(SEP.ServiceTypeIDs as varchar(255))+',',','+#ServiceTypes+',') > 0))
OR (#ReconciledValue is null and #ActivePart is null and #ServiceTypes is null
and (NOT EXISTS (SELECT ServiceEntryID
FROM ServiceEntryPart SEP
JOIN Part on SEP.PartID = Part.ID)))
This probably isn't exactly what you want, to be honest the query seems all over the place.
You're probably looking for an active part filter like '(#ActivePart is null or #ActivePart = Part.Active)'. Your exists is implicitly taken care of by an inner join. A query that I believe is close to what you're looking for is below.
select *
FROM ServiceEntry AS S
INNER JOIN ServiceEntryPart AS SEP ON SEP.ServiceEntryID = S.ServiceEntryID
INNER JOIN Part AS P ON P.PartID = SEP.PartID
WHERE S.RunLogEntryID is not null
and S.Reconciled = #ReconciledValue
and (#ActivePart is null or #ActivePart = Part.Active)
and (#ServiceTypes is null or charindex(','+cast(SEP.ServiceTypeIDs as varchar(255))+',',','+#ServiceTypes+',') > 0
I can't be sure because I don't have the business definitions of your columns, but I suspect your conditions dealing with Part.Active are wrong. I would expect the values to be 0 or 1, not NULL or 0. So I should think the test should return TRUE when the parameter is NULL or the parameter matches the column.
Your CHARINDEX function has the parameters reversed.
You need additional parentheses to get the correct order of operation and your OR NOT EXISTS clause needs additional conditions to make it only true if the parameters are NULL.
I believe the following is much closer to what you are looking for. But I am worried about the ServiceTypes test. It will only work reliably if the parameter contains a single ServiceTypeID. If it contains multiple IDs it may not work. For example, I would think a parameter value of '1,3' should match a list of '1,2,3', but it won't.
SELECT *
FROM ServiceEntry AS S
WHERE (S.RunLogEntryID IS NOT NULL)
AND (S.Reconciled=#ReconciledValue)
AND( EXISTS(
SELECT ServiceEntryID
FROM ServiceEntryPart SEP
JOIN Part ON SEP.PartID = Part.ID
WHERE( #ActivePart IS NULL
OR #ActivePart = Part.Active
)
AND ( #ServiceTypes IS NULL
OR CHARINDEX( ','+#ServiceTypes+',',
','+cast(SEP.ServiceTypeIDs as varchar(255))+','
) > 0
)
)
OR( #ActivePart IS NULL
AND #ServiceTypes IS NULL
AND NOT EXISTS( SELECT ServiceEntryID
FROM ServiceEntryPart SEP
JOIN Part on SEP.PartID = Part.ID
)
)
)

Why does my SQL query return rows with NULL? It should never return rows with NULL

Suppose I have a SQL table called AT_Devices with each record representing a piece of hardware. I have a second table called AT_Event_History which describes "events" that occur for pieces of hardware. An event can consist of something like the piece of equipment being lost, destroyed, retired, etc. Each event has a corresponding status code.
I've written some SQL to return the code of the most recent event for each record in AT_Devices. I have a business rule that if the device has no events on record, the status code should be 0.
I've written my SQL query to return 0 in this case, but for some reason, it is returning NULL. Why?
SELECT atDeviceHistory.StatusCodeNotNull AS StatusCode0
FROM dbo.AT_Devices atd LEFT OUTER JOIN
(
SELECT DeviceID,
ParentCode,
(CASE WHEN (StatusCode IS NOT NULL) THEN StatusCode ELSE 0 END) AS
StatusCodeNotNull,
WhenEntered AS StatusDate
FROM AT_Event_History as A
WHERE A.ParentCode=0
AND A.WhenEntered >= (SELECT MAX(WhenEntered)
FROM AT_Event_History AS B
WHERE A.DeviceID=B.DeviceID AND ParentCode=0)
) atDeviceHistory ON atd.DeviceID=atDeviceHistory.DeviceID
Since you are doing a left join, if there are no matching records a null value will be returned for anything referencing the atDeviceHistory columns. If you do not want to return records with no match, then change this to an inner join. I would also recommend changing your case statement to a COALESCE:
SELECT DeviceID,
ParentCode,
COALESCE(StatusCode ,0) AS StatusCodeNotNull,
WhenEntered AS StatusDate
FROM AT_Event_History as A
UPDATE:
Try this:
SELECT COALESCE(atDeviceHistory.StatusCode,0) AS StatusCode0
FROM dbo.AT_Devices atd LEFT OUTER JOIN
(
SELECT DeviceID,
ParentCode,
StatusCode
WhenEntered AS StatusDate
FROM AT_Event_History as A
WHERE A.ParentCode=0
AND A.WhenEntered >= (SELECT MAX(WhenEntered)
FROM AT_Event_History AS B
WHERE A.DeviceID=B.DeviceID AND ParentCode=0)
) atDeviceHistory ON atd.DeviceID=atDeviceHistory.DeviceID
Now that #Abe Miessler has pointed out the misplaced NULL check in your query, you could actually get rid of the subselect and rewrite the entire query like this:
SELECT
COALESCE(h.StatusCode, 0) AS StatusCode0,
… /* whatever other columns you might need */
FROM dbo.AT_Devices atd
LEFT OUTER JOIN AT_Event_History h ON atd.DeviceID = h.DeviceID
AND h.ParentCode = 0
AND h.WhenEntered = (SELECT MAX(WhenEntered)
FROM AT_Event_history
WHERE DeviceID = a.DeviceID AND ParentCode = 0)

MySQL: Select pages that are not tagged?

I have a db with two tables like these below,
page table
pg_id title
1 a
2 b
3 c
4 d
tagged table
tagged_id pg_id
1 1
2 4
I want to select the pages which are tagged, I tried with this query below but doesn't work,
SELECT *
FROM root_pages
LEFT JOIN root_tagged ON ( root_tagged.pg_id = root_pages.pg_id )
WHERE root_pages.pg_id != root_tagged.pg_id
It returns zero - Showing rows 0 - 1 (2 total, Query took 0.0021 sec)
But I want it to return
pg_id title
2 b
3 c
My query must have been wrong?
How can I return the pages which are not tagged correctly?
SELECT *
FROM root_pages
LEFT JOIN root_tagged ON root_tagged.pg_id = root_pages.pg_id
WHERE root_tagged.pg_id IS NULL
The != (or <>) operator compare two values, but cannot be used for NULL.
NULL = NULL returns false
NULL = 0 returns false
NULL != NULL returns false
You get the point, to check for NULL you should use the IS or IS NOT operator.
If your density to tag to pages is more than 2:1 or so, then using NOT EXISTS will be faster than using LEFT JOIN + IS NULL
SELECT *
FROM root_pages
WHERE NOT EXISTS (
SELECT *
FROM root_tagged
WHERE root_tagged.pg_id = root_pages.pg_id )
It is an alternative that more clearly states what you are looking for, a non-existence.
For the strikeout text above:
The question is MySQL specific, and assuming root_tagged.pg_id is not nullable, LEFT JOIN + IS NULL is implemented using ANTI-JOIN which is the same strategy as NOT EXISTS, except there seems to be some overhead added by NOT EXISTS, so LEFT JOIN is supposed to work faster.