SQL Server query buildup - sql

I have a set with data in a SQL Server database and I need to calculate uptime of a machine. I'm using two variables to determine uptime or downtime. These two variables are machine_ON and failure(s). machine_ON is only one variable in the database, failure can be 64 different failures, but all indicates as fx_x.
The status information of these variables is stored in the database as follow:
timestamp failurebitNr timestampOutOfAlarm
2012-01-17 10:38:58.000 f1_14 2012-01-17 10:39:05.000
Meaning: failure f1_14 was active from 2012-01-17 10:38:58.000 until 2012-01-17 10:39:05.000
Also the machine_ON state is saved in the same table on the same way, only the failurebitNr has a different value [t2_13].
So to determine the uptime, I need to get the timediff between timestamp and timestampOutOfAlarm where failurebutNr = 't2_13' minus any failure time.
So for example I have those rows in my database:
This should give the following graphical representation:
Green is uptime, red is downtime.
I'm used to work with PHP and than use a while loop and file some array's and do other scripting. But now I need to do this all in a SQL Server database in query ways...
So, how can I calculate the uptime (green) and downtime (red)?
UPDATE
I've tried to get the time in seconds that the machine is ON. I used this query:
<!-- language: lang-sql -->
DECLARE #startDate datetime
DECLARE #endDate datetime
DECLARE #projectNr int
DECLARE #MachineNr nvarchar(10)
SET #startDate = '2012-01-01 00:00:00.000'
SET #endDate = '2012-02-01 00:00:00.000'
SET #projectNr = '1234567'
SET #MachineNr = '2'
SELECT
DATEDIFF("SECOND",
CASE WHEN timestamp < #startDate
THEN #startDate
ELSE timestamp
END,
CASE WHEN timestampOutOfAlarm > #endDate OR timestampOutOfAlarm IS NULL
THEN #endDate
ELSE timestampOutOfAlarm
END) AS Uptime
FROM
[table]
WHERE
timestamp < #endDate
AND (timestampOutOfAlarm > #startDate OR timestampOutOfAlarm IS NULL)
AND fileProjectNr = #projectNr
AND fileProjectMachineNr = #MachineNr
AND (failureBitNr = 't2_13' AND failureBitValue = '1')

Problem solved:
I did it in 2 stored procedures, or Query's. One for getting the "ON" time and one for getting all the "DOWN" time within the "ON" time. Whith those two times I can calculate the uptime, downtime, maintenance time.
Query for ON time:
<!-- language: lang-sql -->
SELECT fctAlarmId,
fileProjectNr,
fileProjectMachineNr,
failureBitNr,
timestamp,
CASE WHEN timestampOutOfAlarm > #endDate OR timestampOutOfAlarm IS NULL
THEN #endDate
ELSE timestampOutOfAlarm
END
AS timestampOutOfAlarm
INTO #tempTable1
FROM fctAlarmHistory
WHERE (timestampOutOfAlarm >= #startDate OR timestampOutOfAlarm = NULL)
AND timestamp < #endDate
AND failureBitValue = 1
AND failureBitNr = 't2_13'
AND fileProjectNr = #projectNr
And fileProjectMachineNr = #ProjectMachineNr
-- SUM the result of all ON times into OnTime in seconds
SELECT
SUM(DATEDIFF("SECOND",
CASE WHEN timestamp < #startDate
THEN #startDate
ELSE timestamp
END,
CASE WHEN timestampOutOfAlarm > #endDate
THEN #endDate
ELSE timestampOutOfAlarm
END)) AS OnTime
FROM
#tempTable1
Query for Downtime during ON time:
<!-- language: lang-sql -->
SELECT fctAlarmId,
fileProjectNr,
fileProjectMachineNr,
failureBitNr,
timestamp,
CASE WHEN timestampOutOfAlarm > #endDate OR timestampOutOfAlarm IS NULL
THEN #endDate
ELSE timestampOutOfAlarm
END
AS timestampOutOfAlarm
INTO #tempTable1
FROM fctAlarmHistory
WHERE (timestampOutOfAlarm >= #startDate OR timestampOutOfAlarm = NULL)
AND timestamp < #endDate
AND failureBitValue = 1
AND failureBitNr = 't2_13'
AND fileProjectNr = #projectNr
And fileProjectMachineNr = #ProjectMachineNr
SELECT fctAlarmId,
fileProjectNr,
fileProjectMachineNr,
failureBitNr,
timestamp,
CASE WHEN timestampOutOfAlarm > #endDate OR timestampOutOfAlarm IS NULL
THEN #endDate
ELSE timestampOutOfAlarm
END
AS timestampOutOfAlarm
INTO #tempTable2
FROM fctAlarmHistory
WHERE (timestamp BETWEEN #startDate AND #endDate)
AND failureBitValue = 1
AND (failureBitNr LIKE'f%')
AND fileProjectNr = #projectNr
And fileProjectMachineNr = #ProjectMachineNr
CREATE TABLE #tempTable3
(
ID int IDENTITY(1,1),
timestamp datetime,
timestampOutOfAlarm datetime
)
DECLARE failure_Cursor CURSOR FOR
SELECT timestamp, timestampOutOfAlarm
FROM #tempTable2
ORDER BY timestamp ASC
OPEN failure_Cursor
-- Perform the first fetch.
FETCH NEXT FROM failure_Cursor
INTO #rij_timestamp, #rij_timestampOutOfAlarm
INSERT INTO #tempTable3 (timestamp, timestampOutOfAlarm) VALUES(#rij_timestamp,#rij_timestampOutOfAlarm)
-- Check ##FETCH_STATUS to see if there are any more rows to fetch.
WHILE ##FETCH_STATUS = 0
BEGIN
PRINT #rij_timestamp
IF #rij_timestamp <= (SELECT TOP 1 timestampOutOfAlarm FROM #tempTable3 ORDER BY timestamp DESC)
BEGIN
IF #rij_timestampOutOfAlarm > (SELECT TOP 1 timestampOutOfAlarm FROM #tempTable3 ORDER BY timestamp DESC)
BEGIN
UPDATE #tempTable3 SET timestampOutOfAlarm = #rij_timestampOutOfAlarm WHERE ID = (SELECT TOP 1 ID FROM #tempTable3 ORDER BY timestamp DESC)
END
END
ELSE
INSERT INTO #tempTable3 (timestamp, timestampOutOfAlarm) VALUES(#rij_timestamp,#rij_timestampOutOfAlarm)
FETCH NEXT FROM failure_Cursor
INTO #rij_timestamp, #rij_timestampOutOfAlarm
END
CLOSE failure_Cursor
DEALLOCATE failure_Cursor
-- Select the failure time.
SELECT
SUM(DATEDIFF("SECOND",
CASE WHEN tt3.timestamp < #startDate
THEN #startDate
ELSE tt3.timestamp
END,
CASE WHEN tt3.timestampOutOfAlarm > tt1.timestampOutOfAlarm
THEN tt1.timestampOutOfAlarm
ELSE tt3.timestampOutOfAlarm
END)) AS DownTime
FROM
#tempTable3 tt3
INNER JOIN
#tempTable1 tt1
ON
tt3.timestamp BETWEEN tt1.timestamp AND tt1.timestampOutOfAlarm

Related

sql how many days one period contains in other given period

I need to calculate how many days one set period contains in another set period. I have table with
create table #test
(
,project nvarchar(10),
startProjectDate date,
endProjectDate date)
insert into #test values
('EE43213','2021-12-31','2022-01-06') ,
('EE0211213','2022-01-09','2022-03-14'),
('EE53134','2022-02-18','2022-02-22')
I have parameters with dates (user input in the future)
DECLARE #startDate DATE = N'2021-12-16'
DECLARE #endDate DATE = N'2022-03-02'
For every project I need to calculate, how many days of their running time will be set on user chosen period and then * this count on some koeff.
I have case when in mind, if the whole project was in parameter-set period, I just find datediff between two project dates and * it.
case when (startProjectDate BETWEEN #startDate and #endDate)
and (endProjectDate BETWEEN #startDate and #endDate)
then DATEDIFF(day, startProjectDate , endProjectDate) + 1 * coeff else ...
But how to find an amount of days if they only partially set on this period?
Seems you just need a DATEDIFF and some CASE expressions here:
DECLARE #StartDate DATE = N'20211216',
#EndDate DATE = N'20220302';
SELECT project,
DATEDIFF(DAY, CASE WHEN startProjectDate < #StartDate THEN #StartDate ELSE startProjectDate END, CASE WHEN endProjectDate > #EndDate THEN #EndDate ELSE endProjectDate END) AS DaysInRange,
DATEDIFF(DAY,startProjectDate, endProjectDate) AS DaysInProject
FROM #test
WHERE startProjectDate <= #EndDate
AND endProjectDate >= #StartDate;

How to run Query in loops and counts the number of rows of each loop

I have a query that collects data for me, at the end of it I'm filtering on two dates and I count the number of rows.
FROM TAB
WHERE
(tab.transfer_date < '2019-03-11' AND Real_Updated_date >= '2019-03-11')
ORDER BY transfer_date
Is there a possibility to increase both dates by '1' till '2019-03-20'
and count and print how many rows I had in each day?
Thanks!
Full Query:
WITH TAB AS (
SELECT
[vortex_hvc].[vortex_dbo].material_history.updated_datetime
,[vortex_hvc].[vortex_dbo].material_history.transfer_date
,cast(
case
when [vortex_hvc].[vortex_dbo].material_history.transfer_date = [vortex_hvc].[vortex_dbo].material_history.updated_datetime then getdate()
else [vortex_hvc].[vortex_dbo].material_history.updated_datetime end as datetime
) as Real_Updated_date
FROM [vortex_hvc].[vortex_dbo].[vw_public_material_location]
join [vortex_hvc].[vortex_dbo].[vw_public_material_unit]
on vw_public_material_location.material_name = vw_public_material_unit.unit_number
JOIN [vortex_hvc].[vortex_dbo].[material_history]
ON [vortex_hvc].[vortex_dbo].vw_public_material_location.material_id = [vortex_hvc].[vortex_dbo].material_history.material_id
where
DateDiff(d,[vortex_hvc].[vortex_dbo].material_history.transfer_date, getdate()) < 30
AND
[vortex_hvc].[vortex_dbo].vw_public_material_location.quantity = 1
and
[vortex_hvc].[vortex_dbo].material_history.location_id in ('3492','3500','3981','3493','3504','3497','4140',
'3498', '3496','3627','4378','3512','4376','4542','4379','3802','4517','4410','4182','4758','3499','4897','4239','4820',
'4133','4377','4342','5042','5113','5358','5100','5550','5548','5549','5359',
'5594','5601','5614','5696','5701')
)
select tab.*
FROM TAB
where
(tab.transfer_date < '2019-03-11' ANd Real_Updated_date >= '2019-03-11')
order by transfer_date
You could do something like this:
DECLARE #dateFilter datetime, #enddate datetime
DECLARE #mycounts AS TABLE (mydate datetime, mycount int)
DECLARE #mydata AS TABLE (updated_datetime datetime, transfer_date datetime, real_updated_date datetime)
INSERT INTO #mydata
SELECT
[vortex_hvc].[vortex_dbo].material_history.updated_datetime
,[vortex_hvc].[vortex_dbo].material_history.transfer_date
,cast(
case
when [vortex_hvc].[vortex_dbo].material_history.transfer_date = [vortex_hvc].[vortex_dbo].material_history.updated_datetime then getdate()
else [vortex_hvc].[vortex_dbo].material_history.updated_datetime end as datetime
) as Real_Updated_date
FROM [vortex_hvc].[vortex_dbo].[vw_public_material_location]
join [vortex_hvc].[vortex_dbo].[vw_public_material_unit]
on vw_public_material_location.material_name = vw_public_material_unit.unit_number
JOIN [vortex_hvc].[vortex_dbo].[material_history]
ON [vortex_hvc].[vortex_dbo].vw_public_material_location.material_id = [vortex_hvc].[vortex_dbo].material_history.material_id
where
DateDiff(d,[vortex_hvc].[vortex_dbo].material_history.transfer_date, getdate()) < 30
AND
[vortex_hvc].[vortex_dbo].vw_public_material_location.quantity = 1
and
[vortex_hvc].[vortex_dbo].material_history.location_id in ('3492','3500','3981','3493','3504','3497','4140',
'3498', '3496','3627','4378','3512','4376','4542','4379','3802','4517','4410','4182','4758','3499','4897','4239','4820',
'4133','4377','4342','5042','5113','5358','5100','5550','5548','5549','5359',
'5594','5601','5614','5696','5701')
)
SET #dateFilter = '2019-03-11' --this is the first date used as filter
SET #enddate='2019-03-20' --this is the last one
WHILE #dateFilter <= #enddate
BEGIN
INSERT INTO #mycounts
SELECT #dateFilter, count(*) as mycount FROM #mydata tab
WHERE
(tab.transfer_date < #dateFilter AND Real_Updated_date >= #dateFilter)
SET #dateFilter = DATEADD(day,1,#dateFilter)
END
SELECT * FROM #mycounts
By using DATEADD you are going to be able to filter different months/years as well.

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

Counting records with a specific date value

I want to get all records entered with specific day, for example today:
ALTER PROCEDURE [dbo].[SP_GET_QUEUESINFO_BY_DATE]
#date nvarchar = '2012-09-21'
AS
BEGIN
declare #dateStart nvarchar(50) = #date + ' 00:00:00.0'
declare #dateEnd nvarchar(50) = #date + ' 23:59:59.437';
declare #returnData table (allQueue int,inQueue int,outQueue int)
SELECT 'table1' as table_name, COUNT(*)
FROM Queue as Counts
UNION ALL
SELECT 'table2' as table_name,COUNT(*) FROM Queue as Counts
WHERE QueueDate BETWEEN #dateStart AND #dateEnd
AND QueueNumIn != 0
END
Edited :
I Edited my code now it works :
ALTER PROCEDURE [dbo].[SP_GET_QUEUESINFO_BY_DATE]
AS
BEGIN
declare #date2 datetime
set #date2= '2012-09-21'
SELECT 'AllQueue' as table_name, COUNT(*)
FROM Queue as sdfds
UNION ALL
SELECT 'InQueue' as table_name,COUNT(*)
FROM Queue as sdfds
WHERE QueueDate >=#date2
AND QueueNumIn != 0
UNION ALL
SELECT 'OutQueue' as table_name, COUNT(*) FROM Queue as sdfds
WHERE QueueDate >=#date2
AND QueueNumOut != 0
END
It returns three records:
One problem is that the second column has no name. Why? Also, I want to return just one record that has three rows, not 3 separate records that have 2 fields.
you need to cast your varchar to datetime. and i think you want to assign Counts alias to Count(*)
ALTER PROCEDURE [dbo].[SP_GET_QUEUESINFO_BY_DATE]
#date nvarchar = '2012-09-21'
AS
BEGIN
declare #dateStart DATETIME = CAST(#date AS DATETIME)
declare #dateEnd DATETIME = DATEADD(hh,24,CAST(#date AS DATETIME))
declare #returnData table (allQueue int,inQueue int,outQueue int)
select 'table1' as table_name,COUNT(*) as Counts from QUEUE AS tb1
union all
select 'table2' as table_name,COUNT(*) as Counts from QUEUE AS tb2 where QueueDate >= #dateStart and QueueDate < #dateEnd and QueueNumIn !=0
END
not sure why returning all if you only want those in the spetial date.
Also, is better not to use between in Dates
ALTER PROCEDURE [dbo].[SP_GET_QUEUESINFO_BY_DATE]
#date datetime = '2012-09-21'
AS
BEGIN
select count(*) as 'AllQueue' ,
sum(case when QueueDate >=#date and QueueNumIn != 0 THEN 1 else 0 end) as 'InQueue',
sum(case when QueueDate >=#date and QueueNumOut != 0 THEN 1 else 0 end) as 'OutQueue'
from Queue
END
this should work.
this will give you something like
Allqueue inqueue, outqueue
----------------------------------------------------
11 | 8 | 10
This Code Worked and is edited code from ElVieejo
ALTER PROCEDURE [dbo].[SP_GET_QUEUESINFO_BY_DATE]
AS
BEGIN
declare #date2 datetime
set #date2= '2012-09-21'
select COUNT(QueueID) ,
sum(case when QueueNumIn != 0 THEN 1 else 0 end) as 'InQueue',
sum(case when QueueNumOut != 0 THEN 1 else 0 end) as 'OutQueue'
from Queue where QueueDate >= #date2
END

Compare dates regardless of time(MSSQL)

I Used this Store procedure to return data from special date . my QueueDate type is datetime , but when i want use = in Where Clause it return 0 , i want all field that are in one day independence in time of field.
ALTER PROCEDURE [dbo].[SP_GET_QUEUESINFO_BY_DATE]
AS
BEGIN
declare #date2 datetime
set #date2= '2012-09-21'
select COUNT(QueueID) ,
sum(case when QueueNumIn != 0 THEN 1 else 0 end) as 'InQueue',
sum(case when QueueNumOut != 0 THEN 1 else 0 end) as 'OutQueue'
from Queue where QueueDate >= #date2 -- QueueDate = #date2
END
You can CAST the column name to change the datatype from DATETIME to DATE (prior to version 2008+). Try,
ALTER PROCEDURE [dbo].[SP_GET_QUEUESINFO_BY_DATE]
AS
BEGIN
declare #date2 datetime
set #date2= '2012-09-21'
select COUNT(QueueID) ,
sum(case when QueueNumIn != 0 THEN 1 else 0 end) as 'InQueue',
sum(case when QueueNumOut != 0 THEN 1 else 0 end) as 'OutQueue'
from Queue
where CAST(QueueDate as DATE) >= #date2 -- QueueDate = #date2
END
SQL Server, correct?
To compare just the date component of a DateTime value and your are using SQL Server 2008 or higher, you can, as noted cast/convert to a Date datatype. Something like this should do you:
select *
from foo
where convert(date,foo.someDateTimeColumn) = '2012-10-27'
For any version of SQL Server the following will work:
select *
from foo
where convert(datetime,convert(varchar,foo.someDateTimeColumn,112),112) = '2012-10-27'
The above, using the 112 style, converts the datetime value to a compact form ISO 8601 date string 'yyyymmdd' and then converts that back to a datetime value.
Alternatively, for any version of SQL Server, you can test against a time period:
declare
#dtNow datetime
set #dtNow = current_timestamp
declare
#TodayStartOfDay datetime ,
#TodayEndOfDay datetime
set #TodayStartOfDay = convert(datetime,convert(varchar,#dtNow,112),112)
set #TodayEndOfDay = dateadd(ms,-3,datetime(day,1,#dtFrom))
...
select *
from foo
where foo.someDateTimeColumn between #TodayStartOfDay and #TodayEndOfDay
The advantage of this approach is that any indices on your DateTime column are eligible for use by the query optimizer.
WHERE DATEDIFF(DAY,#date2, QueueDate)=0