Using SQL Cursor, To find the date > 45 days difference from initial date, then again from 2nd date found, again > 45 and find next date - sql

Declare #quoteReference NVARCHAR(20)
Declare #policyNo NVARCHAR(20)
Declare #todaysDate DATE
Declare #time int
Declare #ddiff int
Declare #cnt varchar(10)
Declare EmpCursor Cursor for
select quote_reference, Miscellaneous_TodaysDate, diff from #temp1
OPEN EmpCursor
FETCH NEXT FROM EmpCursor INTO #quoteReference ,#todaysDate,#ddiff WHILE(##FETCH_STATUS = 0)
BEGIN
select #quoteReference = quote_reference
--#policyno = policy_policyNo,
-- #time = Miscellaneous_CurrentTime
from #temp1 where
Miscellaneous_TodaysDate = #todaysDate and diff > 45
if
#ddiff < 45
update #temp1
set counts = 'N'
where Miscellaneous_TodaysDate = #todaysDate
else
update #temp1
set counts = 'Y'
where Miscellaneous_TodaysDate = #todaysDate
FETCH NEXT FROM EmpCursor INTO #quoteReference, #policyNo, #todaysDate
END
CLOSE EmpCursor
DEALLOCATE EmpCursor
The first time quote_reference entered in the database is 31/05/2021.
I need another date where the same record came again in the database after more than 45 days (so in this case its 64 days i.e 03/08/2021) then 03/08/2021 date becomes my 2nd initial date and from that date same rules and conditions, i.e. to find date where the same record
entered again after more than 45 days difference.
DATA:

have you tried using the LAG function. link here - https://learn.microsoft.com/en-us/sql/t-sql/functions/lag-transact-sql?view=sql-server-ver16
I dont remember when was the last time I used cursor, you can very much achieve what you want in a SQL statement

Related

Better alternative for Cursor using While loop to iterate Sql

Good Day,
I am trying to fetch dates from one of my table, containing records in millions, in database and save them in a variable using Cursor. After fetching dates i am inserting records in db at that particular date. For this I am using While Loop. It turns out that While loop is really slowing down the performance, it is taking about hours to complete execution. I am including a part of a query for further clearance.
declare #tranDateCursor cursor;
declare #today as date;
begin
set #tranDateCursor = cursor
for
select distinct transferDate
from transactions
where [type] = 'customer'
open #tranDateCursor
fetch next from #tranDateCursor
into #today
while ##fetch_status = 0
begin
declare #yesterday as date
set #yesterday = (
select top(1) transferDate
from (
select distinct(transferDate) AS transferDate
from transactions
where transferDate < #today
and [type] = 'customer'
) as ODC_DATES
order by ODC_DATES.transferDate desc
)
insert into transactions([type],transferDate)
select 'customer'
,#today
from transactions xt
right outer join x_itransaction as y
on y.customer_account = xt.customer_account
AND y.transferDate = #yesterday
AND xt.transferDate = #today
where xt.transactionId is null
and y.transferDate = #yesterday
and y.[type] ='customer'
end
end
I have tried using Table Variables instead of CURSOR with WHILE loop, but it turns out that it was too taking very much time to run. My concern is that, Is there a better alternative for a while loop in this particular scenario?

Need to incorporate if else statement within my code

I am trying to achieve if else statement or case statement within my code below. I want to use one of these statement (if or case) to see if my RT_Ch_Pres_PX1 values are within certain min or max specs if they are above or below I want to say that if my RT_Ch_Pres_PX1 is below my min value by this much then display that value and indicate by how much is it of by and same this for exceeding max value. for example if my RT_Ch_Pres_PX1 value is 5 and I want to use my min valuse at 6 and max at 10. So my Rt_Ch_pres_px1 value is off by 1 so I would like to display this and say this value is of by 1 value. if RT_Ch_Pres_PX1 is within min and max values do nothing. Please see code below.
DECLARE #Result TABLE
(
RT_DateTime datetime,
RT_Phase_Name varchar(30),
RT_PhaseChangeCount int,
RT_Phase_Type int,
RT_Ch_Pres_PX1 float
);
/* Variables used to track changes to Phase Name */
DECLARE #RT_DateTime datetime;
DECLARE #RT_Phase_Name varchar(30);
DECLARE #RT_PhaseChangeCount int;
DECLARE #RT_Phase_Type int;
DECLARE #RT_Ch_Pres_PX1 float;
DECLARE #PhaseNameHold varchar(30);
DECLARE #PhaseChangeCount int;
SELECT #PhaseNameHold = ' ';
SELECT #PhaseChangeCount = 0;
SELECT #RT_PhaseChangeCount = 0;
/* Declare a cursor for determining when Phases change */
DECLARE ImportCursor CURSOR FAST_FORWARD FOR
SELECT
CONVERT(datetime, dbo.CycleData.Date_Time) as TimeConvert,
[dbo].[LookupPhases].[Phase_Name],
[dbo].[cycledata].[phase_type],
[dbo].[cycledata].[Ch_Pres_PX1]
FROM
CycleData INNER JOIN
CycleDataHeader ON CycleData.Unit_Number = CycleDataHeader.Unit_Number AND CycleData.Cycle_Counter_No = CycleDataHeader.Cycle_Counter_No INNER JOIN
LookupPhases ON CycleData.Phase_Type = LookupPhases.Phase_Type INNER JOIN
LookupEvent ON CycleData.Event_Type = LookupEvent.Event_Id LEFT OUTER JOIN
LookupAlarm ON CycleData.Alarm_Type = LookupAlarm.Alarm_Id
WHERE
[dbo].[CycleDataHeader].[Entered_Load_No1] = 'T14-0008'
ORDER BY
/* Appears to be the order that needs to be reported on */
Cycle_Time
-- dbo.CycleData.Unit_Number,
-- TimeConvert;
OPEN ImportCursor;
FETCH NEXT FROM ImportCursor INTO #RT_DateTime,
#RT_Phase_Name,
#RT_Phase_Type,
#RT_Ch_Pres_PX1
WHILE ##FETCH_STATUS = 0
BEGIN
IF (#RT_Phase_Name <> #PhaseNameHold)
BEGIN
SET #PhaseNameHold = #RT_Phase_Name;
SET #RT_PhaseChangeCount = #RT_PhaseChangeCount + 1;
END
INSERT INTO #Result VALUES(#RT_DateTime, #RT_Phase_Name,#RT_PhaseChangeCount,#RT_Phase_Type,#RT_Ch_Pres_PX1);
FETCH NEXT FROM ImportCursor INTO #RT_DateTime, #RT_Phase_Name,#RT_Phase_Type,#RT_Ch_Pres_PX1;
END
CLOSE ImportCursor;
DEALLOCATE ImportCursor;
SELECT
RT_DateTime,
RT_Phase_Name,
RT_PhaseChangeCount,
RT_Phase_Type,
RT_Ch_Pres_PX1
FROM #Result;
This case will generate the value you want:
case
when RT_Ch_Pres_PX1 < some_min then RT_Ch_Pres_PX1 - some_min
when RT_Ch_Pres_PX1 > some_max then RT_Ch_Pres_PX1 - some_max
else 0
end
The value created for the undershoot is negative (a good idea I think). If you want it to be positive, flip the calculation.

SQL Query List of months between dates

I've been searching without success for a way to list the months where my tables entries are in use.
Let's say we have a table with items in use between two dates :
ID StartDate EndDate as ItemsInUse
A 01.01.2013 31.03.2013
B 01.02.2013 30.04.2013
C 01.05.2013 31.05.2013
I need a way to query that table and return something like :
ID Month
A 01
A 02
A 03
B 02
B 03
B 04
C 05
I'm really stuck with this. Does anyone have any clues on doing this ?
PS : European dates formats ;-)
Create a calendar table then
SELECT DISTINCT i.Id, c.Month
FROM Calendar c
JOIN ItemsInUse i
ON c.ShortDate BETWEEN i.StartDate AND i.EndDate
Here should be the answer:
select ID,
ROUND(MONTHS_BETWEEN('31.03.2013','01.01.2013')) "MONTHS"
from TABLE_NAME;
I would use MONTH() http://dev.mysql.com/doc/refman/5.6/en/date-and-time-functions.html#function_month
SELECT ID, MONTH(StartDate)
If your dates are padded in the way you suggest - i.e. where one digit days or months have an initial 0 - you can just use the SUBSTRING function as in:
SELECT SUBSTRING(StartDate, 3, 2) ...
Alternatively, if you do not have the padding you can use:
SELECT SUBSTRING_INDEX( SUBSTRING_INDEX(StartDate, '.',2), '.', -1) ...
(This latter command is in two parts. The inner SUBSTRING_INDEX generates a string containing your day and month, and the outer SUBSTRING_INDEX takes the month.)
Hmm... Dare say this can be improved on, but as a rough 'n' ready (assuming ms-sql)
create table #results (ID varchar(10), Month int, Year int);
declare #ID varchar(10);
declare #StartDate datetime;
declare #EndDate datetime;
declare myCursor cursor for select [ID], [StartDate],[EndDate] from ItemsInUse;
open myCursor;
delete from #results;
fetch next from myCursor into #ID, #StartDate, #EndDate;
declare #tempDate datetime;
while ##FETCH_STATUS = 0
begin
set #tempdate = CAST( CAST(year(#StartDate) AS varchar) + '-' + CAST(MONTH(#StartDate) AS varchar) + '-' + CAST(1 AS varchar) AS DATETIME);
while #tempDate < #EndDate
begin
insert into #results values (#ID, MONTH(#tempDate), YEAR(#tempDate));
set #tempDate = DATEADD(month,1,#tempdate);
end
fetch next from myCursor into #ID, #StartDate, #EndDate;
end
close myCursor;
deallocate myCursor;
select * from #results;
drop table #results;

SQL Server FOR EACH Loop

I have the following SQL query:
DECLARE #MyVar datetime = '1/1/2010'
SELECT #MyVar
This naturally returns '1/1/2010'.
What I want to do is have a list of dates, say:
1/1/2010
2/1/2010
3/1/2010
4/1/2010
5/1/2010
Then i want to FOR EACH through the numbers and run the SQL Query.
Something like (pseudocode):
List = 1/1/2010,2/1/2010,3/1/2010,4/1/2010,5/1/2010
For each x in List
do
DECLARE #MyVar datetime = x
SELECT #MyVar
So this would return:-
1/1/2010
2/1/2010
3/1/2010
4/1/2010
5/1/2010
I want this to return the data as one resultset, not multiple resultsets, so I may need to use some kind of union at the end of the query, so each iteration of the loop unions onto the next.
edit
I have a large query that accepts a 'to date' parameter, I need to run it 24 times, each time with a specific to date which I need to be able to supply (these dates are going to be dynamic) I want to avoid repeating my query 24 times with union alls joining them as if I need to come back and add additional columns it would be very time consuming.
SQL is primarily a set-orientated language - it's generally a bad idea to use a loop in it.
In this case, a similar result could be achieved using a recursive CTE:
with cte as
(select 1 i union all
select i+1 i from cte where i < 5)
select dateadd(d, i-1, '2010-01-01') from cte
Here is an option with a table variable:
DECLARE #MyVar TABLE(Val DATETIME)
DECLARE #I INT, #StartDate DATETIME
SET #I = 1
SET #StartDate = '20100101'
WHILE #I <= 5
BEGIN
INSERT INTO #MyVar(Val)
VALUES(#StartDate)
SET #StartDate = DATEADD(DAY,1,#StartDate)
SET #I = #I + 1
END
SELECT *
FROM #MyVar
You can do the same with a temp table:
CREATE TABLE #MyVar(Val DATETIME)
DECLARE #I INT, #StartDate DATETIME
SET #I = 1
SET #StartDate = '20100101'
WHILE #I <= 5
BEGIN
INSERT INTO #MyVar(Val)
VALUES(#StartDate)
SET #StartDate = DATEADD(DAY,1,#StartDate)
SET #I = #I + 1
END
SELECT *
FROM #MyVar
You should tell us what is your main goal, as was said by #JohnFx, this could probably be done another (more efficient) way.
You could use a variable table, like this:
declare #num int
set #num = 1
declare #results table ( val int )
while (#num < 6)
begin
insert into #results ( val ) values ( #num )
set #num = #num + 1
end
select val from #results
This kind of depends on what you want to do with the results. If you're just after the numbers, a set-based option would be a numbers table - which comes in handy for all sorts of things.
For MSSQL 2005+, you can use a recursive CTE to generate a numbers table inline:
;WITH Numbers (N) AS (
SELECT 1 UNION ALL
SELECT 1 + N FROM Numbers WHERE N < 500
)
SELECT N FROM Numbers
OPTION (MAXRECURSION 500)
declare #counter as int
set #counter = 0
declare #date as varchar(50)
set #date = cast(1+#counter as varchar)+'/01/2013'
while(#counter < 12)
begin
select cast(1+#counter as varchar)+'/01/2013' as date
set #counter = #counter + 1
end
Off course an old question. But I have a simple solution where no need of Looping, CTE, Table variables etc.
DECLARE #MyVar datetime = '1/1/2010'
SELECT #MyVar
SELECT DATEADD (DD,NUMBER,#MyVar)
FROM master.dbo.spt_values
WHERE TYPE='P' AND NUMBER BETWEEN 0 AND 4
ORDER BY NUMBER
Note : spt_values is a Mircrosoft's undocumented table. It has numbers for every type. Its not suggestible to use as it can be removed in any new versions of sql server without prior information, since it is undocumented. But we can use it as quick workaround in some scenario's like above.
[CREATE PROCEDURE [rat].[GetYear]
AS
BEGIN
-- variable for storing start date
Declare #StartYear as int
-- Variable for the End date
Declare #EndYear as int
-- Setting the value in strat Date
select #StartYear = Value from rat.Configuration where Name = 'REPORT_START_YEAR';
-- Setting the End date
select #EndYear = Value from rat.Configuration where Name = 'REPORT_END_YEAR';
-- Creating Tem table
with [Years] as
(
--Selecting the Year
select #StartYear [Year]
--doing Union
union all
-- doing the loop in Years table
select Year+1 Year from [Years] where Year < #EndYear
)
--Selecting the Year table
selec]

How to determine when a time stamp does not exist in a table

I have a table that receives data on an hourly basis. Part of this import process writes the timestamp of the import to the table. My question is, how can I build a query to produce a result set of the periods of time when the import did not write to the table?
My first thought is to have a table of static int and just do an outer join and look for nulls on the right side, but this seems kind of sloppy. Is there a more dynamic way to produce a result set for the times the import failed based on the timestamp?
This is a MS SQL 2000 box.
Update: I think I've got it. The two answers already provided are great, but instead what I'm working on is a function that returns a table of the values I am looking for for a given time frame. Once I get it finished I'll post the solution here.
Here's a slightly modified solution from this article in my blog:
Flattening timespans: SQL Server
DECLARE #t TABLE
(
q_start DATETIME NOT NULL,
q_end DATETIME NOT NULL
)
DECLARE #qs DATETIME
DECLARE #qe DATETIME
DECLARE #ms DATETIME
DECLARE #me DATETIME
DECLARE cr_span CURSOR FAST_FORWARD
FOR
SELECT s_timestamp AS q_start,
DATEADD(minute, 1, s_timestamp) AS q_end
FROM [20090611_timespans].t_span
ORDER BY
q_start
OPEN cr_span
FETCH NEXT
FROM cr_span
INTO #qs, #qe
SET #ms = #qs
SET #me = #qe
WHILE ##FETCH_STATUS = 0
BEGIN
FETCH NEXT
FROM cr_span
INTO #qs, #qe
IF #qs > #me
BEGIN
INSERT
INTO #t
VALUES (#ms, #me)
SET #ms = #qs
END
SET #me = CASE WHEN #qe > #me THEN #qe ELSE #me END
END
IF #ms IS NOT NULL
BEGIN
INSERT
INTO #t
VALUES (#ms, #me)
END
CLOSE cr_span
This will return you the consecutive ranges when updates did happen (with a minute resolution).
If you have an index on your timestamp field, you may issue the following query:
SELECT *
FROM records ro
WHERE NOT EXISTS
(
SELECT NULL
FROM records ri
WHERE ri.timestamp >= DATEADD(minute, -1, ro.timestamp)
AND ri.timestamp < ro.timestamp
)
I was thinking something like this:
select 'Start' MissingStatus, o1.LastUpdate MissingStart
from Orders o1
left join Orders o2
on o1.LastUpdate between
dateadd(ss,1,o2.LastUpdate) and dateadd(hh,1,o2.LastUpdate)
where o2.LastUpdate is null
union all
select 'End', o1.LastUpdate MissingEnd
from Orders o1
left join Orders o2
on o1.LastUpdate between
dateadd(hh,-1,o2.LastUpdate) and dateadd(ss,-1,o2.LastUpdate)
where o2.LastUpdate is null
order by 2