CASE SELECT is limiting returning rows - sql

I've a scenario when a SELECT CASE statement is limiting the number of rows returned. Whilst I'm not a professional DB, I get by with some reasonable SQL and I didn't think that a SELECT CASE would actually limit the results?, just perform the THEN part of the CASE if the WHEN qualified.
To give some context, I've a transaction table which logs whenever an item is moved between a depot and the customer on some rental activity. Each transaction is registered as a row with a reference order and date of the transaction. To get both the customer receipt transaction and back to depot transaction on a single line, I've used (what might be a crude!) FROM SELECT. However, this part seems ok.
I'm on a SQL Server 2012.
Now, depending on what dates all of this happened, I've a taken the first date and last date involved and applied a SELECT CASE to define some conditions and resulting Date Difference calculations.
DECLARE #RUNDATE AS DECIMAL(8) = '20170101'
DECLARE #DAYFIRST AS DATETIME = DATEADD(m, DATEDIFF(m, 0, CONVERT(DATE, CAST(#RUNDATE AS VARCHAR(8)), 112)),0)
DECLARE #DAYEND AS DATETIME = DATEADD(s, -1, DATEADD(m, DATEDIFF(m, 0, CONVERT(DATE, CAST(#RUNDATE AS VARCHAR(8)), 112))+1, 0))
DECLARE #DAYSINPERIOD AS DECIMAL(2) = DAY(EOMONTH(CONVERT(DATE, CAST(#RUNDATE AS VARCHAR(8)), 112)))
SELECT MTITNO AS 'Item Number',
MTBANO AS 'Serial Number',
MTRORN AS 'Rental Order',
MTRORL AS 'Rental Order Line',
MTTRQT AS 'Delivered Quantity',
CONVERT(DATE, CAST(MTTRDT AS VARCHAR(8)), 112) AS 'Customer Receipt Date', --received in M3 to the customer, although this is aligned to when qty left the yard manually by logistics
CONVERT(DATE, CAST(NEXTDATE AS VARCHAR(8)), 112) AS 'Return To Depot Date', --last date in the return chain, receiving to the yard. If still in transit this equals the termination date
CASE
WHEN CONVERT(DATE, CAST(MTTRDT AS VARCHAR(8)), 112) < #DAYFIRST AND NEXTDATE IS NULL --is started before current period and no return activity or termination
THEN #DAYSINPERIOD --simply count the days in the current month as utilised
ELSE 0
END AS 'On Rent Entire Period',
CASE
WHEN CONVERT(DATE, CAST(MTTRDT AS VARCHAR(8)), 112) < #DAYFIRST AND NEXTDATE >= #DAYFIRST AND NEXTDATE <= #DAYEND --is started before current period but has a return activity/termination date
THEN DATEDIFF("DD",#DAYFIRST,CONVERT(DATE, CAST(NEXTDATE AS VARCHAR(8)), 112))+1 --difference between start of month and the nextdate (+1 for date difference include all)
ELSE 0
END AS 'Started Previous Period, Ended This Period',
CASE
WHEN CONVERT(DATE, CAST(MTTRDT AS VARCHAR(8)), 112) >= #DAYFIRST AND NEXTDATE IS NULL --is started in the current period and no return activity or termination
THEN DATEDIFF(D,CONVERT(DATE, CAST(MTTRDT AS VARCHAR(8)), 112),#DAYEND)+1 --difference between start date and last day of the current month (in line with utilisation) (+1 for date differnce include all)
ELSE 0
END AS 'Started This Period, No End Date',
CASE
WHEN CONVERT(DATE, CAST(MTTRDT AS VARCHAR(8)), 112) >= #DAYFIRST AND NEXTDATE >= #DAYFIRST AND NEXTDATE <= #DAYEND --is started in the current period and has a return activity/termination date
THEN DATEDIFF(D,CONVERT(DATE, CAST(MTTRDT AS VARCHAR(8)), 112),CONVERT(DATE, CAST(NEXTDATE AS VARCHAR(8)), 112))+1 --different between start date and the nextdate (+1 for date differnce include all)
ELSE 0
END AS 'Started and Ended This Period', --current period rental days (should match utilisation as sum of all lines)
#DAYSINPERIOD AS 'Calendar Days in Month', --how many days in the current month
#DAYFIRST AS 'First of Month', --what is the date of the first day of the current month
#DAYEND AS 'Last of Month' --what is the date of the last day of the current month
FROM
(
SELECT MTITNO,
MTBANO,
MTRORN,
MTRORL,
MTTRQT,
MTTRDT,
(
SELECT MIN(MTTRDT)
FROM MVXJDTA.MITTRA T2
WHERE T1.MTITNO = T2.MTITNO
AND T1.MTBANO = T2.MTBANO
AND T1.MTRORN = T2.MTRORN
AND T1.MTRORL = T2.MTRORL
AND T1.MTTRQT = T2.MTTRQT
AND T2.MTTRDT > T1.MTTRDT
) AS NEXTDATE
FROM MVXJDTA.MITTRA T1
WHERE MTCONO = 880
AND MTTTYP = '50'
AND MTTRQT > 0
AND MTCAMU <> ''
AND MTRORN LIKE 'A0%'
) T
This only returns the first 2 rows in what should be an entire data set.
2 rows
However, if I comment out the 4th SELECT CASE, then I get all records returned?!
all rows
-- CASE
-- WHEN CONVERT(DATE, CAST(MTTRDT AS VARCHAR(8)), 112) >= #DAYFIRST AND NEXTDATE >= #DAYFIRST AND NEXTDATE <= #DAYEND --is started in the current period and has a return activity/termination date
-- THEN DATEDIFF(D,CONVERT(DATE, CAST(MTTRDT AS VARCHAR(8)), 112),CONVERT(DATE, CAST(NEXTDATE AS VARCHAR(8)), 112))+1 --different between start date and the nextdate (+1 for date differnce include all)
-- ELSE 0
-- END AS 'Started and Ended This Period', --current period rental days (should match utilisation as sum of all lines)
The strange thing is that the 3rd row should match the condition I've just commented out.
Can anyone help me better understand why a SELECT CASE would be a limit to the rows returned? And where my probable poor syntax might be the cause!!!

Related

SQL CASE rolling date difference

How would I pull the annual rolling date difference within a case statement? Or, if there's a better method that would work. When the NXT Anniversary Date month is after the current month, then I need the date to roll to the next year. I need to see the next anniversary dates that would be coming up in 2022.
For example:
Below is the code I was using, which works great for the current year.
Declare #prevbiz as Date set #prevbiz = DateAdd(day,Case (Datepart(Weekday,Cast(GetDate() as Date)))
When 2 then -3
Else -1
End, Cast(GetDate() as Date));
DECLARE #prev12MONTHS AS DATE set #prev12MONTHS = DATEADD(MONTH, DATEDIFF(MONTH, 0, GETDATE()) - 12, DAY(#enddate)-1)
SELECT
ANNUALREVIEWDATE
, 'NXT Anniversary Date' = CASE WHEN DATEADD(yy, DATEDIFF(yy, ANNUALREVIEWDATE, #prevbiz), ANNUALREVIEWDATE) <= #prev12MONTHS
THEN DATEADD(yy, DATEDIFF(yy, ANNUALREVIEWDATE, #prevbiz) -1, ANNUALREVIEWDATE)
ELSE DATEADD(yy, DATEDIFF(yy, ANNUALREVIEWDATE, #prevbiz), ANNUALREVIEWDATE)
END
FROM [table]
I believe you're just looking to decide whether the anniversary has already passed and should then be advanced to next year.
datefromparts(
year(getdate()) +
case when datefromparts(2000, month(getdate()), day(getdate()))
>= datefromparts(2000, month(X), day(X))
then 1 else 0 end,
month(getdate()),
day(getdate())
)
You could also just compare month and day parts individually. Year 2000 is just an arbitrary year that happens to have a leap day.
Based on my findings on your question, this maybe could help you.
with dates(annualAnniversary) as (
select DateAdd(month,6,DateAdd(year,-5,cast(GetDate() as date)))
union
select DateAdd(month,3,DateAdd(year,-4,cast(GetDate() as date)))
union
select DateAdd(month,3,DateAdd(year,-1,cast(GetDate() as date)))
union
select DateAdd(month,5,DateAdd(year,-2,cast(GetDate() as date)))
union
select DateAdd(month,2,DateAdd(year,-1,cast(GetDate() as date)))
union
select DateAdd(month,8,DateAdd(year,-3,cast(GetDate() as date)))
union
select DateAdd(month,0,DateAdd(year,0,cast(GetDate() as date)))
)
select annualAnniversary,
DateAdd(year,DATEDIFF(year,annualAnniversary, cast(GetDate() as date)),annualAnniversary) as needsToShow
from dates
for excluding rows those not meet their own annual Anniversary, You can add this to the above code:
where DATEDIFF(year,annualAnniversary, cast(GetDate() as date))>0
Output with raw data:

SQL Query needed for desired Output

Below is the sample data which gets filled into sql server database from different PLC machine. datetime,machineID, cycletime(TIME TAKEN TO PRODUCE THAT MATERIAL) AND shift
There are 3 shifts in company A(6:30Am to 2:30PM), B(2:30 to 10:30), C(10:30 to 6:30AM).
when i take C Shift count, my query should take next day data also till 6:30AM time. Where as A shift should take current day data starting from 6:30Am to 2:30Pm.Where as B shift should take current day data starting from 2:31pm to 10:30PM.
Desired Output::
I need to find the quantity for each hour...6:30 to 7:30 what is the quantity... 7:30 to 8:30 what is the quantity and so on for each individual hour. Quantity should not get added with previous hour quantity.. individual hour quantity
You can start with this, and then wrap it with
"SELECT ... just the columns you want
FROM this example
GROUP BY ...."
SELECT
-- isolate Date from Time from HourMinutes only for testing
CONVERT(VARCHAR(10), [MDate], 111) as RealDate
,CONVERT(VARCHAR, [MTime], 108) as RealTime
,SUBSTRING(CONVERT(VARCHAR, [MTime], 108),4,5) as HrMn
-- from midnight to 6:30 adjust to prior day
, Case When (CONVERT(VARCHAR, [MTime], 108) < '06:30:00')
Then CONVERT(VARCHAR(10), DATEADD(day,-1,[MDate]), 111)
Else CONVERT(VARCHAR(10), [MDate], 111)
End as RptDate
-- from after the half hour, report it with the next hour
,Case When (SUBSTRING(CONVERT(VARCHAR, [MTime], 108),1,5)) > '23:30:00'
Then ' 0:30'
When (SUBSTRING(CONVERT(VARCHAR, [MTime], 108),4,5)) > '30:00'
Then STR(DATEPART ( hour , [MTime] ) + 1, 2) + ':30'
Else STR(DATEPART ( hour , [MTime] ), 2) + ':30'
End as RptHour
,[MachinelD]
,[CYCLETIM]
,[Shift]
FROM [StackOver].[dbo].[CShift]
For your add-on question of getting only Previous or Current shift,
we need to think ahead to what the Where clause might look like--
Where (MDate = #fromDate and MTime >= #fromTime)
Or (MDate > #fromDate)
And then, before the main SELECT/FROM, create appropriate local vars --
Declare #fromDate as datetime, #fromTime as datetime
If CONVERT (time, GETDATE()) <= '06:30:00' Begin
Set #fromDate=DATEADD(day,-1,CONVERT (date, GETDATE())) --yesterday
Set #fromTime='14:30'
End
Else If CONVERT (time, GETDATE()) <= '14:30:00' Begin
Set #fromDate=DATEADD(day,-1,CONVERT (date, GETDATE())) --yesterday
Set #fromTime='22:30'
End
Else If CONVERT (time, GETDATE()) <= '22:30:00' Begin
Set #fromDate=CONVERT (date, GETDATE()) --today
Set #fromTime='06:30'
End
Else Begin -- time > 22:30
Set #fromDate=CONVERT (date, GETDATE()) --today
Set #fromTime='14:30'
End
-- for testing only, show the values
Select #fromDate, #fromTime
I leave any remaining question(s) to your own solution

Replace the date value to first of the month if it is any other day

I have a requirement for a date column.
Condition: If the date column value is any date other then the 1st of its month then the same has to be replaced to 1st of that month, say for example today its 05-01-2017 and it has to be replaced by 01-01-2017. Similarly for 15th of the month.
I have achieved the same using below query:
select 'date column',
case when datediff(DAY,-15, 'date column') != 41043 then
DATEADD(dd,-(DAY( 'date column')-1), 'date column')
end
from TABLE
This I cracked by running below query:
select datediff(DAY,-15,'date column')
from TABLE
This gives value "41043", which I used in my query.
But I have two concerns here
what is this value "41043" ?? like is it ASCII value of that date or subtraction of date from SQL beginning date ..etc..
Is there any other Simpler way to achieve my query?
Please suggest.
There is also an easier way without using CASE, you can always add 16 days (for 31 days months) then found first day of the month like this:
SELECT
DATEADD(month, DATEDIFF(month, 0, DATEADD(DAY, 16, dateColumn)), 0) AS firstDayOfMonth
FROM
yourTable;
SELECT
'date column'
,CASE
WHEN DATEPART(DAY, 'date column') < 15 THEN
CONVERT(VARCHAR(10), DATEADD(mm, DATEDIFF(mm, 0, 'date column'), 0), 105)
WHEN DATEPART(DAY, 'date column') >= 15 THEN
CONVERT(VARCHAR(10), DATEADD(mm, DATEDIFF(mm, 0, 'date column'), 14), 105)
END
FROM
[TABLE]

How to query 2 different date ranges depending on the day it is run

I am making a program that auto-runs using windows scheduler. What I'd like to do is set the program to run on the 1st and the 16th of every month. If the program run's on the 1st. I'd like to have the query run for last month... For example if today was the first of august I would want it to run 7/1/12 - 7/31/12. If I run the program on the 16th I want it to run the query for the current month to the 15th. For example if it were 8/16, I would want the program to run the query for 8/1/12 - 8/15/12. What is the best way to accomplish this? Do I go with 2 seperate programs with the query attaching it to the correct date range? One scheduled to run on the first of every month, and one on the 16th? How would I go about getting the date range and the year as it will depend on which month/year it is run... My query is:
SELECT Store_Number, Invoice_Number, Invoice_Date, Extended_Price, Warranty_Amount, Quantity_Sold, Invoice_Detail_Code
FROM Invoice_Detail_Tb
WHERE (Warranty_Amount > 0) AND (Invoice_Date BETWEEN CONVERT(DATETIME, '2012-08-01 00:00:00', 102) AND CONVERT(DATETIME, '2012-08-05 00:00:00', 102))
ORDER BY Store_Number, Invoice_Date
Try 8/1/2012 and 8/16/2012 as the date. It returns the values you want to see:
declare #date datetime = '8/16/2012', #start datetime, #end datetime
if datepart(dd, #date) = 1
begin
set #start = dateadd(mm, -1, #date)
set #end = dateadd(dd, -1, #date)
end
else
begin
set #start = dateadd(dd, -15, #date)
set #end = dateadd(dd, -1, #date)
end
select #start, #end
It would be fairly easy to adapt this so that it would dynamically calculate the correct start and end dates based on any input date -- so you could run it anytime during the month.
This should be simple, let me throw some examples for you.
I truly think this should be one scheduled task, not multiple ones.
It is easier at the end of the day to point and look at one scheduled task (one procedure)
then go digging up multiple procedures just to see what might have wen't wrong.
The task can be scheduled using the SQL Server Agent (under the jobs section). The job can point to one single stored procedure.
In the procedure you can do a simple if else if logic.
IF DAY(GetDate()) = 1
--code here
ELSE IF DAY(GETDATE()) = 16
--code here
DAY(date_expression) returns the day in a datetime column. Ironically there is a MONTH and YEAR function if you for some reason need those. The rest is simple, if you are on the first date of the month then perform the monthly query from months first date till next months first day - 1, this becomes:
SELECT DATEADD(s,-1,DATEADD(mm, DATEDIFF(m,0,GETDATE())+1,0))
Otherwise if it hits on the 16th, you can run on the first day until half of the month.
If you have your query in a view, you might use this:
where
Invoice_Date between
(
case
when datepart(dd, getdate()) = 1 then dateadd(mm, -1, getdate())
else dateadd(dd, -15, getdate())
end
)
and
(
case
when datepart(dd, getdate()) = 1 then dateadd(dd, -1, getdate())
else dateadd(dd, -1, getdate())
end
)
UPDATE: Ignoring the time
(I know it looks ugly.)
where
Invoice_Date between
(
case
when datepart(dd, dateadd(dd, datediff(dd, 0, getdate()), 0)) = 1 then dateadd(mm, -1, dateadd(dd, datediff(dd, 0, getdate()), 0))
else dateadd(dd, -15, dateadd(dd, datediff(dd, 0, getdate()), 0))
end
)
and
(
case
when datepart(dd, dateadd(dd, datediff(dd, 0, getdate()), 0)) = 1 then dateadd(dd, -1, dateadd(dd, datediff(dd, 0, getdate()), 0))
else dateadd(dd, -1, dateadd(dd, datediff(dd, 0, getdate()), 0))
end
)
This is how I usually do something like that. Your stored procedure should look something like this:
declare
#today datetime ,
#dtFrom datetime ,
#dtThru datetime
------------------------------------------------------
-- get the current date, discarding the time component
------------------------------------------------------
set #today = convert(datetime,convert(varchar,current_timestamp,112),112) -- get todays date, discarding the time component
---------------------------------------------------------------------------------------------------------------------------------------------------
-- determine the start/end dates of the query period.
--
-- if the query date (#today) is in the 1st half of the month (1st - 15th), the query range is the entire preceding month
-- if the query date (#today) is in the last half of the month (16 - 31st), the query range is the 1st of the current month up to the current date
---------------------------------------------------------------------------------------------------------------------------------------------------
if ( datepart(day) < 16 )
begin
set #dtThru = dateadd(day, - datepart(day, #today ) , #today ) -- set the end date to the last day of the previous month
set #dtFrom = dateadd(day, 1 - datepart(day, #dtThru ) , #dtThru ) -- set the start date to the first day of the previous month
end
else
begin
set #dtfrom = dateadd(day, 1 - datepart(day, #today) , #today ) -- set the start date to the first day of the current month
set #dtThru = #today
end
----------------------------------------------------------------------------------------------------------------------
-- finally, adjust the start/end times to cover the entire gamut of date/time values for the month
--
-- We don't have to modify #dtFrom at all: we know its time component is 00:00:00.000 already. However, we want
-- #dtThru to have a time component of 23:59:59.997, due to SQL Server's broken way of counting time -- any time value
-- higher than that (e.g., '23:59.59.999') is 'rounded up' to start-of-day (00:00.00.000), the next day. Brilliant!
--
----------------------------------------------------------------------------------------------------------------------
set #dtThru = dateadd(ms, -3 , dateadd(day,1,#dtThru) )
--------------------------------
-- return the data to the caller
--------------------------------
SELECT Store_Number ,
Invoice_Number ,
Invoice_Date ,
Extended_Price ,
Warranty_Amount ,
Quantity_Sold ,
Invoice_Detail_Code
FROM Invoice_Detail_Tb id
WHERE Warranty_Amount > 0
AND Invoice_Date BETWEEN #dtFrom AND #dtThru
ORDER BY Store_Number ,
Invoice_Date
If you aren't using a stored procedure, you can accomplish the same thing with a parameterized query. Compute the two DateTime values needed. Put placeholders in your select statement ('#dtFrom' and '#dtThru'). When you execute the query, pass in your two DateTime values as SqlParameter objects with names matching the placeholders.

SQL Date Function

I may be using the wrong term (hence why I can't find it on google).
"Are there any functions or common code for Accounting Months Deliminations?"
For Example, this month started on a friday but on most accounting journals the weeks are measured by the first monday of the month so instead of having the 1st of July it would be the 4th of July. Same thing with the month end (29th instead of the 31st)
Again, I'm sure someone has created this 'wheel' before, and I can't seem to find it for the life of me.
The following query assumes a table, SalesTable, has a field called Amount (the value you want to sum) and a field called SaleDate (the date on which the sale occured.) It also assumes that accounting months begin the first Monday of the month and end on the Sunday prior to the beginning of the next accounting month.
Again, I highly recommend a table-based approach to this, but if you can't modify the schema, this should do the trick in T-SQL:
SELECT
CASE WHEN s.SaleDate < DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, s.SaleDate ),s.SaleDate )), 0)
THEN DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, DATEADD(day,-7,s.SaleDate) ),DATEADD(day,-7,s.SaleDate) )), 0)
ELSE DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, s.SaleDate ),s.SaleDate )), 0)
END AccountingMonth,
SUM(s.Amount) TotalSales
FROM SalesTable s
GROUP BY
CASE WHEN s.SaleDate < DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, s.SaleDate ),s.SaleDate )), 0)
THEN DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, DATEADD(day,-7,s.SaleDate) ),DATEADD(day,-7,s.SaleDate) )), 0)
ELSE DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, s.SaleDate ),s.SaleDate )), 0)
END
Note that the AccountingMonth return field actually contains the date of the first Monday of the month. In actual practice, you probably want to wrap this entire query in another query that reformats AccountingMonth to whatever you like... "2011-07", "2011-08", etc.
Here's how it works: This bit of code is the important part:
DATEADD(WEEK, DATEDIFF(WEEK, 0, DATEADD(DAY, 6 - DATEPART(DAY, s.SaleDate ),s.SaleDate )), 0)
It takes any date and returns the first Monday of the month in which that date occurred. In your case, however, you have to do a little more work because a sale might have occurred in the window between the first of the month and the first Monday of the month. The CASE statement detects that scenario and, if it's true, subtracts a week off of the date before calculating the first Monday.
Good luck!
-Michael
I have some code that takes in a year and month and returns the fiscal start and end dates. Perhaps this will give you something to go by:
DECLARE #yr int;
DECLARE #mo int;
SELECT #yr = 2011
SELECT #mo = 7
DECLARE #FiscalMonthStartDate datetime
DECLARE #FiscalMonthEndDate datetime
DECLARE #startOfMonth datetime
DECLARE #startOfNextMonth datetime
select #startOfMonth = CAST((CAST(#yr AS VARCHAR(4)) + '-' + CAST(#mo AS VARCHAR(2)) + '-' + '01') as DATE)
select #startOfNextMonth = CAST((CAST(#yr AS VARCHAR(4)) + '-' + CAST((#mo + 1) AS VARCHAR(2)) + '-' + '01') as DATE)
SELECT #FiscalMonthStartDate =
CASE
WHEN DATEPART(DW,#startOfMonth) = 0
THEN DATEADD(DD, 1, #startOfMonth)
ELSE
DATEADD(DD, 8 - DATEPART(DW,#startOfMonth), #startOfMonth)
END
SELECT #FiscalMonthEndDate =
CASE
WHEN DATEPART(DW,#startOfNextMonth) = 0
THEN DATEADD(DD, 1, #startOfNextMonth)
ELSE
DATEADD(DD, 8 - DATEPART(DW,#startOfNextMonth), #startOfNextMonth)
END
-- subtract one day to get end of fiscal month (not start of next fiscal month)
SELECT #FiscalMonthEndDate = DATEADD(DD, -1, #FiscalMonthEndDate)
SELECT #FiscalMonthStartDate, #FiscalMonthEndDate