Grouping OR clause with DATEDIFFS in SQL - sql

I have a sql table. We'll call it 'table'. I'm trying to do a select with a few WHERE clauses. I need the last where clause to be an OR. Normally, I just do this:
SELECT *
FROM table
WHERE 1=1
AND (firstName='tom' OR lastName='jones')
However, on this particular query, I need the two sides of the OR to be DATEDIFFS. I'm trying to grab all rows where the difference between the timestamp and "NOW" is either greater than 10 hours or less than 0 hours. So, what I've got now, which errors, is this:
SELECT *
FROM table
WHERE 1=1
AND (
DATEDIFF(hh, table.timestamp, GETDATE()) > 10
OR
DATEDIFF(hh, table.timestamp, GETDATE()) < 0
)
I have tried several different versions of what I have above and nothing seems to be working. Any help would be much appreciated! I'm sure I've just been staring at it too long and it's something stupid.
A new more detailed query:
Yeah, there's two left joins in my query which is probably what's making a difference here. I was trying to keep my examples simple, but it wound up biting me. Here's a more relevant query:
SELECT
tblCheckoutActivity.rid,
tblCheckoutActivity.checkedOutUser,
tblCheckoutActivity.checkedOutTime,
tblCheckoutInventory.serial,
tblCheckoutInventory.unit,
tblCheckoutInventory.tag,
tblCheckoutUsers.firstName,
tblCheckoutUsers.lastName
FROM
tblCheckoutActivity
LEFT JOIN
tblCheckoutInventory
ON
tblCheckoutActivity.scannerID = tblCheckoutInventory.SID
LEFT JOIN
tblCheckoutUsers
ON
tblCheckoutActivity.checkedOutUser = tblCheckoutUsers.badgeID
WHERE
tblCheckoutActivity.checkedInTime Is Null
AND
tblCheckoutInventory.site='San Francisco'
AND
(
DATEDIFF(hh, tblCheckoutActivity.checkedOutTime, GETDATE()) > 10
OR
DATEDIFF(hh, tblChecckoutActivity.checkedOutTime, GETDATE()) < 0
)
ORDER BY tblCheckoutUsers.lastName

I think your problem is somewhere else. I just ran this with no errors:
with temp as
(
select 1 as id, CAST('10/31/2011' as DATETIME) as timestamp
UNION
SELECT 2, CAST('11/1/2011' as DATETIME)
UNION
SELECT 3, CAST('11/2/2011' as DATETIME)
UNION
SELECT 4, CAST('11/3/2011' as DATETIME)
UNION
SELECT 5, CAST('11/6/2011' as DATETIME)
UNION
SELECT 6, CAST('11/8/2011' as DATETIME)
)
SELECT *
FROM temp
WHERE 1=1
AND (
DATEDIFF(hh, timestamp, GETDATE()) > 10
OR
DATEDIFF(hh, timestamp, GETDATE()) < 0
)
Are you showing us an abbreviated version of your query? Is there code before this query that is missing from your example?

If you're using the timestamp datatype, then that's your problem. You can't do a DATEDIFF comparison between a DATETIME and a timestamp.

SELECT *
FROM table
WHERE
DATEDIFF(hh, table.timestamp, GETDATE()) > 10
UNION
SELECT *
FROM table
WHERE
DATEDIFF(hh, table.timestamp, GETDATE()) < 0
quick and dirty but it should work

Related

Dynamic dates queries in SQL

I have a big dataset and I want to make it shorter in order to make it easier for Power BI to read. What I need is to get data for only 6 months having my date variable as FechaCarga in MyTable, which is refreshed daily and has daily data.
Example:
select *
from Mytable
where FechaCarga between (
select max(FechaCarga)
from MyTable)
and
--THIS IS THE PART THAT IM MISSING, PROBABLY USING DATEADD.
I expect data from Today (MaxDate) and MaxDate - 6 months. Please help me.
Thanks in advance,
IC
Is this what you want?
select t.*
from (select t.*, max(fechacarga) over () as max_fechacarga
from mytable t
) t
where fechacarga > dateadd(month, -6, max_fechacarga);
Like you said, just use DATEADD(). Try current_date to get today's date. (not sure if all DBMS's support that)
select *
from Mytable
where
FechaCarga between
(select max(FechaCarga) from MyTable)
and dateadd(month, -6, current_date)
The easiest way since you are always looking until the max date is:
select *
from Mytable
where FechaCarga >= dateadd(month, -6, (select max(FechaCarga) from MyTable))

select results from one date field compared to another + 24 hours

I need to pull values from one table that are only 24 hours from the date in another table in sybase ase 15.5.
Here is my wrong code:
SELECT p_generaldata.admissiontime,*
FROM Patient.dbo.P_MonVals P_MonVals
INNER JOIN Patient.dbo.P_GeneralData P_GeneralData
ON P_MonVals.PatientID=P_GeneralData.PatientID
where p_generaldata.admissiontime < P_MonVals.entertime +1
order by p_generaldata.patientid ASC
Im trying to return all rows in p_monvals, where the entertime in that table is less than 24 hours after the admissiontime.
the error im getting is INT is not compatible with DATETIME
Any help greatly appreciated
thank you
Take a look a the DateAdd function, and add a day to the entertime
Example from docs:
Adds one day to a date:
declare #a date
select #a = "apr 12, 9999"
select dateadd(dd, 1, #a)
In your case...
...
where p_generaldata.admissiontime < dateadd(dd, 1, P_MonVals.entertime)
Use function dateadd to sum 1 day in your date time:
dateadd(dd, 1, P_MonVals.entertime)
Reference: Sybase dateadd function.
You want to use the dateadd function:
SELECT p_generaldata.admissiontime, *
FROM Patient.dbo.P_MonVals P_MonVals
INNER JOIN Patient.dbo.P_GeneralData P_GeneralData
ON P_MonVals.PatientID=P_GeneralData.PatientID
WHERE p_generaldata.admissiontime < DATEADD(dd, 1, P_MonVals.entertime)
ORDER BY p_generaldata.patientid ASC

force number of rows to return in date range from SQL query

I'm running a query on our SQL (2012) database which returns a count of records in a given date range, grouped by the date.
For example:
Date Count
12/08 12
14/08 19
19/08 11
I need to fill in the blanks as the charts I plot get screwed up because there are missing values. Is there a way to force the SQL to report back a blank row, or a "0" value when it doesn't come across a result?
My query is
SELECT TheDate, count(recordID)
FROM myTable
WHERE (TheDate between '12-AUG-2013 00:00:00' and '20-AUG-2013 23:59:59')
GROUP BY TheDate
Would I need to create a temp table with the records in, then select from that and right join any records from myTable?
Thanks for any help!
If you create a (temporary or permanent) table of the date range, you can then left join to your results to create a result set including blanks
SELECT dates.TheDate, count(recordID)
FROM
( select
convert(date,dateadd(d,number,'2013-08-12')) as theDate
from master..spt_values
where type='p' and number < 9
) dates
left join yourtable on dates.thedate = convert(date,yourtable.thedate)
GROUP BY dates.TheDate
A temp table would do the job but for such a small date range you could go even simpler and use a UNION-ed subquery. E.g:
SELECT dates.TheDate, ISNULL(counts.Records, 0)
FROM
(SELECT TheDate, count(recordID) AS Records
FROM myTable
WHERE (TheDate between '12-AUG-2013 00:00:00' and '20-AUG-2013 23:59:59')
GROUP BY TheDate
) counts
RIGHT JOIN
(SELECT CAST('12-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('13-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('14-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('15-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('16-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('17-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('18-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('19-AUG-2013' AS DATETIME) AS TheDate
UNION ALL SELECT CAST('20-AUG-2013' AS DATETIME) AS TheDate
) dates
ON counts.TheDate = dates.TheDate
Here's a SQL Fiddle Demo.
If you need a more generic (but also more complex) solution, take a look at this excellent answer (by #RedFilter) to a similar question.

SQL Count to include zero values

I have created the following stored procedure that is used to count the number of records per day between a specific range for a selected location:
[dbo].[getRecordsCount]
#LOCATION as INT,
#BEGIN as datetime,
#END as datetime
SELECT
ISNULL(COUNT(*), 0) AS counted_leads,
CONVERT(VARCHAR, DATEADD(dd, 0, DATEDIFF(dd, 0, Time_Stamp)), 3) as TIME_STAMP
FROM HL_Logs
WHERE Time_Stamp between #BEGIN and #END and ID_Location = #LOCATION
GROUP BY DATEADD(dd, 0, DATEDIFF(dd, 0, Time_Stamp))
but the problem is that the result does not show the days where there are zero records, I pretty sure that it has something to do with my WHERE statement not allowing the zero values to be shown but I do not know how to over come this issue.
Thanks in advance
Neil
Not so much the WHERE clause, but the GROUP BY. The query will only return data for rows that exist. That means when you're grouping by the date of the timestamp, only days for which there are rows will be returned. SQL Server can't know from context that you want to "fill in the blanks", and it wouldn't know what with.
The normal answer is a CTE that produces all the days you want to see, thus filling in the blanks. This one's a little tricky because it requires a recursive SQL statement, but it's a well-known trick:
WITH CTE_Dates AS
(
SELECT #START AS cte_date
UNION ALL
SELECT DATEADD(DAY, 1, cte_date)
FROM CTE_Dates
WHERE DATEADD(DAY, 1, cte_date) <= #END
)
SELECT
cte_date as TIME_STAMP,
ISNULL(COUNT(HL_Logs.Time_Stamp), 0) AS counted_leads,
FROM CTE_Dates
LEFT JOIN HL_Logs ON DATEADD(dd, 0, DATEDIFF(dd, 0, Time_Stamp)) = cte_date
WHERE Time_Stamp between #BEGIN and #END and ID_Location = #LOCATION
GROUP BY cte_date
Breaking it down, the CTE uses a union that references itself to recursively add one day at a time to the previous date and remember that date as part of the table. If you ran a simple statement that used the CTE and just selected * from it, you'd see a list of dates between start and end. Then, the statement joins this list of dates to the log table based on the log timestamp date, while preserving dates that have no log entries using the left join (takes all rows from the "left" side whether they have matching rows on the "right" side or not). Finally, we group by date and count instead and we should get the answer you want.
When there is no data to count, there is no row to return.
If you want to include empty days as a 0, you need to create a table (or temporary table, or subquery) to store the days, and left join to your query from that.
eg: something like
SELECT
COUNT(*) AS counted_leads,
CONVERT(VARCHAR, DATEADD(dd, 0, DATEDIFF(dd, 0, Time_Stamp)), 3) as TIME_STAMP
FROM
TableOfDays
left join
HL_Logs
on TableOfDays.Date = convert(date,HL_Logs.Time_Stamp)
and ID_Location = #LOCATION
WHERE TableOfDays.Date between #BEGIN and #END
GROUP BY DATEADD(dd, 0, DATEDIFF(dd, 0, Time_Stamp))
Use a left outer join. Such as
select count(stuff_ID), extra_NAME
from dbo.EXTRAS
left outer join dbo.STUFF on suff_EXTRA = extra_STUFF
group by extra_NAME
I just recently has a similar task and used this as a backdrop to my work. However, as explained by robwilliams I too, couldn't get it KeithS solution to work. Mine task was slightly different I was doing it by hours vs days but I think the solution to the neilrudds question would be
DECLARE #Start as DATETIME
,#End as DATETIME
,#LOCATION AS INT;
WITH CTE_Dates AS
(
SELECT #Start AS cte_date, 0 as 'counted_leads'
UNION ALL
SELECT DATEADD(DAY, 1, cte_date) as cte_date, 0 AS 'counted_leads'
FROM CTE_Dates
WHERE DATEADD(DAY, 1, cte_date) <= #End
)
SELECT cte_date AS 'TIME_STAMP'
,COUNT(HL.ID_Location) AS 'counted_leads'
FROM CTE_Dates
LEFT JOIN HL_Logs AS HL ON CAST(HL.Time_Stamp as date) = CAST(cte_date as date)
AND DATEPART(day, HL.Time_Stamp) = DATEPART(day,cte_date)
AND HL.ID_Location = #LOCATION
group by cte_date
OPTION (MAXRECURSION 0)

Can I use recursion in a Sql Server 2005 View?

I tried to use OPTION (MAXRECURSION 0) in a view to generate a list of dates.
This seems to be unsupported. Is there a workaround for this issue?
EDIT to Explain what I actually want to do:
I have 2 tables.
table1: int weekday, bool available
table2: datetime date, bool available
I want the result:
view1: date (here all days in this year), available(from table2 or from table1 when not in table2).
That means I have to apply a join on a date with a weekday.
I hope this explanation is understandable, because I actually use more tables with more fields in the query.
I found this code to generate the recursion:
WITH Dates AS
(
SELECT cast('2008-01-01' as datetime) Date
UNION ALL
SELECT Date + 1
FROM Dates
WHERE Date + 1 < DATEADD(yy, 1, GETDATE())
)
No - if you can find a way to do it within 100 levels of recusion (have a table of numbers), which will get you to within 100 recursion levels, you'll be able to do it. But if you have a numbers or pivot table, you won't need the recursion anyway...
See this question (but I would create a table and not a table-valued function), this question and this link and this link
You can use a CTE for hierarchical queries.
Here you go:
;WITH CTE_Stack(IsPartOfRecursion, Depth, MyDate) AS
(
SELECT
0 AS IsPartOfRecursion
,0 AS Dept
,DATEADD(DAY, -1, CAST('01.01.2012' as datetime)) AS MyDate
UNION ALL
SELECT
1 AS IsPartOfRecursion
,Parent.Depth + 1 AS Depth
--,DATEADD(DAY, 1, Parent.MyDate) AS MyDate
,DATEADD(DAY, 1, Parent.MyDate) AS MyDate
FROM
(
SELECT 0 AS Nothing
) AS TranquillizeSyntaxCheckBecauseWeDontHaveAtable
INNER JOIN CTE_Stack AS Parent
--ON Parent.Depth < 2005
ON DATEADD(DAY, 1, Parent.MyDate) < DATEADD(YEAR, 1, CAST('01.01.2012' as datetime))
)
SELECT * FROM CTE_Stack
WHERE IsPartOfRecursion = 1
OPTION (MAXRECURSION 367) -- Accounting for leap-years
;