I'm creating a report using SQL to pull logged labor hours from our labor database for the previous month. I have it working great, but need to add logic to prevent it from breaking when it runs in January. I've tried adding If/Then statements and CASE logic, but I don't know if I'm just not doing it right, or if our system can't process it. Here's the snippet that pulls the date range:
SELECT
...
FROM
...
WHERE
...
AND
YEAR(ENTERDATE) = YEAR(current date) AND MONTH(ENTERDATE) = (MONTH(current date)-1)
Just use AND as a barrier like this. In January, the second clause will be executed instead of the first one:
SELECT
...
FROM
...
WHERE
...
AND
(
(
(MONTH(current date) > 1) AND
(YEAR(ENTERDATE) = YEAR(current date) AND MONTH(ENTERDATE) = (MONTH(current date)-1))
-- this one gets used from Feb-Dec
)
OR
(
(MONTH(current date) = 1) AND
(YEAR(ENTERDATE) = YEAR(current date) - 1 AND MONTH(ENTERDATE) = 12)
-- alternatively, in Jan only this one gets used
)
)
If your report is always going to be for the previous month, then I think the simplest idea is to declare the year and month of the previous month and then reference those in the Where clause. For example:
Declare LastMo_Month Integer = MONTH(DATEADD(MONTH,-1,getdate()));
Declare LastMo_Year Integer = YEAR(DATEADD(MONTH,-1,getdate()));
Select ...
Where MONTH(EnterDate) = #LastMo_Month
and YEAR(EnterDate) = #LastMo_Year
You could even take it a step further and allow the report to be created for any number of months ago:
Declare Delay Integer = -1;
Declare LastMo_Month Integer = MONTH(DATEADD(MONTH,#Delay,getdate()));
Declare LastMo_Year Integer = YEAR(DATEADD(MONTH,#Delay,getdate()));
Select ...
Where MONTH(EnterDate) = #LastMo_Month
and YEAR(EnterDate) = #LastMo_Year
Hope this helps.
PS - This is my first answer on StackOverflow, so sorry if the formatting isn't right!
if(month(getdate()) = 1)
begin
your jan logic
end
else
begin
your logic
end
The above answer with the Case is ok, but running a CASE on a huge result set would be pretty costly
WHERE
...
AND
DATEPART(yy,ENTERDATE) = DATEPART(yy,DATEADD(m,-1,ENTERDATE))
AND DATEPART(m,ENTERDATE) = DATEPART(m,DATEADD(m,-1,ENTERDATE))
Which Dialect of SQL are you speaking?
As opposed to doing it all with case statements, just use the built it date / time functions to subtract a month from the current date, which should handle crossing year boundaries.
TransACT
WHERE
YEAR(ENTERDATE) = year(dateadd(MONTH,-1, CURRENT_TIMESTAMP))
AND MONTH(ENTERDATE) = month(dateadd(MONTH,-1, CURRENT_TIMESTAMP))
Mysql
WHERE
YEAR(ENTERDATE) = YEAR(date_sub(curdate(),INTERVAL 1 MONTH))
AND MONTH(ENTERDATE) = MONTH(date_sub(curdate(),INTERVAL 1 MONTH) )
Try adding the previous month and year to your SELECT statement:
SELECT
...
,CASE MONTH(current date)
WHEN 1 THEN 12
ELSE MONTH(current date)-1
END AS previous_month
,CASE MONTH(current date)
WHEN 1 THEN YEAR(current date)-1
ELSE YEAR(current date)
END AS previous_year
FROM
...
WHERE
...
AND YEAR(ENTERDATE) = previous_year
AND MONTH(ENTERDATE) = previous_month
This should allow you to set the value before the WHERE comparison. This should be the most performant way to perform this procedure, as it avoids creating two entirely separate clauses or using OR.
Related
i'm trying to get in a new column the sessions who are between 08:00 and 18:00. You can see my last CASE in the CTE. For each date there should be a new column "TotalRestrictedSessions" which indicate how many session were on that particular date. If there are none, in this case i have to write 0. I suspect that my problem is when i convert the DATE?
WITH ParkeonCTE
AS
(
SELECT
OccDate = CONVERT(DATE, OC.LocalStartTime),
TotalOccSessions = COUNT(OC.SessionId),
AuthorityId,
TotalOccDuration = ISNULL(SUM(OC.DurationMinutes),0),
TotalNumberOfOverstay = SUM(CAST(OC.IsOverstay AS INT)),
TotalMinOfOverstays = ISNULL(SUM(OC.OverStayDurationMinutes),0),
(CASE
WHEN OC.OspId IS NULL THEN 'OffStreet' ELSE 'OnStreet'
END
) AS ParkingContextType,
(CASE
WHEN CAST(OC.LocalStartTime AS TIME) >= '08:00:00' AND CAST(OC.LocalStartTime AS TIME) <=
'18:00:00'
THEN COUNT(OC.SessionId)
END
) AS TotalRestrictedSessions
FROM Analytics.OccupancySessions AS OC
WHERE OC.AuthorityId IS NOT NULL
GROUP BY CONVERT(DATE,OC.LocalStartTime), OC.AuthorityId,OC.OspId
)
SELECT OC.OccDate,
OC.ParkingContextType,
OC.AuthorityId,
OC.TotalRestrictedSessions,
SUM(OC.TotalOccSessions) AS TotalOccSessions,
AVG(OC.TotalOccDuration) AS AvgOccMinutesDuration, -- wrong
SUM(OC.TotalOccDuration) AS TotalOccDuration,
SUM(OC.TotalNumberOfOverstay) AS TotalNumberOfOverstay,
SUM(OC.TotalMinOfOverstays) AS TotalMinOfOverstays,
CAST(AVG(OC.TotalMinOfOverstays) AS decimal(10,2)) AS AvgMinOfOverstays -- wrong
FROM ParkeonCTE AS OC
GROUP BY OC.OccDate, OC.AuthorityId, OC.ParkingContextType
ORDER BY OC.OccDate DESC
You just need to move your aggregation outside of your CASE expression, called conditional aggregation.
SUM(CASE
WHEN CAST(OC.LocalStartTime AS TIME) >= '08:00:00'
AND CAST(OC.LocalStartTime AS TIME) <= '18:00:00'
THEN 1
ELSE 0
END
) AS TotalRestrictedSessions
Generally, you should include the current query results and your desired results in your question to make it easier to figure out where the issues are.
I missunderstod this a lot and thanks again for all the help.
I eventually found a solution for my case and it didn't even involve anything like
IF Monday THEN SELECT
I didn't even needed a CASE-statement.
What I had to do was to put different WHERE statements with an OR in between.
I have stated my final code bolow:
SELECT * FROM
(SELECT
t.pay_date
, t.supp_name
, t.client AS row_client
, t.ip_status
, t.bank_account
, t.remitt_curr AS remitt_curr
, t.remitt_id
, t.apar_id
, t.payment_id
FROM
aipheader t
WHERE
DATEPART(DW, GETDATE()) IN (2,3,4)
AND
t.pay_date BETWEEN '2019-01-01' AND DATEADD(DAY,-4,GETDATE())
OR
DATEPART(DW, GETDATE()) IN (5,6)
AND
t.pay_date BETWEEN '2019-01-01' AND DATEADD(DAY,-2,GETDATE())) x
ORDER BY 1
The statement above will execute the OR-block that is true, in my case the one with the rigt weekday.
********* OLD QUESTIONS BELOW ***********
I want to run different SELECT statements on different days off the week.
I can actually get the following code to work:
select
case
when DATEPART(DW, GETDATE()) = 4 then
(select 'HELLO' )
ELSE
(select 'GOODBYE')
end
On a wendsday the code above returns "HELLO", every other workday it would returns "Goodbye". So far so good!
As long as the select statement only return one value it seams to work, but I realy want a full table like below.
The only differance here should be the [* FROM table] part, and it breaks it all:
select
case
when DATEPART(DW, GETDATE()) = 4 then
(select * FROM table1)
ELSE
(select * FROM table2)
end
If I can't even make the above to work I can not make different SELECTs for different workdays, so this is the hard ting to figure out.
I have tried to encapsulate things with () and add a few SELECTs in different ways but it do not work, so it is probably some SQL principal.
I get an error that I think says that this is ok if it is only one value that's returned:
SqlState 37000 Native 116 [Microsoft][ODBC SQL Server Driver][SQL
Server]Only one expression can be specified in the select list when
the subquery is not introduced with EXISTS.
THANKS FOR ALL OF THE REPLYS I NOW UNDERSTAND IT A BIT BETTER.
I was not looking to add a column, I tought I could make a complete new SELECT statement. I now see that my CASE statement only adds a column, as you point out.
So then my first ide might be a better aproce, to actually modify the WHERE-clause in the select statement.
I have now tried that but it does not work.
SELECT * FROM aipheader t
WHERE
CASE
WHEN DATEPART(DW, GETDATE()) = 4 THEN
t.pay_date = '2019-10-15'
ELSE
t.pay_date = '2019-10-17'
END
It returns the error:
SqlState 37000 Native 102 [Microsoft][ODBC SQL Server Driver][SQL
Server]Incorrect syntax near '='.
I have tried the simpler:
SELECT * FROM aipheader t
WHERE
t.pay_date BETWEEN DATEADD(DAY,-120,GETDATE()) AND DATEADD(DAY,-5,GETDATE())
The above is a simpler form, but that one returns the right values.
But the one obove that one with the CASE-clause return the error about "="-sign.
Maybee the CASE part in a WHERE statement do not work.
After your edit, the correct way to filter your date would be the following:
SELECT
*
FROM
aipheader t
WHERE
t.pay_date = CASE WHEN DATEPART(DW, GETDATE()) = 4 THEN '2019-10-15' ELSE '2019-10-17' END
However, do you really want to hard-code the 2019-10-15 and 2019-10-17 dates? Seems like these should be computed automatically as time goes.
Be careful when using DATEPART with DW, since the week number starting point can actually change depending on the server's and/or current session settings. Check this example:
DECLARE #TestDate DATE = '2020-01-01' -- Wednesday
SET DATEFIRST 1 -- 1: Monday, 7: Sunday
SELECT DATEPART(DW, #TestDate) -- Returns 3
SET DATEFIRST 7 -- 1: Sunday, 7: Saturday
SELECT DATEPART(DW, #TestDate) -- Returns 4!
So whenever checking for a particular day of the week, make sure to force the DATEFIRST session parameter to a particular value so it's consistent with your checks.
I do not fully understand what you are trying to achieve.
Of course your statement will not work, because your select statement in the case when returns multiple columns and rows. That cannot work, because your case when statement will display one column!
So what you can do is the following:
select
case
when DATEPART(DW, GETDATE()) = 4 then
'HELLO'
ELSE
'GOODBYE'
end as [someField],
*
from
[table]
This query will have all rows and columns from table as output + the someField displaying HELLO or GOODBYE.
If you want to display different data from the table on the different dates (wednesday or not wednesday), you can alter the query this way:
select
case
when DATEPART(DW, GETDATE()) = 4 then
[table].[field1]
ELSE
[table].[field2]
end as [someField],
*
from
[table]
Is this ur looking for ? Sub query should return 1 row and specify col name
select
case
when DATEPART(DW, GETDATE()) = 4 then
(select top 1 col1 from table1 )
ELSE
(select top 1 col2 from table2 )
end as result
From table
CASE WHEN can only return a single value for each THEN, and never a Result Set with multiple rows and/or columns. That is just how CASE WHEN works.
You can achieve what you need like this:
DECLARE #DAY INT = DATEPART(DW, GETDATE())
select * FROM table
WHERE (#DAY = 4 AND table.SomeCol = SomeValue)
OR (#DAY = 5 AND table.SomeCol = SomeOtherValue)
... repeat for other days
Or like this:
DECLARE #DAY INT = DATEPART(DW, GETDATE())
IF #DAY = 4
(select * FROM table WHERE table.SomeCol = SomeValue)
ELSE IF #DAY = 5
(select * FROM table WHERE table.SomeCol = SomeOtherValue)
... repeat for other days
Or like this:
DECLARE #DAY INT = DATEPART(DW, GETDATE())
select * FROM table
WHERE #DAY = 4 AND table.SomeCol = SomeValue
UNION ALL
select * FROM table
WHERE #DAY = 5 AND table.SomeCol = SomeOtherValue
UNION ALL
... repeat for other days
My preference would probably be either the 1st or 2nd form.
A query must return before-known columns. If the tables table1 and table2 have different columns, you cannot write one query that returns, say, table1's five columns one day and table2's nine columns another.
As long as the resulting columns stay the same, however, you can join optionally. Here is a small example:
select
p.project_id,
p.project_name,
coalesce(c1.name, c2.name) as team_member,
coalesce(c1.salary, c2.salary * (1.0 + c2.tax / 100.0)) as team_member_salary,
coalesce(c1.job_name, c2.job_title) as team_member_job
from project p
left join crew1 c1 on c1.project_id = p.project_id and datepart(dw, getdate()) = 4
left join crew2 c2 on c2.project_id = p.project_id and datepart(dw, getdate()) <> 4
order by p.project_id;
Not sure I really understand what you are trying to achieve here. This example assumes that you are reading from different tables based on the date and will return all of the rows from both tables:
select * from (
select 'HELLO' as col1, * from table1 where DATEPART(DW, GETDATE()) = 4
UNION ALL
select 'GOODBYE' as col1, * from table2 where DATEPART(DW, GETDATE()) <> 4
)
order by col1
I am having problem with the datepart calculation in the Where clause of a query. The query returns result without the calculation but nothing if i add the condition.
DECLARE #StatusId INT;
SELECT #StatusId = Id FROM company.Status WHERE Name = 'Signed' AND CompanyId = 1;
SELECT FORMAT(CAST(cont.CreatedDate AS DATE), 'MM/dd') AS newDate,
SUM(CASE WHEN cont.UpdatedDate IS NOT NULL THEN 1 ELSE 0 END) AS TotalSignedLeads
FROM client.testw cont
WHERE cont.CompanyId = 1
AND cont.AffiliateId = 1
AND cont.CreatedDate BETWEEN '7-01-2017' AND '7-09-2017'
AND DATEPART(dw, cont.CreatedDate) NOT IN (1, 7) //This causes problem.
AND cont.StatusId = #StatusId
GROUP BY CAST(cont.CreatedDate AS DATE)
ORDER BY newDate ;
This is the data above query gives without the datepart condition.
newDate TotalSignedLeads
07/08 7
Well since you are casting cont.CreatedDate as a date in the top of the query, I suspect it's actually a varchar... thus you need
...
and datepart(weekday, cast(cont.CreatedDate as date)) not in (1,7)
...
bad-habits-to-kick-using-shorthand-with-date-time-operations
If you aren't getting an error, then you don't have any rows which meet that condition. Perhaps your DATEFIRST setting isn't what you think it is.
Also, not sure what GROUP BY CAST(#StatusId.CreatedDate AS DATE) is meant to be...
I suspect that you want a query more like this:
SELECT FORMAT(CAST(cont.CreatedDate AS DATE), 'MM/dd') AS newDate,
COUNT(cont.UpdatedDate) AS TotalSignedLeads
FROM client.testw cont
WHERE cont.CompanyId = 1 AND
cont.AffiliateId = 1 AND
cont.CreatedDate BETWEEN '2017-07-01' AND '2017-09-01' AND
DATEPART(dw, CAST(cont.CreatedDate AS DATE)) NOT IN (1, 7) AND //This causes problem.
cont.StatusId = #StatusId
GROUP BY FORMAT(CAST(cont.CreatedDate AS DATE), 'MM/dd')
ORDER BY newDate ;
Changes:
The dates for comparison are in a standard format, so they should be interpreted correctly.
The GROUP BY uses the same structure as the SELECT for the columns.
The SUM(CASE) is replaced by the much simpler COUNT().
The value for CreatedDate is cast as a DATE. To be honest, I'm not sure that will fix any problem, because that should be happening anyway.
So I can not use a temp table to accomplish this. Basically I need to create an experation date based off the next effective date available.
So for one proc code i have 10 fee schedules with only effective dates. I tried writing this but its not working because it is giving for some records the same expiration date. I know an order by is needed but i placed it in various places and it still gives me the same issue. Any help is greatly appreciated
SELECT a.*,
( CASE
WHEN (SELECT TOP 1 b.effectivedate - 1
FROM ietl_profileprocedure b
WHERE b.effectivedate > a.effectivedate
AND a.profilesid = b.profilesid) IS NULL THEN (SELECT
Dateadd(mm, Datediff(mm, 0, Getdate()) + 1, -1))
ELSE (SELECT TOP 1 b.effectivedate - 1
FROM ietl_profileprocedure b
WHERE b.effectivedate > a.effectivedate
AND a.profilesid = b.profilesid)
END ) AS 'ExpDate'
FROM ietl_profileprocedure a
WHERE profilesid = '4197'
AND procedurecode = '90685'
Thank you Juan I looked up lead and lag and was able to come up with the code below to suit my needs! now i can have a date something was posted and find the amount that should have pertained to the ProcedureCode at that time more easily
SELECT s.ProfileSID,s.ProcedureCode,s.Amount,cast(s.EffectiveDate as date) as EffectiveDate ,
cast(isnull(dateadd(day,-1,LEAD(EffectiveDate) OVER (ORDER BY ProfileSID,ProcedureCode,EffectiveDate
)),getdate())as date) ExpDate
FROM iETL_ProfileProcedure s
WHERE ProfileSID IN ('4197')and ProcedureCode = '90685'
Is there a way to use an if statement in the where part of the SQL query? For example:
SELECT count(*)
from table_name tb
where ( if (#enddate>dateadd("d",2,#date) then date > tb.date
else dateadd("d",2,#date)>tb.date) )
I need to somehow do this check where I check if the date 2 days later is not greater than the end date, otherwise I have to use the end date by default.
You can use CASE expression:
SELECT count(*)
FROM table_name tb
WHERE (CASE WHEN (#enddate>dateadd("d",2,#date)) THEN date > tb.date
ELSE
dateadd("d",2,#date)>tb.date
END);
this answer is tailored onto to the case explained into the question as the issue to solve and not on the title.
to handle that case there is no need for a special solution and/or keyword, it is handled nicely using a regular WHERE clause:
SELECT count(*)
from table_name tb
where (#enddate > dateadd("d",2,#date) AND date > tb.date)
OR
(dateadd("d",2,#date) > tb.date))
I found the following:
CASE
WHEN (DATENAME(dw,#st) = 'Monday')
AND (
((Day(#st) = 01))
OR ((Day(#st) = 02))
OR ((Day(#st) = 03))
)
THEN DATEADD(DAY,-2,#st)
ELSE DATEADD(DAY, - 1, DATEADD(m, DATEDIFF(m, 0, GETDATE()), 0))
END
And it actually works quite nicely.
This will only run when the first working day of the month is a Monday, hence that's when I want to get the weekend's data. Otherwise, I already have it.