I have the below table:
PasswordHistoryId ClientId Password CreationDate (dd/MM/YYYY)
1 1 abcd 05/01/2023
2 1 xyz 11/08/2022
3 2 efg 11/12/2022
I want to check if the latest password set has been expired. A password is considered as expired if it has been created more than 90 days ago.
I have the below SQL to check if a password has expired:
SELECT TOP 1 1 -- Returns 1 if password has expired
FROM PASSWORD_HISTORY CHP
WHERE (DATEDIFF(DAY, CHP.CreationDate, GETDATE())) > 90
AND CHP.ClientId = 1
ORDER BY CHP.CreationDate DESC
However this returns me 1 because of the second line in the table where the password was set on the 11th of August 2022 and it has already been expired. I need to do the comparison on the latest record based on the creation date.
Any idea of how I can do this?
Use aggregation:
SELECT CASE
WHEN DATEDIFF(DAY, MAX(CreationDate), GETDATE()) > 90 THEN 1
ELSE 0
END AS expired
FROM PASSWORD_HISTORY
WHERE ClientId = 1;
See the demo.
Why don't you just check if they have a record where the password hasn't expired - e.g., if they have a password less than or equal to 90 days, then don't flag them.
No sorting or other functions required.
IF NOT EXISTS
(SELECT *
FROM PASSWORD_HISTORY CHP
WHERE (DATEDIFF(DAY, CHP.CreationDate, GETDATE())) <= 90
AND CHP.ClientId = 1
)
SELECT 'Password expired';
Edit: Sargability - change WHERE to use indexes
The above (and potentially in other answers) is a non-sargable query. It has to do the calculation for every row even if you have an index.
If we calculate the cutoff/criterion date instead, then just compare with the data directly, it allows use of indexes if you have them.
IF NOT EXISTS
(SELECT *
FROM PASSWORD_HISTORY CHP
WHERE CHP.CreationDate >= DATEADD(day, -90, CAST(getdate() AS date))
AND CHP.ClientId = 1
)
SELECT 'Password expired';
Just move DATEDIFF check to SELECT statement instead of WHERE:
SELECT TOP 1 CASE WHEN (DATEDIFF(DAY, CHP.CreationDate, GETDATE())) > 90 THEN 1 END
FROM PASSWORD_HISTORY CHP
WHERE CHP.ClientId = 1
ORDER BY CHP.CreationDate DESC
This will return NULL, though, if password is not expired. If you want to return nothing, you do need to use CTE or sub-query:
WITH CTE AS
(
SELECT *, ROW_NUMBER() OVER (ORDER BY CHP.CreationDate DESC) RN
FROM PASSWORD_HISTORY CHP
WHERE CHP.ClientId = 1
)
SELECT 1
FROM CTE
WHERE RN = 1
AND (DATEDIFF(DAY, CreationDate, GETDATE())) > 90
Related
I have a phone and a call date field. I need to find all phone and call dates where calls were made more than once (>1) within a 7 day period.
What is the best approach to this?
Example:
ID|Phone|CallDate
-----------------
1|5551212|11/21/2020
2|5551212|11/22/2020
3|5551212|10/9/2020
4|4441212|11/22/2020
5|4441212|11/1/2020
output:
5551212|11/21/2020
5551212|11/22/2020
Here's an example query I tried but I assume I can do better (besides, its taking a very long time, over 1 million records):
SELECT A1.Phone
FROM CallDetail A1, CallDetail A2
WHERE (A1.Phone = A2.Phone) AND (A1.ID <> A2.ID)
GROUP BY A1.Phone, A1.CallDate, A2.CallDate
HAVING COUNT(A1.Phone) > 1 AND DATEDIFF(DAY, A1.CallDate, A2.CallDate) <= 7
You seem to want lead() and lag() to compare the calldate on one row to the nearest calldate before or after:
select cd.*
from (select cd.*
lag(cd.calldate) over (partition by cd.phone order by cd.calldate) as prev_calldate,
lead(cd.calldate) over (partition by cd.phone order by cd.calldate) as next_calldate
from calldetail cd
) cd
where prev_calldate > dateadd(day, -7, calldate) or
next_calldate < dateadd(day, 7, calldate)
order by phone, calldate;
It's not quite working for me!
My query is as follows:
SELECT COUNT (*) as [generic]
FROM [Log]
Where value IN (Select ID, tsSendDocument, sysReceivedFrom
WHERE sysReceivedFrom = 'generic' AND
DATEDIFF(hour, tsSendDocument, GetDate()) > 2)
So, what am I doing wrong here?
I want it to to count every time the tsSendDocument column is older than 2 hours. It will eventually give me a count that's equal to 1. I have a table set up to alert me if the value = 1, which means that the tsSendDocument is older than 2 hours.
Do this make any sense?
As per your comment, I've understood that you want to check if the last entry is older than 2 hours, so this should work:
SELECT TOP 1 CASE WHEN tsSendDocument < DATEADD(HOUR, -2, GETDATE()) THEN 1 ELSE 0 END AS [generic]
FROM [Log]
ORDER BY tsSendDocument DESC
I think you want only aggregation :
Select COUNT(*)
FROM Log
WHERE sysReceivedFrom = 'generic' AND
DATEDIFF(hour, tsSendDocument, GetDate()) > = 2;
subquery will only return one expression when you specified IN orNOT IN clause.
I have an sql table (t_accountdetails) with an account column called AccountId and effective date column for that account. An account can have multiple effective date. I have a requirement to get all the entries for the accounts which has very close effective date entries.(an offset of +/-14 days)
Say for eg:
AccountId: 12345 has got 2 entries with effective date 12/11/2017 and 12/18/2017
So my query should return above case where we have an entry of effective dates within offset of +/-14days
Please note I am actually not looking for date +-14 from today. I am looking for effective date which +/- 14 days of another effective date for the same account
You want all records where exists another effective date within 14 days, so use WHERE EXISTS:
select *
from t_accountdetails t
where exists
(
select *
from t_accountdetails other
where other.accountid = t.accountid
and other.id <> t.id
and abs(datediff(day, other.effective_date, t.effective_date)) <= 14
)
order by accountid, effective_date;
You can use the DATEADD function to make it work
select * from t_accountdetails where AccountId = 12345 and effectiveDate >= DATEADD(day, -14, getdate()) and effectiveDate <= DATEADD(day, 14, getdate())
This will return all records with AccountID = 12345 and an effective date between today - 14 days and today + 14 days.
Note: if more than one record match the criteria then all matching records will be returned.
I would be inclined to use lag() and lead():
select ad.*
from (select ad.*,
lag(effective_date) over (partition by accountid order by effective_date) as prev_ed,
lead(effective_date) over (partition by accountid order by effective_date) as next_ed
from t_accountdetails ad
) ad
where effective_date <= dateadd(day, 14, prev_ed) or
effective_date >= dateadd(day, -14, next_ed);
It would be interesting to compare the performance of this version to the exists version with an index on t_accountdetails(accountid, effective_date).
We currently have a table that logs traffic through our service. We're looking for a way to do some diagnostics/alerts based on the traffic, but find that at 2am we don't have enough traffic to be reliable (1 fail at 2 am can be a 50% failure rate, but at 9am when people work up, 1 fail can be .01%).
We'd like to check on the last 10 minutes by server, if the last 10 minutes doesn't have 300 records in it, we'd like to go back until we have 300 records.
Is there a way to do this in a query?
Our table looks like:
ID INT,
ServerID INT,
Success BIT,
ActionDate DATETIME
I could use ROW_NUMBER() to get the last 300, but if the traffic is high enough that this isn't the full last 10 minutes, we're missing data that might be relevant.
I'm looking to do something along the lines of
SET _RowCount = SELECT COUNT(*) FROM tbl WHERE Date >= DATEADD(M, -10, GETDATE());
IF _RowCount < 300 SET #RowCount = 300;
SELECT TOP _RowCount records
But can't seem to use a variable as the TOP count.
Using the last 300 records seems like the easiest thing to do:
SELECT top 300 t.*
FROM tbl t
order by date desc;
But you can do what you want with a single query:
select t.*
from (select t.*, row_number() over (order by date desc) as seqnum
from tbl t
) t
where seqnum <= 300 or Date >= DATEADD(M, -10, GETDATE());
Not as neat as yours, but:
SET #RowCount = SELECT COUNT(*) FROM tbl WHERE Date >= DATEADD(M, -10, GETDATE());
IF #RowCount < 300
BEGIN
SELECT TOP 300 records order by Date Desc
END
ELSE
SELECT records WHERE Date >= DATEADD(M, -10, GETDATE())
END
SQL Server will allow you to use a variable or a query for the TOP parameter if enclosed in parentheses.
So
SELECT (#RowCount) * FROM tbl
works while
SELECT #RowCount * FROM tbl
does not
Thanks to StackOverflow for: Dynamic SELECT TOP #var In SQL Server I can use a variable and do an if to make it the proper value.
Thanks
I have this MS SQL 2005 query:
SELECT
DATEDIFF(dd, getdate(), CreatedOn) as Day,
COUNT(CreatedOn) as 'Active Cases'
FROM
[dbo].[IncidentBase]
WHERE
(StatusCode != 6 AND StatusCode != 5)
AND (CaseTypeCode = '200000' OR CaseTypeCode = '200005' OR CaseTypeCode = '200006')
GROUP BY
DATEDIFF(dd, getdate(), CreatedOn)
ORDER BY
Day DESC
And returns something like this:
-1 10
-2 6
-5 4
-7 8
I would really like it to be like:
-1 10
-2 6
-3 0
-4 0
-5 4
-6 0
-7 8
(Insert zero between dates with no records)
How can I do that?
Many thanks in advance!
Try an outer join on a subquery returning all the dates
SELECT table_cal.day_diff as "Day",
COALESCE(table_count.base_count,0) as "Active Cases"
FROM
(SELECT DISTINCT DATEDIFF(dd, getdate(), ibase.CreatedOn) as day_diff
FROM [dbo].[IncidentBase] ibase) table_cal
LEFT OUTER JOIN
(SELECT DATEDIFF(dd, getdate(), ibase.CreatedOn) as day_diff,
COUNT(ibase.CreatedOn) as base_count
FROM [dbo].[IncidentBase] ibase
WHERE ibase.StatusCode NOT IN (5,6) AND ibase.CaseTypeCode IN ('200000','200005','200006')
GROUP BY DATEDIFF(dd, getdate(), ibase.CreatedOn)) table_count
ON (table_cal.day_diff = table_count.day_diff)
ORDER BY table_cal.day_diff DESC
The idea behind is quite simple. You need a subquery to generate the list of existing dates, and another to generate the result values. Then you outer join both and replace null values by 0.