SQL Ignores Nested Case - sql

I'm creating a Date_Dimension and have the Problem that i have to define "lastworkingdayofmonth" with Setting a flag on it.
I can handle all days but when the calculated they is a Holiday i cant get the day before to set the flag.
Please help me :)
UPDATE DATE_DIMENSION_001
SET ISLASTWORKINGDAYMONTH =
CASE WHEN ( CONVERT(VARCHAR(8), lastdayofmonth, 112) = Datekey ) AND IsWeekday = 1 AND IsHolidayAut = 0 THEN 1
WHEN ( CONVERT(VARCHAR(8), dbo.fn_LastWorkday(FullDate), 112) ) = Datekey AND IsHolidayAut = 0 THEN 1
WHEN ( CONVERT(VARCHAR(8), dbo.fn_LastWorkday(FullDate), 112) ) = Datekey AND IsHolidayAut = 1 THEN
CASE WHEN ( DATEADD(DD, -1, dbo.fn_LastWorkday(FullDate)) ) = CONVERT(DATE, Datekey) THEN 1
END
END

I think that something like this may work for you:
UPDATE dd
SET ISLASTWORKINGDAYMONTH = 1
FROM
DATE_DIMENSION_001 dd
left join
DATE_DIMENSION_001 dd_anti
on
DATEPART(year,dd.FullDate) = DATEPART(year,dd_anti.FullDate) and
DATEPART(month,dd.FullDate) = DATEPART(month,dd_anti.FullDate) and
dd_anti.FullDate > dd.FullDate and
dd_anti.IsWeekday = 1 and
dd_anti.IsHolidayAut = 0
WHERE
dd.IsWeekday = 1 and
dd.IsHolidayAut = 0 and
dd_anti.FullDate is null
That is, we locate rows which are weekdays, not holidays, and for which (via dd_anti, the LEFT JOIN and the null check in the WHERE clause) we cannot locate another row for the same month, but a later date, and is also a weekday and not a holiday.

Related

Adding between dates - sql query

Currently I have a query that will get me the data from the pervious month .
I need to change thisso I can choose a selected date range that could allow me to input 2 dates and pull back all results between them.
See my query below:
select * from Table1 I
inner join Service K on I.Service_Key = K.Service_Key
inner join Status S on I.Status_Key = S.Status_Key
where K.Service_Key = '1'
and S.Status_Name = 'Closed'
and month(I.Date_Key) = (select case when Month (GETDATE())-1 = 0 then 12 else Month (GETDATE())-1 end)
and year(I.Date_Key) = (select case when Month (GETDATE()) -1 = 0 then year (GETDATE()) -1 ELSE YEAR (GETDATE()) end)
I need to be able to say where dates between dd/mm/yy and dd/mm/yy
You could declare the dates a variables:
Declare #Startdate as datetime
Declare #Enddate as datetime
set #Startdate = '01-AUG-20'
set #Enddate = '22-OCT-20'
select * from Table1 I
inner join Service K on I.Service_Key = K.Service_Key
inner join Status S on I.Status_Key = S.Status_Key
where K.Service_Key = '1'
and S.Status_Name = 'Closed'
and I.Date_Key > #Startdate
and I.Date_Key < #Enddate
A simple method is:
where K.Service_Key = '1' and
S.Status_Name = 'Closed' and
datediff(month, i.Date_key, getdate()) = 1
That version, however, cannot use an index on i.Date_Key if that is appropriate. A more index friendly version is:
where K.Service_Key = '1' and
S.Status_Name = 'Closed' and
i.Date_key < datefromparts(year(getdate()), month(getdate()), 1) and
i.Date_key >= dateadd(month, 1, datefromparts(year(getdate()), month(getdate()), 1))

CASE in WHERE Clause

Below is my current SQL Server 2012 query. Basically I want the information from the last business day, but on Monday, I want it to pull Friday's info instead of Sunday. This is what I have so far in my query but it won't accept it.
USE [LetterGeneration]
SELECT
g.LetterGenerationPrintJobId,
CONVERT(CHAR(12), r.CreatedDate, 101) AS CreatedDate,
YEAR(r.CreatedDate) AS Year,
MONTH(r.CreatedDate) AS Month,
DAY(r.CreatedDate) AS Day,
CASE
WHEN DATEPART(dw, r.CreatedDate) = 1
THEN 1
WHEN DATEPART(dw, r.CreatedDate) = 7
THEN 1
ElSE 0
END AS Weekend,
s.LetterGenerationStatusId AS Status,
COUNT(g.LetterGenerationId) AS LetterCount,
SUM(g.LetterPageCount) AS PageCount,
t.IsLitigationCoverLetterAllowed,
CASE
WHEN g.CarrierTrackingNumber LIKE '%1ZE%'
THEN 1
WHEN g.CarrierTrackingNumber LIKE '921489%'
THEN 2
WHEN g.CarrierTrackingNumber LIKE '917190%'
THEN 2
ELSE 3
END AS CarrierType
FROM
[LetterGenerationTemplateRequest] AS R
INNER JOIN
[LetterGenerationTemplate] AS T ON t.[LetterGenerationTemplateId] = r.LetterGenerationTemplateId
INNER JOIN
LetterGeneration G ON g.LetterGenerationTemplateRequestId = r.LetterGenerationTemplateRequestId
INNER JOIN
LetterGenerationStatus S ON g.LetterGenerationStatusId = s.LetterGenerationStatusId
WHERE
(CASE
WHEN (DATENAME(dw,GETDATE()) = 'Monday')
THEN (DATEDIFF(d, r.CreatedDate, GETDATE()) = 3)
ELSE (DATEDIFF(d, r.CreatedDate, GETDATE()) = 1)
END)
AND t.[TemplateKey] NOT LIKE '%PLTV1%'
AND s.LetterGenerationStatusId = 19
ORDER BY
r.CreatedDate DESC, g.LetterGenerationPrintJobId DESC
What am I missing or misunderstanding about my WHERE clause in order to make it work in the way I'm thinking?
Thanks
Maybe convert to a regular AND/OR?
WHERE (
((DATENAME(dw,GETDATE()) = 'Monday') AND (DATEDIFF(d, r.CreatedDate, GETDATE()) = 3))
OR
(DATEDIFF(d, r.CreatedDate, GETDATE()) = 1)
)
....
What am I missing or misunderstanding about my WHERE clause in order to make it work in the way I'm thinking?
Though you haven't given the error message you're getting, I'm sure it's syntax related because you're putting the test INSIDE the result of the case, not outside it
You're writing:
WHERE CASE WHEN it_is_monday THEN data_date = friday ELSE data_date = yesterday END
You should be writing:
WHERE data_date = CASE WHEN it_is_monday THEN friday ELSE yesterday END
Essentially: you're not supposed to use case/when in a where clause to do your "column = something" comparison and return you true or false, you're supposed to use it to just return the "something" you compare against "column" else in order to get your true or false
The other answers focus on "giving you a working solution"; this answer focuses on telling you what was going wrong with your thought processes re your original query
Here's a simpler example:
--wrong syntax to search a table full of cats (4 legs) and people (2 legs)
WHERE CASE WHEN animal_type = 'cat' THEN legs = 4 ELSE legs = 2 END
--right syntax
WHERE limbs = CASE WHEN animal_type = 'cat' THEN 4 ELSE 2 END
Ignoring holidays for a second, and assuming you have at least one record for every date, something like this should work.
where cast(createdDate as date) =
(select max(createdDate )
from table
where createdDate < cast(getDate() as date
and dateName(dw, createdDate in ('Monday' etc)
)
In order to maintain SARGability(able to do a seek against an index) you want to make sure the table columns in the predicate aren't included in any functions.
The following should work and maintain SARGability...
WHERE
r.CreatedDate = CASE
WHEN DATEPART(dw, getdate) = 2
THEN DATEADD(dd, -3, CAST(GETDATE() AS DATE))
ELSE CAST(GETDATE() AS DATE)
END
HTH,
Jason

Query executes successfully but returns no results

The following query is used for a report, the report is still showing live data when accesssed, yet when running the query in Management Studio I am not getting any results despite the message 'query successfully completed'
I have had to declare the #Date parameter which I think I have done correctly at the top of the query.
DECLARE #Date datetime;
BEGIN
SET #Date = 27/07/2017;
END
SELECT CAST(CASE WHEN (SOTD_STWH_CODE = 'HPP SHEF') THEN DATE - (CASE
DATEPART(dw, DATE) WHEN 2 THEN 3 ELSE 1 END) ELSE DATE END AS date) AS
ShipDate,
DeFactoUser.F_SO_Transaction.SOTR_CUST_CODE,
DeFactoUser.F_SO_Transaction_Details.SOTD_HEAD_NO,
DeFactoUser.F_SO_Transaction.SOTR_DLSC_CODE,
DeFactoUser.F_SO_Transaction_Details.SOTD_STWH_CODE,
DeFactoUser.F_SO_Transaction_Details.SOTD_STRC_CODE,
DeFactoUser.F_SO_Transaction_Details.SOTD_QTY_UNITS_ORDERED,
DeFactoUser.F_SO_Transaction_Details.SOTD_QTY_UNITS_OUTSTANDING,
DeFactoUser.F_SO_Transaction_Details.SOTD_QTY_UNITS_PICKED,
DeFactoUser.F_BM_Transactions_Details.BMTD_BMTR_SYS_NO,
DeFactoUser.F_BM_Transactions_Details.BMTD_QTY_OUTSTANDING,
ISNULL(CAST(BaseOn.baseon AS varchar), '') AS BaseOn,
CASE BaseOn.baseonstat WHEN '99' THEN 'Complete' WHEN
'98' THEN 'Outstanding' WHEN '1' THEN 'open' ELSE '' END AS BaseOnStatus,
DeFactoUser.F_SL_Customers.CUST_NAME
FROM DeFactoUser.F_SL_Customers INNER JOIN
DeFactoUser.F_SO_Transaction_Details WITH (NOLOCK) INNER
JOIN
DeFactoUser.F_ST_Products WITH (NOLOCK) ON
DeFactoUser.F_SO_Transaction_Details.SOTD_STRC_CODE =
DeFactoUser.F_ST_Products.STRC_CODE INNER JOIN
DeFactoUser.F_SO_Transaction WITH (NOLOCK) ON
DeFactoUser.F_SO_Transaction_Details.SOTD_HEAD_NO =
DeFactoUser.F_SO_Transaction.SOTR_SYS_NO INNER JOIN
tbl_DFBI_Date ON
DeFactoUser.F_SO_Transaction.SOTR_PROMISED_DATE = tbl_DFBI_Date.Date ON
DeFactoUser.F_SL_Customers.CUST_CODE =
DeFactoUser.F_SO_Transaction_Details.SOTD_CUST_CODE LEFT OUTER JOIN
DeFactoUser.F_BM_Transactions INNER JOIN
DeFactoUser.F_BM_Transactions_Details ON
DeFactoUser.F_BM_Transactions.BMTR_SYS_NO =
DeFactoUser.F_BM_Transactions_Details.BMTD_BMTR_SYS_NO ON
DeFactoUser.F_SO_Transaction_Details.SOTD_SYS_NO =
DeFactoUser.F_BM_Transactions_Details.BMTD_ORDER_LINK_NUMBER LEFT OUTER JOIN
(SELECT RIGHT(SOTR_BASED_ON_REF, 7) AS link,
SOTR_STATUS AS baseonstat, SOTR_SYS_NO AS baseon
FROM DeFactoUser.F_SO_Transaction AS
F_SO_Transaction_1
WHERE (SOTR_CUST_CODE = 'h075') AND
(SOTR_BASED_ON_REF > '0')) AS BaseOn ON
CAST(DeFactoUser.F_SO_Transaction_Details.SOTD_HEAD_NO AS varchar) =
BaseOn.link
WHERE (DeFactoUser.F_ST_Products.STRC_NI_CODE = 'panelcut') AND
(DeFactoUser.F_ST_Products.STRC_ANAL1 = '1033') AND
(DeFactoUser.F_SO_Transaction_Details.SOTD_SOTR_TYPE = 10) AND
(DeFactoUser.F_SO_Transaction.SOTR_CUST_CODE <> 'h075')
AND (DeFactoUser.F_SO_Transaction.SOTR_STATUS < '99') AND (CASE WHEN
(SOTD_STWH_CODE = 'HPP SHEF')
THEN DATE - (CASE DATEPART(dw, DATE) WHEN 2 THEN 3 ELSE 1
END) ELSE DATE END <= #Date)
Any suggestions as to how i can view the results?
You need the following to initialize the date:
SET #Date = '20170727'; -- Format as YYYYMMDD which is locale neutral
What you currently have is, 27 divided by 7 divided by 2017. These are integers, so the result is 0. This number is then converted to datetime, which will not result in the date you intended to have.
You best stick to the ISO 8601 formatting of dates, or date/time values. You can read more about that in the DATETIME documentation.

multi-select sql query with date range

I have this query where I get totals of different stats from an employee roster table.
SELECT A.rempid AS EmpId,
E.flname,
A.rdo_total,
B.grave_total,
C.sundays,
D.holidays
FROM (SELECT rempid,
Count(rshiftid)AS RDO_Total
FROM rtmp1
WHERE rshiftid = 2
GROUP BY rempid
HAVING Count(rshiftid) > 0) A,
(SELECT rempid,
Count(rshiftid)AS Grave_Total
FROM rtmp1
WHERE rshiftid = 6
GROUP BY rempid
HAVING Count(rshiftid) > 0)B,
(SELECT rempid,
Count(rshiftid) AS Sundays
FROM rtmp1
WHERE Datepart(dw, rdate) = 1
AND rshiftid > 2
GROUP BY rempid
HAVING Count(rshiftid) > 0)C,
(SELECT rempid,
Count(rshiftid) AS Holidays
FROM rtmp1
WHERE rdate IN (SELECT pubhdt
FROM pubhol)
AND rshiftid > 2
GROUP BY rempid
HAVING Count(rshiftid) > 0)D,
(SELECT empid,
[fname] + ' ' + [sname] AS flName
FROM remp1)E
WHERE A.rempid = B.rempid
AND A.rempid = E.empid
AND A.rempid = C.rempid
AND A.rempid = D.rempid
ORDER BY A.rempid
I would like to add a date range into it, so that I can query the database within 2 dates. The rTmp1 table has a column called rDate. I was wondering what the best way to do this. I could add it to a stored procedure and add variable to each select query. Or is there a better way to run the query within a date range.
i think just add an additional where clause item similar to:
AND ( rDate > somedate AND rDate < someotherdate )
Adding the date range to each query is the most direct solution.
Making it a stored procedure is something that can always be done with a query, but has nothing to do with this specific case.
If the number of records resulting from narrowing down your table to the specified date range is substantially less than the entire table, it might be an option to insert these records into a temporary table or a table variable and run your existing query on that table/resultset.
Though I do not have any data to test, you might consider the following query as it is more easy to read and might perform better. But you have to check the results for yourself and maybe do some adjustments.
DECLARE #startDate date = '12/01/2012'
DECLARE #endDate date = DATEADD(MONTH, 1, #startDate)
SELECT
[e].[empid],
[e].[fname] + ' ' + [e].[sname] AS [flName],
SUM(CASE WHEN [t].[rshiftid] = 2 THEN 1 ELSE 0 END) AS [RDO_Total],
SUM(CASE WHEN [t].[rshiftid] = 6 THEN 1 ELSE 0 END) AS [Grave_Total],
SUM(CASE WHEN [t].[rshiftid] > 2 AND DATEPART(dw, [t].[rdate]) = 1 THEN 1 ELSE 0 END) AS [Sundays],
SUM(CASE WHEN [t].[rshiftid] > 2 AND [h].[pubhdt] IS NOT NULL THEN 1 ELSE 0 END) AS [Holidays]
FROM [remp1] [e]
INNER JOIN [rtmp1] [t] ON [e].[empid] = [t].[rempid]
LEFT JOIN [pubhol] [h] ON [t].[rdate] = [h].[pubhdt]
WHERE [t].[rdate] BETWEEN #startDate AND #endDate
GROUP BY
[e].[empid],
[e].[fname],
[e].[sname]
ORDER BY [empid] ASC

SQL Date Order Correction

Working on a SQL statement here and I have sort of a stupid question. Ive got this date field that spits out various dates from years and months etc. I'm trying to order them correctly but i get only the month in order. Example is:
01-05-2012
12-30-2011
12-18-2011
11-25-2011
11-24-2011
Etc.
My query is as follows:
SELECT TOP (100) PERCENT CONVERT(VARCHAR(10), A.tran_end_time, 110) AS Date
FROM dbo.ttdpur040101_CT AS A INNER JOIN
dbo.ttdpur040101_Audit AS B ON NOT (A.tran_begin_time > B.event_time_local OR
A.tran_end_time < B.event_time_local) AND (A.__$operation = 2 AND B.action_id = 'IN' OR
(A.__$operation = 3 OR
A.__$operation = 4) AND B.action_id = 'UP' OR
A.__$operation = 1 AND B.action_id = 'DL') AND B.class_type = 'U'
WHERE (B.server_principal_name = #Name)
GROUP BY CONVERT(VARCHAR(10), A.tran_end_time, 110)
ORDER BY Date
I would like to have it shown as follows:
11-24-2011
11-25-2011
12-18-2011
12-25-2011
01-08-2012
01-09-2012
etc.
Thanks
You are ordering by a Date column that has been converted to a VARCHAR(). Instead order by the original date column:
ORDER BY A.tran_end_time ASC
That's because Date is a varchar.
Try it this way:
SELECT TOP (100) PERCENT CONVERT(VARCHAR(10), A.tran_end_time, 110) AS Date
FROM dbo.ttdpur040101_CT AS A INNER JOIN
dbo.ttdpur040101_Audit AS B ON NOT (A.tran_begin_time > B.event_time_local OR
A.tran_end_time < B.event_time_local) AND (A.__$operation = 2 AND B.action_id = 'IN' OR
(A.__$operation = 3 OR
A.__$operation = 4) AND B.action_id = 'UP' OR
A.__$operation = 1 AND B.action_id = 'DL') AND B.class_type = 'U'
WHERE (B.server_principal_name = #Name)
GROUP BY CONVERT(VARCHAR(10), A.tran_end_time, 110)
ORDER BY CONVERT(DATETIME, Date, 103) ASC
SELECT CONVERT(VARCHAR(10), [Date], 110) FROM (
SELECT TOP (100) PERCENT DATEADD(dd, datediff(dd, 0, A.tran_end_time), 0) AS Date
FROM dbo.ttdpur040101_CT AS A
INNER JOIN dbo.ttdpur040101_Audit AS B ON
NOT (A.tran_begin_time > B.event_time_local OR
A.tran_end_time < B.event_time_local) AND
(A.__$operation = 2 AND B.action_id = 'IN' OR
(A.__$operation = 3 OR A.__$operation = 4) AND
B.action_id = 'UP' OR
A.__$operation = 1 AND
B.action_id = 'DL'
) AND
B.class_type = 'U'
WHERE (B.server_principal_name = #Name)
GROUP BY DATEADD(dd, datediff(dd, 0, A.tran_end_time), 0)
) t
ORDER BY [Date]
Quite important is "rounding" to date part only. Faster than converting to varchar is DATEADD(dd, datediff(dd, 0, A.tran_end_time), 0) - because it's just math that SQL Server can do much faster than varchar manipulation.