SQL Query Dependent Logic in WHERE clause - sql-server

I have a table TableX. It contains three columns: A INT , B INT and C VARCHAR(1) (valid values for column C being the name of the columns 'A' or 'B'). I need to count the number of occurrences where either of the following conditions are met:
When C is NULL and does contains values in either A or B greater than zero. or
When C is NOT NULL (i.e. 'A' or 'B') and the value in the specified column (A or B) is either zero or NULL.
My current stored procedure looks like
CREATE PROCEDURE ispcSomeName #NumOcc INT OUTPUT
AS
SELECT COUNT(*) AS [NumOcc]
FROM TableName
WHERE (C IS NULL
AND ((A IS NOT NULL OR A > 0) OR
(B IS NOT NULL OR B > 0)))
OR (CritCarType IS NOT NULL
AND (CASE SET #TmpColumnName = CritCareType
WHEN N'A' THEN (A IS NULL OR A <= 0)
WHEN N'B' THEN (B IS NULL OR B <= 0)))
GO
The problem is that I need to check the column that C references. That is if C = 'A', I need to check if A IS NOT NULL OR A <= 0. There is clearly an issue with the SET in the final WHERE. I have got a few of these types of SPs to author and I want to avoid complex TSQL if I can help it...
How do I dynamically get reference to the relevant column in the WHERE clause?
Thanks for your time.

Select Count(*)
from tab
where
(
C is null and (Coalesce(A,0)>0 or Coalesce(B,0)>0)
)
or
(
C is not null and Case when C='A' then Coalesce(A,0)
when C='B' then Coalesce(B,0) end <=0
)

Related

Combining columns of two different datasets

I have a UDF that needs to always return the same dataset structure, columns a, b, c and d.
It needs to return a UNION ALL from more than one datasource, including other UDF:s.
Let's say I have another function (myOtherUDF) that returns column a and b.
I also have a table (myTable) with the column names a, b, c and d.
What I want to do is to UNION ALL on myOtherUDF and myTable in a way that the columns c and d are added to myOtherUDF.
i.e. I want this to work although myOtherUDF lacks the columns c and d:
CREATE FUNCTION myUDF (#param INT)
RETURNS #tbl TABLE
(
a int NOT NULL,
b int NOT NULL,
c int NOT NULL,
d int NOT NULL
)
AS
BEGIN
INSERT INTO #tbl
SELECT * FROM myTable
UNION ALL // this will obviously not work
SELECT * FROM myOtherUDF(#param)
RETURN
END
I cannot use a process to preload a table and I cannot use a view since I need the parameter #param.
If you explicitly list your columns - which is best practice - you immediately solve this problem, well after adding defaults for c and d from myOtherUDF;
INSERT INTO #tbl (a, b, c, d)
SELECT a, b, c, d
FROM myTable
UNION ALL
SELECT a, b, 0, 0
FROM myOtherUDF(#param);
RETURN;
To be clear you should pretty much never select * and never insert into table without listing the columns. It saves so many issues down the line.
And for performance reasons its nearly always better to use an inline table valued function e.g.
CREATE FUNCTION myUDF
(
#param INT
)
RETURNS TABLE
RETURN
SELECT a, b, c, d
FROM myTable
UNION ALL
SELECT a, b, 0, 0
FROM myOtherUDF(#param);

SQL Server : why doesn't this query return NULL values?

I have a table with one column named value, of type int, and 4 rows in it:
20
50
NULL
0
Why does this query return 20 and 50 only? I need NULL values, too.
SELECT *
FROM dbo.myTable
WHERE value != 0
In SQL, NULL is a special value, nothing can ever be equal to or not equal to it. In fact, NULL doesn't even equal itself. When searching for null values, you must use the IS operator. So you need to change your query to include them:
SELECT *
FROM dbo.myTable
WHERE value !=0
OR value IS NULL
You can also replace null for sth else what is not a zero using isnull or coalesce
SELECT *
FROM dbo.myTable
WHERE ISNULL(value,1) <> 0
NULL is a "non-value". Some comparing-tools will ignore/skip every NULL's. (NULL doesn't exists.)
You have 3 Options:
1) Insert a value (42) instead of NULL. ex.: Set up a default ALTER TABLE myTable ADD CONSTRAINT DF_mytable_value DEFAULT (42) FOR value
2) Add NULL to your query condition.
SELECT *
FROM myTable
WHERE value != 1 OR value IS NULL
3) Compare every 0 as 0 and every else as 1 within your WHERE clausel:
SELECT *
FROM myTable
WHERE 1 = CASE value WHEN 0 THEN 0 ELSE 1

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.

Multiple result sets excluding column

I have a collection of entries in a table, table is joined with another table and together I need to return a resultset excluding entries by a particular date value.
Table 1
I need to return a collection of entries based on a query and find the value, along with a collection of other items where the date as per the screenshot is <= GETDATE()
Results should be
As you can see, the resultset returns all three of the General Worker items but should only return where the date time is <= GetDate().
I have tried various approaches, from the (SELECT .. (PARTITION)) approach to sub-value table results and none of them return the resultset I need.
I need all other rows intact with only the General Worker where date <= GETDATE() and I'm stuck.
UPDATE
My T-SQL statement before modifications:
SELECT
T0.nContractID,
T1.sJobCatNo,
T1.nJobCatID,
T1.sJobCatDesc,
T1.nDeleted,
T1.nAdminLocked,
T1.nClientDefault,
T1.nRateNT,
CASE
WHEN (T0.sDistributionCode IN ('Nails', 'Board'))
THEN 1
ELSE 0
END AS 'ShowRate'
FROM
[dbo].[Contract] AS T0
INNER JOIN [dbo].[JobCategoryRates] AS T1 ON T1.nContractID = T0.nContractID
WHERE
T1.nContractID = 200198
AND T1.nDeleted = 0
ORDER BY
T1.sJobCatDesc
UPDATE 2
I need the results to look like this:
UPDATE 3
Maybe this might help?
Table 1, for nContractID returns 19 results (3 of which are the same), the only distinct value is the dEndDate column should should be <= GETDATE(). I need to extract all values where dEndDate is null and dEndDate <= GETDATE(). Everything I've tried thus far brings back only one result, but logic in my head says I should have 17 results, if the dEndDate items >= GETDATE() is removed?
Need to clean up the query and your thought process
If you want to debug dEndDate then include it in the output
All values where dEndDate is null and dEndDate <= GETDATE() is always false.
A value cannot be null and have a value.
In the default configuration a comparison to null is always false.
null <= 1/1/2000 is false
null >= 1/1/2000 is false
null = null is false
If you want null OR dEndDate <= GETDATE() then:
where dEndDate is null or dEndDate <= GETDATE()
Why would you expect this not to return one row?
dEndDate <= GETDATE()

How to list all duplicated rows which may include NULL columns?

I have a problem of listing duplicated rows that include NULL columns. Lemme show my problem first.
USE [tempdb];
GO
IF OBJECT_ID(N'dbo.t') IS NOT NULL
BEGIN
DROP TABLE dbo.t
END
GO
CREATE TABLE dbo.t
(
a NVARCHAR(8),
b NVARCHAR(8)
);
GO
INSERT t VALUES ('a', 'b');
INSERT t VALUES ('a', 'b');
INSERT t VALUES ('a', 'b');
INSERT t VALUES ('c', 'd');
INSERT t VALUES ('c', 'd');
INSERT t VALUES ('c', 'd');
INSERT t VALUES ('c', 'd');
INSERT t VALUES ('e', NULL);
INSERT t VALUES (NULL, NULL);
INSERT t VALUES (NULL, NULL);
INSERT t VALUES (NULL, NULL);
INSERT t VALUES (NULL, NULL);
GO
Now I want to show all rows that have other rows duplicated with them, I use the following query.
SELECT a, b
FROM dbo.t
GROUP
BY a, b
HAVING count(*) > 1
which will give us the result:
a b
-------- --------
NULL NULL
a b
c d
Now if I want to list all rows that make contribution to duplication, I use this query:
WITH
duplicate (a, b) AS
(
SELECT a, b
FROM dbo.t
GROUP
BY a, b
HAVING count(*) > 1
)
SELECT dbo.t.a, dbo.t.b
FROM dbo.t
INNER JOIN duplicate
ON (dbo.t.a = duplicate.a
AND dbo.t.b = duplicate.b)
Which will give me the result:
a b
-------- --------
a b
a b
a b
c d
c d
c d
c d
As you can see, all rows include NULLs are filtered. The reason I thought is that I use equal sign to test the condition(dbo.t.a = duplicate.a AND dbo.t.b = duplicate.b), and NULLs cannot be compared use equal sign. So, in order to include rows that include NULLs in it in the last result, I have change the aforementioned query to
WITH
duplicate (a, b) AS
(
SELECT a, b
FROM dbo.t
GROUP
BY a, b
HAVING count(*) > 1
)
SELECT dbo.t.a, dbo.t.b
FROM dbo.t
INNER JOIN duplicate
ON (dbo.t.a = duplicate.a
AND dbo.t.b = duplicate.b)
OR
(dbo.t.a IS NULL
AND duplicate.a IS NULL
AND dbo.t.b = duplicate.b)
OR
(dbo.t.b IS NULL
AND duplicate.b IS NULL
AND dbo.t.a = duplicate.a)
OR
(dbo.t.a IS NULL
AND duplicate.a IS NULL
AND dbo.t.b IS NULL
AND duplicate.b IS NULL)
And this query will give me the answer as I wanted:
a b
-------- --------
NULL NULL
NULL NULL
NULL NULL
NULL NULL
a b
a b
a b
c d
c d
c d
c d
Now my question is, as you can see, this query just include two columns, in order to include NULLs in the last result, you have to use many condition testing statements in the query. As the column number increasing, the condition testing statements you need in your query is increasing astonishingly. How can I solve this problem?
Great thanks.
You can use the OVER clause instead:
select a, b from
(
select a, b,
COUNT(*) over (partition by a, b) Cnt
from dbo.t
) TheResult
where Cnt > 1
That way you can avoid all the conditions, just add all your fields in the subquery and retrieve them in the main select.
If the problem indeed lies in the equal sign and NULL values, SET ANSI_NULLS (setting it to OFF) should/could do the trick.
SET ANSI_NULLS OFF

Resources