Ignore condition in WHERE clause when column is NULL - sql-server

I do have table were one row (with Type =E) is related to another row.
I have written query to return COUNT of those related rows. The problem is that there is no explicit relationship (like ID column that would clearly say which row is related to other row). Therefore I am trying to find relationship based on multiple conditions in WHERE clause.
The problem is that in few cases, the columns A and B could be NULL (for records where TYPE = 'M'). In such a cases I would like to ignore that condition, so It would use only first 3 conditions to determine relationship.
I have tried CASE Statement but is not working as expected:
SELECT [T1].[ID],[T1].[AlphaId],[T1].[Type],[T1].[A],[T1].[B],[T1].[Date],[T1].[ServiceID]
,( SELECT COUNT(*)
FROM MyTable T2
WHERE [T1].[AlphaId]=[T2].[AlphaId] AND
[T1].[Date]=[T2].[Date] AND
[T1].[ServiceID]=[T2].[ServiceID] AND
[T2].[A]=CASE WHEN [T2].[A] IS NULL THEN NULL ELSE [T1].[A] END AND
[T2].[B]=CASE WHEN [T2].[B] IS NULL THEN NULL ELSE [T1].[B] END AND
[T2].[Type]='M'
) as TotalCount
FROM MyTable T1
WHERE [T1].[Type] = 'E'
I can't ignore that condition, as for some cases the Date, ServiceID could be same, however it's the A, B which differs them. Luckily where A, B IS NULL, it is the Date, ServiceID which differs those two records.
http://sqlfiddle.com/#!3/c98db/1
Many thanks in advance.

You could join the tables and use COUNT and GROUP BY to get the counts. Then you can JOIN [A] and [B] if they are equal or NULL.
SELECT [T1].[ID],[T1].[AlphaId],[T1].[Type],[T1].[A],[T1].[B],[T1].[Date],[T1].[ServiceID], count([T2].[ID])
FROM MyTable T1
INNER JOIN MyTable T2 ON [T1].[AlphaId]=[T2].[AlphaId] AND
[T1].[Date]=[T2].[Date] AND
[T1].[ServiceID]=[T2].[ServiceID] AND
([T2].[A]= [T1].[A] OR [T2].[A] IS NULL )AND
([T2].[B]= [T1].[B] OR [T2].[B] IS NULL )AND
[T2].[Type] <> [T1].[Type]
WHERE [T1].[Type] = 'E'
GROUP BY [T1].[ID],[T1].[AlphaId],[T1].[Type],[T1].[A],[T1].[B],[T1].[Date],[T1].[ServiceID]

Related

Need an IFF statement to work where the data is not in a joining table, so it is not labeled as NULL

SELECT DISTINCT
ATO.Agent,
ATO.Bottler_ID,
ATO.Account_number,
ATO.DELIVERY_DATE,
IFF (ATO.Account_number IS NULL,0,1) AS Opportunity
from ATO
left join OOM on ATO.Account_number = OOM.Account_number
The Account number will not be NULL, If it does not appear in the OOM table, I just want a "0" in a new Opportunity column, but if it IS there, I need a 1.
just use a CASE statement to implement the logic based on OOM.Account_number being null or not
This query does a left join and replaces nulls joins with the desired logic:
with data as (select $1 id, $2 name from values(1,'a'), (2,'b') )
, reference as (select $1 id from values(1))
select a.*, iff(b.id is null, 'no ref', 'has ref')
from data a
left join reference b
on a.id=b.id
The query you posted should work as expected. Can you show us what results you get, and what results you expect?

Updating table by using multiple joins on the same table

In the picture that is attached, I highlighted two columns. The left column is a the StartTime and the right column is an ID. I need the IDs that are NULL to be 164, since all 4 of those rows have the same exact StartTime. Is there a way to code one update statement to gather the NULL rows and rows with a value, and then update the NULL rows with a value that has the same StartTime? Any code would be very helpful.
Picture: Query Results
UPDATE t
SET t.ID = x.ID
FROM YourTable t
INNER JOIN (SELECT DISTINCT ID, StartTime
FROM YourTable
WHERE ID IS NOT NULL
) x
ON x.StartTime = t.StartTime
WHERE t.ID IS NULL
This is how I would do it.

Handling TOP in A CASE-WHEN-THEN select

I am having a problem to resolve a SELECT CASE using TOP.
Should I mention I'm quite new to this ? :D
Also this is my first post on Stackoverflow. Hi !
I want to fill one column of informations from two tables :
Table 1 : Column 1 contains data I want to use
Table 1 : Column 2 is a join
Table 2 : Column 1 contains data I want to use
Table 2 : Column 2 is a join
So :
Table 1 Column 1 contains letters (D M and T) and empty spaces.
Table 2 Column 1 contains words ('Rolls' 'Transfers' 'Delivery')
I'm trying to fill my column using the following conditions :
When column T1.C1 contains D, M or T, write D M or T.
When column T1.C1 is empty, look at column T2.C1 :
If column T1.C1 contains 'Rolls', write 'R'
Else don't write anything
Things get ugly really fast for me because the info I want from column B requires a TOP to be used.
THe best I could get so far is Incorrect syntax near the keyword 'From'.
Here is my code so far.
Any suggestions ?
SELECT
(CASE T1.C1
WHEN 'D'
THEN 'D'
WHEN 'M'
THEN 'M'
WHEN 'T'
THEN 'T'
WHEN (SELECT TOP 1 T2.C1 FROM T2 WHERE T1.C2=T2.C2)
THEN 'R'
ELSE
''
END) as my_data,
FROM T1
I think you should try to find a simpler way to write this, something along the lines of:
WITH cte
AS ( SELECT C1 ,
C2 ,
ROW_NUMBER() OVER ( PARTITION BY C2 ORDER BY col ) rn
FROM Table2
)
SELECT CASE WHEN T1.C1 = '' THEN LEFT(T2.C1, 1)
ELSE T1.C1
END AS my_data
FROM Table1 T1
LEFT OUTER JOIN cte T2 ON T1.C2 = T2.C2
AND T2.rn = 1
You haven't posted your table schema, so this may need to be adjusted.

T-SQL: Can a subquery in the SELECT clause implicitly reference a table in the main outer query?

In T-SQL, is it possible to have a subquery in the SELECT clause that implicitly references tables in the main, outer query? For example:
select NAME,
case when exists (select o.ORDERID) then 1 else 0 end BUYER
from CUSTOMER c
left join ORDER o
on c.CUSTID = o.CUSTID
In other words, can I write subqueries without a FROM clause?
Intellisense seems to recognize outer table aliases in subqueries, but I can't find any documentation that says this is acceptable T-SQL. I can certainly run some of my own tests, but I also wanted to check with the community. Thanks.
Yes this is valid syntax.
A SELECT without a FROM is treated as though selecting from a single row table. Referencing columns from the outer query is required for correlated sub queries and is perfectly valid there.
The particular query you have makes no sense though. It will always evaluate to 1 as that subquery always returns a single row (with a single column containing the corresponding o.OrderId) going into the EXISTS check.
Probably you want to check o.OrderId IS NULL
SELECT NAME,
CASE
WHEN o.ORDERID IS NULL THEN 0
ELSE 1
END BUYER,
FROM CUSTOMER c
LEFT JOIN ORDER o
ON c.CUSTID = o.CUSTID
Where it does make sense to use this type of syntax is in a null safe equality check.
e.g.
SELECT A,
B,
CASE
WHEN EXISTS (SELECT T.A
EXCEPT
SELECT T.B) THEN 1
ELSE 0
END AS DistinctFrom
FROM T
Is equivalent to
SELECT A,
B,
CASE
WHEN A <> B
OR ( A IS NULL
AND B IS NOT NULL )
OR ( A IS NOT NULL
AND B IS NULL ) THEN 1
ELSE 0
END AS DistinctFrom
FROM T

Better way to join null values than picking a magic value?

I need to join two tables that are more or less the same (one is a staging table data to go in the other).
Some of the columns are nullable, and when the values are null, the join in my merge statement does not match. (This is normal behavior for nulls.)
The problem is that, when they don't match it causes the row to be deleted and recreated, Changing the value identity of the row in the actual table.
I know that I can do something like this to join nulls:
on coalesce(target.SomeId, -9999) = coalesce(source.SomeId, -9999)
But I don't like having to pick out a number that I hope will never be used. (It feels dirty.)
Is there a better way to make a join on a nullable column than using a magic number like this?
Let's go with this:
target.SomeId = source.SomeId
or (target.SomeId is null and source.SomeId is null)
Conceptually, this should make sense. That is, either both values are null or both values are equal to each other. This should also perform better as the coalesce forces a table scan. I've converted the coalesce style to that above and seen tremendous performance gains.
I almost exclusively use the following pattern
ON EXISTS (SELECT target.SomeId INTERSECT source.SomeId)
after picking it up from Paul White's blog post here.
ON ((target.SomeId IS NULL) AND (source.SomeId IS NULL))
OR ((target.SomeId IS NOT NULL) AND (source.SomeId IS NOT NULL)
AND (target.SomeId = source.SomeId)))
Assuming you mean columns that aren't part of the joining key, then
...and (isnull(source.ColX, target.ColX) = isnull(target.ColX, source.ColX)
or (source.ColX is null and target.ColX is null))
should cover all possibilities: the first line catches if both values are not null or only one value is not null, and the second line catches if both are null. Pretty ugly, but then that's what happens when you get too many nulls in your system.
The result is strange and contains rows resulted from INNER JOIN and rows resulted from a CROSS JOIN betwen NULL IDs from source and target table (ex. {S-C, T-C}, {S-C, T-D}, {S-C, T-E}, {S-D, T-C}, {S-D, T-D}, {S-D, T-E},).
Look at this example:
DECLARE #Source TABLE (SomeId INT NULL, Name VARCHAR(10) NOT NULL);
DECLARE #Target TABLE (SomeId INT NULL, Name VARCHAR(10) NOT NULL);
INSERT #Source VALUES (1,'S-A'),(2,'S-B'),(NULL,'S-C'),(NULL,'S-D');
INSERT #Target VALUES (1,'T-A'),(2,'T-B'),(NULL,'T-C'),(NULL,'T-D'),(NULL,'T-E'),(6,'T-F');
SELECT s.*, t.*
FROM #Source s
INNER JOIN #Target t ON s.SomeId = t.SomeId OR s.SomeId IS NULL AND t.SomeId IS NULL;
SELECT s.*, t.*
FROM #Source s
INNER JOIN #Target t ON ISNULL(s.SomeId,-9999) = ISNULL(t.SomeId,-9999);
Results:
SomeId Name SomeId Name
----------- ---------- ----------- ----------
1 S-A 1 T-A <- INNER JOIN
2 S-B 2 T-B <- INNER JOIN
NULL S-C NULL T-C <- "CROSS JOIN"
NULL S-C NULL T-D <- "CROSS JOIN"
NULL S-C NULL T-E <- "CROSS JOIN"
NULL S-D NULL T-C <- "CROSS JOIN"
NULL S-D NULL T-D <- "CROSS JOIN"
NULL S-D NULL T-E <- "CROSS JOIN"
Special characters?
Else you can try:
on (target.SomeId is null OR source.SomeId is null OR target.SomeId = source.SomeId)

Resources