"CASE" statement within "WHERE" clause in SQL Server 2012 - sql

i have a select statement like this
select x, y , z from table a
where CAST( CAST(DATEColumn AS VARCHAR) AS DATE) between
case when a.columnx = '1900-01-01' then CAST( CAST(DATEColumn AS VARCHAR) AS DATE) else c.columnx end
and cast( c.CustomerShipToRecTrmDt as date)
why does this case statement does not work
whereas the case statement works if i hardcode the dates?

What are you casting a date columns to varchar and then back to a date? I think this does what you want:
select x, y , z
rom table a
where DATEColumn between
(case when a.columnx = '1900-01-01' then DATEColumn else cast(c.columnx as date) end and
cast( c.CustomerShipToRecTrmDt as date)
You can eliminate the case statement from the where clause by doing:
where DATEColumn >= cast(c.columnx as date) and DateColumns <= cast( c.CustomerShipToRecTrmDt as date)
The comparison to 1900-01-01 doesn't affect the >= part, unless you are working with dates before 1900 (I'm guessing this is unlikely).
Also, whenever casting values to varchar(), always include the length (as in varchar(255)).

Related

SQL replace using SELECT and invalid column

I have a simple table where the date column answer_6 (formatted as varchar(max) either has a valid date or string Now. I wanted to substitute the Now with the current date/time and then calculate the difference. Here is what I did:
select
CASE [answer_6]
WHEN 'Now' THEN CONVERT(varchar, GETDATE())
ELSE answer_6
END as x,
answer_6 from [tDataMult] where DATEDIFF(yy, GETDATE(), [x]) > 5
The system give me invalid column name 'x'. If I remove the DateDiff SELECT statement works fine.
You can't use aliases in the WHERE clause of the same level as it was defined as WHERE clause is evaluated before the SELECT clause. Try the following :
SELECT t.* FROM (<...>) t WHERE DATEDIFF(yy, GETDATE(), [t.x]) > 5
Instead <...> put your query without WHERE clause.
You can't refer to an alias you've just created in a WHERE clause. The easiest way to fix it would just be to turn your original query into a subquery:
SELECT *
FROM
(
select
CASE [answer_6]
WHEN 'Now' THEN CONVERT(varchar, GETDATE())
ELSE answer_6
END as x,
answer_6 from [tDataMult]
) AS qry
WHERE DATEDIFF(yy, GETDATE(), [x]) > 5
Or you could replicate the expression in your WHERE clause:
select
CASE [answer_6]
WHEN 'Now' THEN CONVERT(varchar, GETDATE())
ELSE answer_6
END as x,
answer_6 from [tDataMult]
WHERE DATEDIFF(yy, GETDATE(),
(CASE [answer_6]
WHEN 'Now' THEN CONVERT(varchar, GETDATE())
ELSE answer_6
END)
) > 5
Column X does not exist in tDataMult table because it is an alias.
Replace it with answer_6 in DateDiff function.
However, one of the possible value of answer_6 column is varchar format ('Now').
You need to have valid data format to use DATEDIFF function properly. (for example: 'yyyy-mm-dd').

Date Time conversion/ Date diff in SSRS

I have an SSRS report where I am using below query.
(This query works fine in SQL server, problem is only in SSRS report)
--DECLARE #Range Number = 10;
SELECT * FROM TBL1 WHERE
USERNAME = 'MIKE'
AND
(
#Range = '10'
and
Convert(datetime, MyDate, 120) <= GETDATE()
)
or
(
#Range IN ('20','30')
and
DATEDIFF(DD, Convert(datetime, MyDate, 120) , GETDATE()) <= #Range
)
Unfortunately the myDate Column coming from database is a varchar column.
The SSRS throws an out-of-range exception.
Then I tried converting Getdate to Convert(varchar(10), getdate(), 120) and compare with myDate (without conversion as myDate is already in YYYY-MM-DD format but as a varchar in database)it still throws error. I assume this time coz SSRS is not able to process datediff in varchar columns.
When I run these queries individually, it works fine. i.e -
Declare #Range Number = 10;
Select * from tbl1
where username = 'MIKE' and
(
#Range = '10'
and
convert(datetime, MyDate, 120) <= getdate()
Has anyone faced similar issue in SSRS ???
Try this!
SELECT * FROM TBL1 WHERE
USERNAME = 'MIKE'
AND
(
#Range = '10'
and
cast(myDate as datetime) <= GETDATE()
)
or
(
#Range IN ('20','30')
and
cast(myDate as datetime) , GETDATE()) <= #Range
)
Presumably, your problem is that some values of myDate are not in the correct format. If you are using a more recent version of SQL Server, the function try_convert() can be a big help.
The reason it is failing is because of your logic. The second condition after the or is looking at all records, not just Mike's. I think you intend for the where clause to be:
SELECT *
FROM TBL1 WHERE
WHERE USERNAME = 'MIKE' AND
((#Range = '10' and Convert(datetime, MyDate, 120) <= GETDATE()
) or
(#Range IN ('20','30') and
DATEDIFF(DD, Convert(datetime, MyDate, 120) , GETDATE()) <= #Range
)
);
Note the extra set of parentheses. Now, this will probably help for this particular query for MIKE. But it won't help overall. Finding the value(s) that fail conversion can be daunting. If you are lucky, they fail easily. You can look for them with starting with:
select MyDate
from tbl1
where MyDate not like '[0-9][0-9][0-9][0-9]-[0-2][0-9]-[0-3][0-9]%';
If you are lucky, then this will find the offending values. Otherwise, you'll have to dive more deeply into the date formats, looking at the particular month and day (and potentially hour, minute, and second) values.
Moral: Store dates as dates. Don't store them as varchar().

only date and only time in same column - sql server

I have a web portal that should display only time if the date is today's date otherwise it should display date .
dossier_date_created | Expected result
----------------------------------------------------
2013-10-22 16:18:46.610 | 2013-10-22
2013-10-23 11:26:56.390 | 11:26
I tried something like this :
select
case
when CONVERT(date,dossier_date_created) = CONVERT(DATE,getdate()) then convert(char(5), dossier_date_created, 108)
else convert(date, dossier_date_created)
end as timedate
from Proj_Manage_Dossier
But the result was :
timedate
-----------
2013-10-22
1900-01-01
How can I do it only with SQL? And btw Datatype of my column "dossier_date_created" is datetime
You can just use CAST or CONVERT to get your values to DATE or TIME datatypes. But, since you can't combine two data types in same column you have to cast them both to something identical - like NVARCHAR afterwards:
SELECT CASE WHEN CAST(dossier_date_created AS DATE) = CAST(GETDATE() AS DATE)
THEN CAST(CAST (dossier_date_created AS TIME) AS NVARCHAR(10))
ELSE CAST(CAST(dossier_date_created AS DATE) AS NVARCHAR(10)) END
FROM dbo.Proj_Manage_Dossier
SQLFiddle DEMO
EDIT - For SQL Server versions older then 2008, where there are no DATE and TIME datatypes - You can also directly CONVERT to VARCHAR using appropriate style codes.
SELECT CASE WHEN CONVERT(VARCHAR(10),dossier_date_created,102) = CONVERT(VARCHAR(12),GETDATE(),102)
THEN CONVERT(VARCHAR(10),dossier_date_created,108)
ELSE CONVERT(VARCHAR(10),dossier_date_created,102) END
FROM dbo.Proj_Manage_Dossier
SQLFiddle DEMO
This works fine for me:
select
case
when (CONVERT(date,PaymentDate) = CONVERT(DATE,getdate()))
then convert(VARCHAR(15), PaymentDate, 108)
else convert(varchar, PaymentDate, 101)
end as timedate
from Payments
Hope it will help you.. :)
This will work
select case when cast(dossier_date_created as date) = cast(getdate() as date)
then cast(cast(dossier_date_created as time) as varchar)
else cast(cast(dossier_date_created as date) as varchar)
end from #Proj_Manage_Dossier
Hope it helps :)
cheers......

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