Array like function for a statement - sql

I have a statement that returns a single number for a date. What I want to do is to be able to execute the statement across a range of dates and get a value for each date.
select dbo.GetItemMTDIssues(inmastx.fac, inmastx.fpartno, inmastx.frev, '6-01-2019')
as MTDiss from inmastx where fpartno='ANF-10-6313-102'
This is how the results look for a single date that I'm getting with my current statement. 6-01-2019
|MTDiss|
600
This is the expected results that I want over a range of dates like 6-01-2019 - 6-05-2019
|MTDiss|
600
450
375
700
300
Also including the function if it's helpful.
CREATE FUNCTION [dbo].[GetItemMTDIssues]
(#fac char(20), #partno char(25), #rev char(3), #currentdate datetime)
returns numeric (15,5)
as
begin
declare #returnval as numeric (15,5)
set #returnval =
isnull(
(select sum(fQty)
from intran
where ftype = 'I'
and month(fdate) = month(#currentdate)
and year(fdate) = year(#currentdate)
and fac = #fac
and fpartno = #partno
and fcpartrev = #rev)
,0.0) * -1
return #returnval
end

You would need first to create the range, and the below t-sql will do that.
declare #startDate datetime='6-01-2019'
declare #endDate datetime='6-05-2019'
;with DateRange as (
select #startDate [date]
union all
select DATEADD(day,1,[date]) [date] from DateRange where [date]<#endDate
)
select * from DateRange
we can test it and see the result to confirm that this is the range we want.
note:if you need to jump by month or number of days other that go day by day you only need to change the code in the DATEADD.
Now we would need to update your function to take the start and end of the range and let it use all the range dates , I think something like the below will help:-
CREATE FUNCTION [GetItemMTDIssuesRange]
(
#fac char(20), #partno char(25), #rev char(3), #startDateRange datetime, #EndDateRange datetime
)
RETURNS TABLE
AS
RETURN
(
with DateRange as (
select #startDateRange [date]
union all
select DATEADD(day,1,[date]) [date] from DateRange where [date]<#EndDateRange
)
--select * from DateRange
select (isnull(sum(fQty),0.0) * -1) MTDiss
from intran
inner join DateRange on year(fdate) = year(DateRange.[date]) and month(fdate) = month(DateRange.[date])
where ftype = 'I'
and fac = #fac
and fpartno = #partno
and fcpartrev = #rev
group by DateRange.[date]
)
GO
Please check it.
if you dont want to change the function this below may help too:-
declare #startDate datetime='6-01-2019'
declare #endDate datetime='6-05-2019'
;with DateRange as (
select #startDate [date]
union all
select DATEADD(day,1,[date]) [date] from DateRange where [date]<#endDate
)
select dbo.GetItemMTDIssues(inmastx.fac, inmastx.fpartno, inmastx.frev, DateRange.[date])
as MTDiss from inmastx,DateRange
where fpartno='ANF-10-6313-102'

Related

Return smalldatetime value from scalar function SELECT query

I'm looking to create a scalar function in SQL Server (2017) that leverages a calendar table I built awhile back in order to calculate and return a date a given number of business days forward in time from a given date. I have been struggling with how to pass the SMALLDATETIME return value back appropriately. To give some idea what I'm attempting:
CREATE FUNCTION dbo.AddBusDaysToDate
(
#startDate SMALLDATETIME,
#numBusDays INT,
)
RETURNS SMALLDATETIME
AS
BEGIN
DECLARE #rs SMALLDATETIME;
SELECT #rs = TOP(1) dt
FROM (
SELECT TOP(#numBusDays) dt
FROM dbo.OurCalendar
WHERE isWeekday = 1
AND isHoliday = 0
AND dt >= #startDate
ORDER BY dt ASC
) as ID
ORDER BY dt DESC
RETURN #rs
END
dt is a SMALLDATETIME data type on our calendar table.
The query itself runs as intended when values plugged in for the variables, but I was trying to repurpose a similar function that calculated the difference in business days between two points on the calendar, with a different data type. So I'm unsure if I'm pulling in a row to the #rs instead of the individual value, or how to separate/isolate that specific 'cell' from the SELECT query result. I expect I'm probably missing something very simple.
Any help or a point in the right direction would be very well appreciated.
I was able to resolve with the following:
CREATE FUNCTION dbo.AddBusDaysToDate
(
#startDate SMALLDATETIME,
#numBusDays INT,
)
RETURNS SMALLDATETIME
AS
BEGIN
DECLARE #rs SMALLDATETIME;
DECLARE #workdayModifier INT;
IF EXISTS (
SELECT dt FROM dbo.OurCalendar
WHERE dt = #startDate
AND isWeekday = 1
AND isHoliday = 0
)
SET #workdayModifier = 1
ELSE
SET #workdayModifier = 0
SELECT TOP(1) #rs = dt
FROM (
SELECT TOP(#numBusDays + #workdayModifier) dt
FROM dbo.OurCalendar
WHERE isWeekday = 1
AND isHoliday = 0
AND dt >= #startDate
ORDER BY dt ASC
) as ID
ORDER BY dt DESC
RETURN #rs
END

How to convert round number to data and time format

Two Column in table tblpress
Date Time
20160307 120949
20160307 133427
Need to be select below the format:
07-03-2016 12:09:49
07-03-2016 13:34 27
or
03-March-2016 12:09: 49 PM
03-March-2016 01:34: 27 PM
You can try below
select format(cast([Date] as date),'dd-MMMM-yyyy') as [Date],
TIMEFROMPARTS(LEFT([Time],2), SUBSTRING([Time],3,2), RIGHT([Time],2), 0,0) as [Time]
I think CAST/CONVERT will help you:
SELECT
CAST('20160307' AS date),
CAST(STUFF(STUFF('120949',3,0,':'),6,0,':') AS time)
And convert for out:
SELECT
CONVERT(varchar(20),NormalDate,105) OutDate, -- Italian style
CONVERT(varchar(20),NormalTime,108) OutTime -- hh:mi:ss
FROM
(
SELECT
CAST([Date] AS date) NormalDate,
CAST(STUFF(STUFF([Time],3,0,':'),6,0,':') AS time) NormalTime
FROM YourTable
) q
CAST and CONVERT (Transact-SQL)
And you can use FORMAT (Transact-SQL)
SELECT
FORMAT(GETDATE(),'dd-MM-yyyy'),
FORMAT(GETDATE(),'HH:mm:ss')
Best way to do it is to create a function :
create FUNCTION [dbo].[udfGetDateTimeFromInteger]
(
#intDate int,
#intTime int
)
RETURNS datetime
AS BEGIN
-- Declare the return variable here
DECLARE #DT_datetime datetime = NULL,
#str_date varchar(11),
#str_time varchar(8)
if(#intDate is not null and #intDate > 0)
begin
select #str_date = CAST( cast(#intDate as varchar(8)) AS date)
if #intTime=0
select #str_time ='000000'
else
select #str_time = right('0'+CONVERT(varchar(11),#intTime),6)
select #str_time =
SUBSTRING(#str_time,1,2)+':'+SUBSTRING(#str_time,3,2)+':'+SUBSTRING(#str_time,5,2)
select #DT_datetime = CAST(#str_date+' '+#str_time as datetime)
end
-- Return the result of the function
RETURN #DT_datetime
END
and then call it in select like :
declare #next_run_date int, #next_run_time int
select #next_run_date = 20160307
select #next_run_time = 130949
SELECT #next_run_date inputdate,
#next_run_time inputtime,
dbo.udfGetDateTimeFromInteger(#next_run_date, #next_run_time) outputdatetime
Output will be like :
inputdate inputtime outputdatetime
20160307 130949 2016-03-07 13:09:49.000
You said those are numbers, right? You can use datetimefromparts (or datetime2fromparts). ie:
select
datetimefromparts(
[date]/10000,
[date]%10000/100,
[date]%100,
[time]/10000,
[time]%10000/100,
[time]%100,0)
from tblpress;
DB Fiddle demo
Note that naming fields like that and also storing date and time like that is a bad idea.
I later noticed it was char fields:
select
cast([date] as datetime) +
cast(stuff(stuff([time],5,0,':'),3,0,':') as datetime)
from tblpress;

How to pass multiple values one by one for a Parameter in an SQL Query and union the results?

I have an SQL Statement which accepts a parameter #EndDate as DateTime. I want to be able to pass several values for #EndDate one by one and then Union the results of all the Queries. I have tried using CTE for this but it is of no use. I want to pass several Dates for #EndDate. Any help would be greatly appreciated.
My Query is:
DECLARE #EndDate as DateTime
SET #EndDate = '2018/02/25'
SELECT
CONVERT(VARCHAR, #EndDate, 101) [R_Date]
,[Name]
[Type]
FROM [dbo].[S_Table]
This is Sample query how u can loop through multiple dates. A while loop should select all rows into one table, do not need to perform union all
DECLARE #table TABLE (
day INT
,Month INT
,DATE DATE
)
DECLARE #startdate DATE = '2018/02/25'
DECLARE #enddate DATE = cast(getdate() AS DATE)
WHILE #startdate <= #enddate
BEGIN
SELECT #startdate = dateadd(dd, 1, #startdate)
INSERT #table (
day
,Month
,DATE
)
SELECT day(#startdate)
,MONTH(#startdate)
,#startdate
END
SELECT *
FROM #table;
In your logic for passing multiple values into query , you can store result set into temporal table, which holds looped records
DECLARE #table TABLE (
[R_Date] DATE
,Name VARCHAR(155)
,Value VARCHAR(155)
)
DECLARE #enddate DATE = '2018/02/25'
DECLARE #enddate1 DATE = cast(getdate() AS DATE)
WHILE #enddate <= #enddate1
BEGIN
SELECT #enddate = dateadd(dd, 1, #enddate)
INSERT #table (
R_Date
,Name
,Value
)
SELECT CONVERT(VARCHAR, #EndDate, 101) [R_Date]
,[Name] [Type]
FROM [dbo].[S_Table]
WHERE DATE = #enddate
-------More filters
END
SELECT *
FROM #table;

How to avoid not to query tables or views in scalar functions?

I have scalar functions( 4 functions) in my View. It drastically reduces the view's performance. I believe the reason for that is I use SELECT queries in my scalar functions.
EG:
CREATE FUNCTION [dbo].[udf_BJs_GENERAL]
(
#TankSystemId int,
#TimeStamp datetime2(7)
)
RETURNS varchar(10)
AS
BEGIN
DECLARE #leakChk varchar(10);
DECLARE #allowableVariance float;
DECLARE #GallonsPumped int;
DECLARE #DailyOverOrShort float;
DECLARE #TimePeriod datetime2(7);
DECLARE #ReportDate datetime2(7)
SELECT TOP 1 #TimePeriod = Date
FROM [bjs].udv_DailySiraData
where TankSystemId=#TankSystemId ORDER BY Date DESC
SET #ReportDate=#TimePeriod
IF( #TimeStamp <= #TimePeriod)
SET #ReportDate=#TimeStamp
SELECT #GallonsPumped = SUM(GallonsPumped)
FROM [bjs].[udv_DailySiraData]
where TankSystemId=#TankSystemId
and Date <=#ReportDate and Date >= DATEADD(mm, DATEDIFF(mm,0,#ReportDate), 0)
SELECT #DailyOverOrShort = SUM(DailyVar)
FROM [bjs].[udv_DailySiraData]
where TankSystemId=#TankSystemId
and Date <=#ReportDate and Date >= DATEADD(mm, DATEDIFF(mm,0,#ReportDate), 0)
SELECT #allowableVariance= (#GallonsPumped/100) + 130
SET #leakChk='FAIL'
IF (#allowableVariance > ABS(#DailyOverOrShort))
SET #leakChk = 'PASS'
RETURN #leakChk;
How can i avoid such situations? Is there a way to do select queries in my View and pass that result to my scalar function?
Try this:
create function dbo.udf_BJs_GENERAL(
#TankSystemId int,
#TimeStamp datetime2(7)
) returns varchar(10) as
with dates as (
select top 1
ReportDate = case when #TimeStamp <= Date then #TimeStamp else Date
from bjs.udv_DailySiraData
where TankSystemId=#TankSystemId
order by Date desc
),
gallons as (
select
allowableVariance = ( sum(GallonsPumped)/100) + 130,
DailyOverOrShort = sum(DailyVar)
from bjs.udv_DailySiraData data
join dates
on data.Date <= dates.ReportDate
and date.Date >= dateadd(mm, datediffmm, 0, dates.ReportDate), 0)
where TankSystemId = #TankSystemId
)
select
leakChk = cast( case when allowableVariance > ABS(DailyOverOrShort))
then 'PASS' else 'FAIL' end as varchar(10) )
from gallons
your case is special, your have a special input parameter,assue the timestamp parameter is on Day level
This view will return check result of each TankSystemId on every day.
Then join will your query with TankSystemId and Day.
But if the input parameter is more detail. I think it is difficult to convert this function to view
CREATE view [dbo].[uvw_BJs_GENERAL]
AS
BEGIN
/*
SET #ReportDate=#TimePeriod
IF( #TimeStamp <= #TimePeriod)
SET #ReportDate=#TimeStamp
*/
SELECT TankSystemId,b.[Date]
,GallonsPumped = SUM(GallonsPumped),DailyOverOrShort = SUM(DailyVar)
,leakChk=CASE WHEN (SUM(GallonsPumped)/100) + 130)> ABS(SUM(DailyVar)) THEN 'PASS' ELSE 'FAIL' END
FROM [bjs].[udv_DailySiraData] AS a
INNER JOIN (
SELECT CONVERT(DATE,[Date]) AS [Date] FROM [bjs].[udv_DailySiraData] GROUP BY TankSystemId, CONVERT(DATE,[Date])
) b ON a.TankSystemId=b.TankSystemId AND DATEDIFF(d,a.[Date],b.[Date])>=0
-- and Date <=#ReportDate and Date >= DATEADD(mm, DATEDIFF(mm,0,#ReportDate), 0)
GROUP BY TankSystemId,b.[Date]
END

Showing Data for those Dates which are not present in Database Table

I am trying show attendance record for employees in particular Branch, let Say "Pune Branch".
While Showing Weekly Records, It only shows the record of dates for which there is an entry in database table. I also want to show record for those Date on which there is no any employee present.
Suppose In Log_TB there are 2 employee record present for 28 Apr 2015 out of 2 Employees, Then It Shows
And If No records for that Date then it Returns nothing.
I want result Like
Here is my Query
Declare
#Date date = GETDATE(),
#date1 date,
#Total int
set #date1 = (CONVERT(date,DATEADD(DD,-6,#Date),108));
select #Total = Count(User_TB.User_Id)
from User_TB join Branch_TB
on User_TB.User_Branch = Branch_TB.Branch_Name
where User_TB.User_Branch = 'Pune';
select convert(varchar,Log_TB.Date,106) AS Date,
CONVERT(CHAR(3),DATENAME(weekday,Log_TB.Date)) AS Day,
#Total AS Total,COUNT(Log_TB.User_Id) Present,
(#Total-COUNT(Log_TB.User_Id)) AS Absent
From Log_TB join User_TB on Log_TB.User_Id = User_TB.User_Id
where Log_TB.Date <= (CONVERT(date,#Date,108))
and Log_TB.Date >= #date1 and User_TB.User_Branch = 'Pune'
group by Log_TB.Date;
My table definition
CREATE TABLE [dbo].[Log_TB]
(
[User_Id] [varchar](50) NOT NULL,
[First_Login] [time](0) NULL,
[Logout] [time](0) NULL,
[Date] [date] NULL,
[Working_Hrs] [time](0) NULL,
[extra_Int] [int] NOT NULL,
[extra_String] [varchar](50) NULL
)
What you need is a Numbers or a Date table. You can generate it using something like this
DECLARE #date1 DATE = '20150401',#Date DATE= GETDATE();
;WITH CTE AS
(
SELECT #date1 as datecol
UNION ALL SELECT DATEADD(day,1,datecol) FROM CTE WHERE DATEADD(day,1,datecol) <= #Date
)
SELECT * FROM CTE
In your query, you would use it like this
Declare
#Date date = GETDATE(),
#date1 date,
#Total int
set #date1 = (CONVERT(date,DATEADD(DD,-6,#Date),108));
select #Total = Count(User_TB.User_Id)
from User_TB join Branch_TB
on User_TB.User_Branch = Branch_TB.Branch_Name
where User_TB.User_Branch = 'Pune';
DECLARE #date1 DATE = '20150401',#Date DATE= GETDATE();
;WITH CTE AS
(
SELECT #date1 as datecol
UNION ALL SELECT DATEADD(day,1,datecol) FROM CTE WHERE DATEADD(day,1,datecol) <= #Date
)
select convert(varchar,CTE.datecol,106) AS Date,
CONVERT(CHAR(3),DATENAME(weekday,CTE.datecol)) AS Day,
#Total AS Total,COUNT(User_TB.User_Id) Present,
(#Total-COUNT(User_TB.User_Id)) AS Absent
From CTE LEFT JOIN Log_TB ON Log_TB.Log_TB.Date = CTE.datecol
LEFT join User_TB on Log_TB.User_Id = User_TB.User_Id
and User_TB.User_Branch = 'Pune'
group by CTE.datecol;
Declare #date datetime -- its a starting date
Set #Date = '1-Apr-2015'
Declare #table table (date datetime)
-- inserts data from starting date till getdate
while #date <> convert(datetime,convert(varchar(10),getdate(),101),101)
begin
insert into #table
select #date aa
select #date = #date+1
end
select * from #table
Just join #table and your data with left join
Select *
from #table left join (Your data) with condition date.