SQL: Cast Not working in SubString - sql

We have a string field for date of birth and now we have to convert it in order to perform the calculations required. However when we are using CAST or CONVERT to convert to perform the calculations it is not working.
select distinct(ptr.RecordID)
from dbo.PatientRecord as ptr
where
ptr.CHName like 'Access2Loc%'
AND ptr.RecordID
in(
select
(
case when
(DATEDIFF(hour, convert(date,DOB,110), GETDATE())/8766)>18
then PatientID
else NULL
end
) as RecordID
from
PatientView
where ISDATE(DOB) = 1
)

SQL Server considers this a "feature". It is hard to explain, but the where is not necessarily executed before the select.
In SQL Server 2012+, use try_convert() (or try_cast():
where ptr.CHName like 'Access2Loc%' and
ptr.RecordID in (select (case when DATEDIFF(hour, try_convert(date, DOB, 110), GETDATE()) / 8766 > 18
then PatientID
end) as RecordID
from PatientView
where ISDATE(DOB) = 1
)
In more ancient versions, you can use a case expression for much the same effect.

-- this will guarantee that ##t1 contains only valid dob rows
if object_id('tempdb..##t1') is not null
drop table #t1
select * into ##t1 from patientview where isdate(dob)=1
select distinct(ptr.RecordID)
from dbo.PatientRecord as ptr
where
ptr.CHName like 'Access2Loc%'
AND ptr.RecordID
in(
select
(
case when
(DATEDIFF(hour, convert(date,DOB,110), GETDATE())/8766)>18
then PatientID
else NULL
end
) as RecordID
from
##t1
)
drop table ##t1

Related

SQL case statement closest to current date

Need help create a case statement to find the closest date from date table. My data: https://imgur.com/hkBu4SA
I basically want to set:
Y flag if it's closest to today's date from a.FROM_EFFDT and is not null.
F if to_effdate is null
else N
WHEN a.FROM_EFFDT < GETDATE() AND (to_effdate) IS NOT NULL THEN 'Y'
WHEN to_effdate IS NULL THEN 'F'
ELSE 'N'
You can use window functions:
(case when row_number() over (order by abs(datediff(day, getdate(), to_effdate)) = 1
then 'Y'
when to_effdate is null then 'F'
else 'N'
end)
You may be able to accomplish it with something like this. Though this isn't bulletproof, you could get duplicates if the closest date is tied.
create table Dates (from_effdt datetime, to_effdt datetime, flag varchar(1))
insert Dates (from_effdt, to_effdt, flag)
values
('2019-03-16', null, '') ,
('2018-06-14', '2019-03-16', '') ,
('2018-05-14', '2018-06-14', '') ,
('2018-01-01', '2018-05-14', '')
select * from Dates
UPDATE Dates
SET flag =
CASE
WHEN from_effdt = (
select top 1 from_effdt
from Dates
order by ABS ( DATEDIFF(day, from_effdt, getdate()) )
)
THEN 'Y'
ELSE
'N'
END
*update, not sure why I created it as an update. This select should do.
SELECT from_effdt, to_effdt,
CASE
WHEN from_effdt = (
select top 1 from_effdt
from Dates
order by ABS ( DATEDIFF(day, from_effdt, getdate()) )
)
THEN 'Y'
ELSE
'N'
END [numberOfDaysAway]
FROM Dates
You can simply do this:
CASE
WHEN from_effdt = (
select from_effdt
from Dates
where abs(datediff(second, from_effdt, getdate()))
= (select min(
abs(datediff(second, from_effdt, getdate()))
)
from Dates)
)
THEN 'Y'
ELSE
'N'
END
ROW_NUMBER() Over (Partition by id order by to_effdt desc)
,id
,from_effdt
,to_effdt
, CASE WHEN (ROW_NUMBER() Over (Partition by id order by to_effdt desc) = 1) THEN ('Y')
WHEN (to_effdt IS NULL) THEN ('F') ELSE ('N') End as flag
from a

converting integer to date and getting records based on this date

I have a table (SQLServer 2008r2) with an integer which is a date. The format is YYYYMMDD (no i did not have design control over this and would not ever do it by choice. It can not be changed).
I want to convert it into a date and get all the records where this field's value is < the current day minus 14 days. This is what I have which is working:
SELECT * from webFormsInstances where formStage <> 'Complete'
AND cast(convert(DATETIME, LEFT(formActionDate, 8)) as date) < dateAdd(day,-14,getdate())
order by formActionDateTime desc
Is there a better more efficient way to do this way to do this?
If you use a formula on the column, sqlserver will not be able to utilize any index on the column since it must perform the calculation on all rows.
(look for sargable. e.g. here)
Do the calculation on the constant instead.
SELECT *
FROM webFormsInstances
WHERE formStage <> 'Complete'
AND formActionDate < CONVERT(int, CONVERT(varchar(8), dateAdd(day,-14, getdate()), 112))
ORDER BY formActionDateTime DESC
Please check this one.
declare #t table(id int, formStage varchar(50), formActionDate int)
insert into #t values (1 ,'notComplete', 20150101), (2 ,'Complete', 20150128),(3 ,'notComplete', 20150129),(2 ,'notComplete', 20150131),(3 ,'notComplete', 20150201),(4 ,'notComplete', 20150209),(5 ,'notComplete' , 20150220),
(6 ,'notComplete', 20150115),(7,'notComplete', 20150113)
select CONVERT(DATETIME, cast(formActionDate as varchar) ,114),* from #t
where formStage <> 'complete'
SELECT * from #t where formStage <> 'Complete'
AND --cast(convert(DATETIME, LEFT(formActionDate, 8)) as date)
CONVERT(DATETIME, cast(formActionDate as varchar) ,114)
< dateAdd(day,-14,getdate())
order by formActionDate desc

Split and select specific values from sqlserver table

I have a table projects in SQL server having a field StartDate with values as
15-02-2013 15:02:40
20-08-2011 10:11:20
etc
From which i need to retrieve distinct year values like
2013 and 2011 only
I tried with queries like
select (case when charindex(' ', StartDate) > 0
then left(StartDate, charindex(' ', (StartDate))-1)
else StartDate
end) as StartDate FROM [kneipp].[dbo].[kn_projects]
which gave result as 15-02-2013 and 20-08-2011
and with
select (case when charindex('-', StartDate) > 0
then right(StartDate, charindex('-', reverse(StartDate))-1)
else StartDate
end) as lastone FROM [kneipp].[dbo].[kn_projects]
got 2013 15:02:40 and 2011 10:11:20
How to achieve my expected result
did you try expression like this?
SELECT distinct year( convert(datetime,StartDate,105))
FROM [kneipp].[dbo].[kn_projects]
You can CAST the column into a DATETIME format
SELECT YEAR(CAST(StartDate AS DATETIME)) FROM [kneipp].[dbo].[kn_projects]
As others have pointed out you can convert to datetime ,if you want to retain the data type then try the below query
Declare #t varchar(50),#t1 varchar(50)
set #t='15-02-2013 15:02:40'
Select left(parsename(replace(#t,'-','.'),1),4)
If you have datetime in any format then try the below expression which will return the year in varchar data type
Select left(parsename(replace(#t1,'-','.'),1),
charindex(' ',parsename(replace(#t1,'-','.'),1)))

Is there a safe way to cast SQL datetimes?

Pretty simple question that I can't quite find the answer for:
Is there a simple and safe† way to cast a varchar to datetime in SQL Server?
† i.e. gracefully handle non-datetime strings with a default datetime value
You can filter your rows using the isdate function. This query returns three rows without any conversion errors:
with v as (
select '20110714' value union all
select '2011-07-15' union all
select '3/22/2011' union all
select 'foo'
)
select cast(value as datetime)
from v
where isdate(value) = 1
Edit
When you want a default vaue (like the current date/time), you could do something like this:
select case when isdate(value) = 1 then cast(value as datetime) else getdate() end
From SQL Server Denali you can use TRY_CONVERT. A case expression is the only safe way in previous versions.
You could try
select cast ('28/08/2006 11:23:25' as datetime)
SELECT * FROM tab_val WHERE (CASE ISDATE(val) WHEN 1 THEN CAST(val As DateTime) ELSE NULL END)
More details in the manual
we can place a check that column have the valid datetime and if not then return nulls not genrating any error.
SELECT CASE ISDATE([YourDate]) WHEN 0
THEN CAST([YourDate] AS DATETIME)
ELSE CAST(NULL AS DATETIME) END AS [Your Date]
FROM [dbo].[yourtable]

How I can select / sort dates by period intervals?

For ex:
If we have in table records like:
25/06/2009
28/12/2009
19/02/2010
16/04/2011
20/05/2012
I want to split/select this dates according to 6 month intervals starting from current date.
result should be like:
0-6 month from now: first record
7-12 month from now: second record
...
It will be much apreciated if you make this simple as I made it very stupid and complicated like:
declare variable like t1=curdate()+6
t2=curdate()+12
...
then selected records to fit between curdate() and t1, then t1 and t2 etc.
Thanks,
r.
CORRECTION: Had it backwards, Need to use Modulus, not integer division - sorry...
If MonthCount is a calculated value which counts the number of months since a specific Dec 31, and mod is modulus division (output the remainder after dividing)
Select [Column list here]
From Table
Group By Case When MonthCount Mod 12 < 6
Then 0 Else 1 End
In SQL Server, for example, you could use the DateDiff Function
Select [Column list here]
From Table
Group By Case When DateDiff(month, myDateColumn, curdate) % 12 < 6
Then 0 Else 1 End
( in SQL Server the percent sign is the modulus operator )
This will group all the record into buckets which each contain six months of data
SELECT (DATEDIFF(MONTH, thedate, GETDATE()) / 6) AS semester,
SUM(receipt)
FROM thetable
GROUP BY semester
ORDER BY semester
the key idea is grouping and ordering by the expression that gives you the "semester".
This question really baffled me, cos I couldn't actually come up with a simple solution for it. Damn.
Best I could manage was an absolute bastardization of the following where you create a Temp Table, insert the "Periods" into it, join back to your original table, and group off that.
Assume your content table has the following
ID int
Date DateTime
Counter int
And you're trying to sum all the counter's in six month periods
DECLARE #min_date datetime
select #min_date = min(date) from test
DECLARE #max_date datetime
select #max_date = max(date) from test
DECLARE #today_a datetime
DECLARE #today_b datetime
set #today_a = getdate()
set #today_b = getdate()
CREATE TABLE #temp (startdate DateTime, enddate DateTime)
WHILE #today_a > #min_date
BEGIN
INSERT INTO #temp (startDate, endDate) VALUES (dateadd(month, -6, #today_a), #today_a)
SET #today_a = dateadd(month, -6, #today_a)
END
WHILE #today_b < #max_date
BEGIN
INSERT INTO #temp (startDate, endDate) VALUES (#today_b, dateadd(month, 6, #today_b))
SET #today_b = dateadd(month, 6, #today_b)
END
SELECT * FROM #temp
SELECT
sum(counter),
'Between ' + Convert(nvarchar(10), startdate, 121) + ' => ' + Convert(nvarchar(10), enddate, 121) as Period
FROM test t
JOIN #Temp ht
ON t.Date between ht.startDate AND ht.EndDate
GROUP BY
'Between ' + Convert(nvarchar(10), startdate, 121) + ' => ' + Convert(nvarchar(10), enddate, 121)
DROP TABLE #temp
I really hope someone can come up with a better solution my brain has obviously melted.
Not quite what you're attempting to accomplish, but you could use the DATEDIFF function to distinguish the ranging of each record:
SELECT t.MonthGroup, SUM(t.Counter) AS TotalCount
FROM (
SELECT Counter, (DATEDIFF(m, GETDATE(), Date) / 6) AS MonthGroup
FROM Table
) t
GROUP BY t.MonthGroup
This would create a sub query with an expression that expresses the date ranging group you want. It would then group the sub-query by this date ranging group and you can then do whatever you want with the results.
Edit: I modified the example based on your example.
If you're using SQL Server:
SELECT *,
(
FLOOR
(
(
DATEDIFF(month, GETDATE(), date_column)
- CASE WHEN DAY(GETDATE()) > DAY(date_column) THEN 1 ELSE 0 END
) / 6.0
) * 6
) AS SixMonthlyInterval
FROM your_table
If you're using MySQL:
SELECT *,
(
FLOOR
(
(
((YEAR(date_column) - YEAR(CURDATE())) * 12)
+ MONTH(date_column) - MONTH(CURDATE())
- CASE WHEN DAY(CURDATE()) > DAY(date_column) THEN 1 ELSE 0 END
) / 6.0
) * 6
) AS SixMonthlyInterval
FROM your_table