Multiple result sets excluding column - sql-server

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()

Related

Apply Different WHERE clause depending on value of one field

i'm trying to build a query in which I need to apply 2 different where clauses, depending on the value of Current Month. In this case, I need to show data from the last 2 years, only of the months before the current month:
Example 1:
Current Date is: 01-01-2017
Need to show data from:
01/2015; 02/2015; 03/2015; 04/2015; 05/2015; 06/2015;
07/2015; 08/2015; 09/2015; 10/2015; 11/2015; 12/2015;
01/2016; 02/2016; 03/2016; 04/2016; 05/2016; 06/2016;
07/2016; 08/2016; 09/2016; 10/2016; 11/2016; 12/2016.
Example 2:
Current Date is: 01-03-2017
Need to show data from: 01/2016; 02/2016; 01/2017; 02/2017.
So I built the following query:
SELECT *
FROM TABLE1
WHERE
CASE MONTH(GETDATE())
WHEN 1
THEN YEAR(Data)>=YEAR(GETDATE())-2 and YEAR(data)<YEAR(GETDATE())
ELSE YEAR(Data)>=YEAR(GETDATE())-1 and YEAR(data)<=YEAR(data) and MONTH(data)<MONTH(GETDATE())
END
I'm getting an error.
Can you please help me?
Thank you.
Your syntax is incorrect for sure. THEN is not a logical expression - it is supposed to return value. So you can't write logical expression in THEN/ELSE blocks as you have attempted to. Instead you might try something like:
WHERE
#date >= CASE WHEN a=b THEN '20150101' ELSE '20160202' END
Another thing is: conversions and functions in predicate are very bad for performance. When working with dates you might want to prepare filter predicate before the query when possible, e.g.:
declare
#date_begin date,
#date_end date
set #date_end = DATEADD(..., #arg_date)
set #date_begin = DATEADD(YEAR, -2, #date_end)
select ...
where date between #date_begin and #date_end
in your case it could be something like:
declare
#arg_date DATE = GETDATE(),
#date_begin DATE,
#date_end DATE,
#max_month INT
set #max_month = MONTH(#date)
if #max_month = 1
begin
set #date_end = DATEADD(dd, 1-DATEPART(dy, #arg_date), #arg_date) /* first day of year */
set #date_begin = dateadd(YY, -2, #date_end)
end
else
begin
set #date_end = #arg_date
set #date_begin = dateadd(YY, -1, DATEADD(dd, 1-DATEPART(dy, #date_end), #date_end)) /* first day of year_begin */
end
SELECT *
FROM TABLE1 t
WHERE t.date >= #date_begin and t.date < #date_end
AND (#max_month = 1 OR MONTH(t.date) < #max_month)
another (a better) way is to prepare #periods table variable, put each (date_begin, date_end) pair you need into it and join with TABLE1 - you'll get rid of all function calls from within WHERE clause.
You should realize: you know exactly which periods of each year you need in the result set. There is nothing to compute from stored TABLE1->date column. Just filter it with precomputed date intervals. Don't convert or modify date column - it is already ready to use. Merely apply appropriate filters. MONTH(date) <= 3 is date <= 20170331. Don't torture left part - prepare appropriate right part of such predicates.
The easiest way would be something like:
SELECT *
FROM TABLE1
WHERE
(YEAR(Data)>=YEAR(GETDATE())-2 and YEAR(data)<YEAR(GETDATE()) AND MONTH(GETDATE()) = 1)
OR (YEAR(Data)>=YEAR(GETDATE())-1 and MONTH(data)<MONTH(GETDATE()) and MONTH(GETDATE()) <> 1)
(Note I removed the superfluous and YEAR(data)<=YEAR(data).).
Personally I prefer (and I think it's generally advised) AND/OR logic to a CASE in a WHERE clause.
The error with your CASE statement is caused by the fact that CASE returns an atomic value. It cannot be used in the same way as if in procedural languages.
You can't swap in additional statements to your where clause using case statements. Instead, you need to resolve the case to an equality:
select *
from Table1
where case month(getdate()) -- You want to avoid using functions on fields in your WHERE claises, as this can reduce performance.
when 1 then case when Data >= dateadd(year,datediff(year,0,getdate())-2,0)
and Data < dateadd(year,datediff(year,0,getdate()),0)
then 1 -- Data rows the meet the criteria will return 1.
else 0 -- Data rows that do not will return 0.
end
else case when (Data >= dateadd(year,datediff(year,0,getdate())-1,0)
and Data < dateadd(m,datediff(m,0,getdate())-12,0)
)
or (Data >= dateadd(year,datediff(year,0,getdate()),0)
and Data < dateadd(m,datediff(m,0,getdate()),0)
)
then 1
else 0
end
end = 1 -- Then limit the results to only those rows that returned a 1.
In your specific instance however, this can be simplified to a standard or:
select *
from Table1
where (month(getdate()) = 1
and Data >= dateadd(year,datediff(year,0,getdate())-2,0)
and Data < dateadd(year,datediff(year,0,getdate()),0)
)
or (month(getdate()) <> 1
and (Data >= dateadd(year,datediff(year,0,getdate())-1,0)
and Data < dateadd(m,datediff(m,0,getdate())-12,0)
)
or (Data >= dateadd(year,datediff(year,0,getdate()),0)
and Data < dateadd(m,datediff(m,0,getdate()),0)
)
)
Note the use of brackets above to separate out the logical tests. Where a Data row meets either one of those criteria it will be returned in your query.

Query using a date range but end date may be null

sql server 2014
I am trying to query data using a date range. Data in the table is datetime datatype.
so I want to use as parameters #IncidentDate and #IncidentEndDate.
Issues are that the #IncidentEndDate may be null. Also each row in the data may or may not have an end_datetime (null if no date)
In my where clause I have
(end_datetime IS NULL) AND (#IncidentDate >= CAST(start_datetime AS DATE) AND #IncidentDate <= DATEADD(d,0,DATEDIFF(d,0,start_datetime)))
OR
(#IncidentDate <= end_datetime) AND (#IncidentEndDate >= start_datetime)
However I am not sure if this si working properly. I would expect rows that have no end_datetime to appear in the results but they don't seem to be .
EDIT: In the end I came up with the following after reading everybody's replies...
WHERE (
#IncidentDate <= isnull(end_datetime, dateadd(day,1,start_datetime))
)
AND (
isnull(#IncidentEndDate,dateadd(day,1,#IncidentDate)) >= start_datetime
)
This seems to me to be a tidier way to satisfy my requirements - it looks after the possiblity of both end_datetime being null and #IncdidentEndDate being Null
Also, you can use the ISNULL() function to help handle NULL values. The ISNULL() function checks the value and, if it's NULL, replaces it with a supplied value. So if you wanted the NULLs, you could put in a value that would match or, if not, something that would definitely be outside your range like 01/01/1900 or the MIN Date (varies depending on DATETIME or DATETIME2).
To exclude:
SELECT * FROM mySweetTable WHERE ISNULL(createdDate, '01-JAN-1900') >= '01-JAN-2015'
To include:
SELECT * FROM mySweetTable WHERE ISNULL(createdDate, GETDATE()) >= '01-JAN-2015'
Although, I don't recommend ACTUALLY leaving GETDATE() in the WHERE clause, that's bad news bears; replace it with a variable or specific value.
--This should get you what you want
start_datetime >= #IncidentDate and (#IncidentEndDate is null or end_datetime is null or end_datetime < dateadd(day,1,#IncidentEndDate))
Unless, of course, you want to exclude ones where the end_datetime is null and if that's the case just let me know!

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!

Correcting a "Subquery returned more than 1 value error" when

I have an idea what's wrong with this query, as it seems like SQL has disassociated my converted date (I have 3 fields i needed combined to make a date) from the Product table. My question is how do I fix it? I can't easily check if the current date is > greater than three separate columns, so I needed to combine them together into a single date.
select ProductID from ctbo.dbo.PRODUCT where (getdate() >
(Select
Convert(DATE,CAST([expYear] AS VARCHAR(10))+'-'+
CAST([expMonth] AS VARCHAR(10))+'-'+
CAST([expDay] AS VARCHAR(10)))
from PRODUCT where expYear not like '0' and expDay not like '0' and expMonth not like '0') )
Since your subquery returns multiple values you need to use either ANY keyword (to make condition applied to any of the subquery result) or ALL (to be applicable to all results) e.g.:
select ProductID from ctbo.dbo.PRODUCT where (getdate() >
ANY(Select
Convert(DATE,CAST([expYear] AS VARCHAR(10))+'-'+
CAST([expMonth] AS VARCHAR(10))+'-'+
CAST([expDay] AS VARCHAR(10)))
from PRODUCT where expYear not like '0' and expDay not like '0' and expMonth not like '0') )

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