CTE recursion infinite loop - sql

I'm working with a stored procedure and using a CTE in SQL Server and I'm trying to reach some data from a 2 tables, but when the execution goes to the CTE query it gets an infinite loop and never ends, is there a way to prevent that infinite loop?
This is the query that I create:
WITH tableName(Id, enddate, statusDte, closeId, shceDte, calcDte, closeEndDte, ParentId, LastClose, lasCloseDte, closeClass,addSe,twon,code)
AS
(
SELECT
tba.Id,
CASE WHEN tb.ParentId IS NOT NULL
THEN tb.Id
WHEN tb.statusDte IN (1,2,3)
THEN tb.calcDte ELSE tb.shceDte
END ForecastDueDate,
statusDte, closeId, shceDte, calcDte,
CASE WHEN tb.ParentId IS NULL
THEN closeEndDte ELSE NULL END, tb.ParentId, 0,
CASE WHEN tb.ParentId IS NOT NULL
THEN statusDte
WHEN tb.statusDte = 5
AND (tb.calcDte BETWEEN '1/1/2020 12:00:00 AM' AND '12/31/2020 11:59:59 PM'
OR tb.closeEndDte BETWEEN '1/1/2020 12:00:00 AM' AND '12/31/2020 11:59:59 PM')
THEN ams.GetPreviousNthFullAuditDate(tb.Id, tb.AuditID, 2) ELSE a.statusDate END as lastDate,
a.closeClass, tba.addSe,tba.town,tba.code
FROM
tableA tba
INNER JOIN
tableB tb ON tb.Id = tba.Id
WHERE
statusDte NOT IN (3,4) AND tba.IsAtve = 1
UNION ALL
SELECT
Id, enddate,
statusDte, statusDte, shceDte, calcDte, closeEndDte, ParentId,
0, lasCloseDte, closeClass,addSe,twon,code
FROM
tableName
WHERE
enddate BETWEEN enddate AND '12/31/2020 11:59:59 PM'
)
SELECT *
FROM tableName
OPTION (maxrecursion 0)
Expected results
Id enddate statusDte closeId shceDte calcDte closeEndDte parentId lastClose lastCloseDte closeClass addSe town code
----------- ----------------------- ------------- ----------- ----------------------- ----------------------- ----------------------- ----------------------- ----------- ----------------------- ----------- --------------------------------- ---------------------- --------------------------------------------------
133 2011-04-04 00:00:00.000 22 14453 NULL 2011-04-04 00:00:00.000 2099-12-31 00:00:00.000 NULL 0 NULL 1 4707 EXECUTIVE DRIVE '' SAN DIEGO 123
56 2018-12-07 13:00:00.000 22 52354 NULL 2018-12-07 13:00:00.000 2019-12-07 00:00:00.000 NULL 0 NULL 1 75 STATE ST FL 24 '' BOSTON 345
12 2021-02-05 17:00:00.000 22 75751 NULL 2021-02-05 17:00:00.000 NULL NULL 0 NULL 1 1450 FRAZEE RD STE 308 '' SAN DIEGO 678
334 2019-03-07 16:30:00.000 15 66707 2019-03-07 16:30:00.000 2019-03-23 21:00:00.000 NULL NULL 0 2019-03-07 16:30:00.000 1 42690 RIO NEDO, STE E '' TEMECULA 91011
33 2020-01-10 17:00:00.000 22 65568 NULL 2020-01-10 17:00:00.000 NULL NULL 0 2018-01-10 17:00:00.000 1 2518 UNICORNIO ST. '' CARLSBAD 136
55 2020-04-16 20:00:00.000 22 67812 NULL 2020-04-16 20:00:00.000 NULL NULL 0 2018-04-17 20:00:00.000 1 4534 OSPREY STREET '' SAN DIEGO 653
66 2020-02-21 17:00:00.000 22 75956 NULL 2020-02-21 17:00:00.000 NULL NULL 0 2019-02-21 17:00:00.000 1 3511 CAMINO DEL RIO S, STE 305 '' SAN DIEGO 0484
094 2021-02-20 21:00:00.000 22 75629 NULL 2021-02-20 21:00:00.000 NULL NULL 0 NULL 1 29349 EAGLE DR '' MURRIETA 345

First, let's try to add some best practices. Qualify all your columns with the appropriate table alias. Just doing some of them is inconsistent and inconsistent style is difficult to read and prone to errors.
Next, you've (hopefully) dumbed down your actual query. Generic names like "tableA" hinder understanding.
Next - your first case expression seems highly suspicious. You have one branch returns tb.id and the others return what appears to be a date (or datetime). You can, unfortunately, cast an int to a datetime. Might not make any sense and it won't generate an error. So - does this make sense?
Next - you've made a common mistake with your datetime boundaries. Depending on your data you might never know this. But there is no reason to expect that and there is every reason to write your logic so that it avoids any possibility. Tibor discusses in great detail here. Shorter version - your upper boundary should always be an exclusive one to support all possible values of time for your datatype. 23:59:59 will ignore any time values with non-zero milliseconds. And use a literal format that is not dependent on language or connection settings.
Next, you add confusion. You named your columns in the cte declaration but your code also includes aliases for some (but not all - see, refer to the consistency comment) columns which differ significantly from the actual column name for the cte. The 2nd column for the cte is "enddate", the anchor query uses the alias "ForecastDueDate"
Next, you have this: tb.statusDte = 5. The name implies date; the literal implies something different. You have other columns that end in "Dte" that are obviously dates, but not this one? Danger, danger!
Next, you refer to columns "a.closeClass" and "a.statusDate". There is no table or alias named "a".
Lastly, you have:
WHERE enddate BETWEEN enddate AND '12/31/2020 11:59:59 PM'
Think about what you wrote. Is not enddate always between enddate and Dec 31 2010 (so long as enddate <= that value)? I think this is the source of your issue. You're not computing or adjusting anything from the anchor, so the recursed part just keeps selecting and selecting and selecting. There is no logic to end the recursion.
The next question is obviously "now to fix it". That is impossible to say without knowing your schema, what it represents, and your goal. The use of recursion here is not obvious.

If the data is in a structure that the hierarchy between records is is a loop then recursion goes to infinite causing a problem in SQL. You will see the resources used by SQL process is increasing tremendously.
If you use MAXRECURSION with a different value than 0 (zero lets SQL to continue recursion without a limit) you will be able to limit the recursion.
With data that is looping or referencing each other you can this MAXRECURSION parameter

Related

Access Query: Match Two FKs, Select Record with Max (Latest) Time, Return 3d Field From Record

I have an Access table (Logs) like this:
pk
modID
relID
DateTime
TxType
1
1234
22.3
10/1/22 04:00
1
2
1234
23.1
10/10/22 06:00
1
3
1234
23.1
10/11/22 07:00
2
4
1234
23.1
10/12/22 08:00
3
5
4321
22.3
10/2/22 06:00
7
6
4321
23.1
10/10/22 06:00
1
7
4321
23.1
10/11/22 07:30
3
Trying to write a query as part of a function that searches this table:
for all records matching a given modID and relID (e.g. 1234 and 23.1),
picks the most recent one (the MAX of DateTime),
returns the TxType for that record.
However, a bit new to Access and its query structure is vexing me. I landed on this but because I have to include a Total/Aggregate function for TxType I had to either choose Group By (not what I want) or Last (closer, but returns junk results). The SQL for my query is currently:
SELECT Last(Logs.TxType) AS LastOfTxType, Max(Logs.DateTime) AS MaxOfDateTime
FROM Logs
GROUP BY Logs.dmID, Logs.relID
HAVING (((Logs.dmID)=[EnterdmID]) AND ((Logs.relID)=[EnterrelID]));
It returns the TxType field when I pass it the right parameters, but not the correct record - I would like to be rid of the Last() bit but if I remove it Access complains that I don't have it as part of an aggregate function.
Anyone that can point me in the right direction here?
Have you tried
SELECT TOP 1 TxtType
FROM Logs
WHERE (((Logs.dmID)=[EnterdmID]) AND ((Logs.relID)=[EnterrelID]))
ORDER BY DateTime DESC;
That will give you the latest single data row based on your DateTime field and other criteria.

Join tables on dates, with dirty date field

In AWS Athena, I am trying to join two tables in the db using the date, but one of the tables (table2) is not clean, and contains values that are not dates, as shown below.
| table2.date |
| ---- |
|6/02/2021|
|9/02/2021|
|1431 BEL & 1628 BEL."|
|15/02/2021|
|and failed to ....|
|18/02/2021|
|19/02/2021|
I am not able to have any influence in cleaning this table up.
My current query is:
SELECT *
FROM table1
LEFT JOIN table2
ON table1.operation_date = cast(date_parse(table2."date",'%d/%m/%Y') as date)
LIMIT 10;
I've tried using regex_like(col, '[a-z]'), but this still leaves the values that are numerical, but not dates.
How do I get the query to ignore the values that are not dates?
You may wrap conversion expression with try function, that will resolve to NULL in case of failed conversion.
select
try(date_parse(col, '%d/%m/%Y'))
from(values
('6/02/2021'),
('9/02/2021'),
('1431 BEL & 1628 BEL.'),
('15/02/2021'),
('and failed to ....'),
('18/02/2021'),
('19/02/2021')
) as t(col)
#
_col0
1
2021-02-06 00:00:00.000
2
2021-02-09 00:00:00.000
3
4
2021-02-15 00:00:00.000
5
6
2021-02-18 00:00:00.000
7
2021-02-19 00:00:00.000

Display the Names and Date of Births of all Programmers Born in January

I am trying to use datetime to find the date of births (DOB) during January of certain PName. I have a short part that works in SQL Server, but I would like to find out the DOB of Jan or 1st month. It works in SQL Server for the whole database, but when I want to find just the particular month (DOB) of the database I can't find a correct way to right it. According to the Internet the Oracle it was written:
SELECT DOB, NAME FROM PROGRAMMER WHERE TO_CHAR(DOB,'MON') LIKE 'JAN';
I am trying to find the SQL Server - Programmer with DOB - say January
Name and dates of DOB with Jan
SELECT TOP (1000) [Programmer_Id]
, [PName]
**, [DOB]**
, [DOB2]
, [DOJ]
, [DOJ2]
, [Gender]
, [Prof1]
, [Prof2]
, [Salary]
, [Salary2]
FROM [Studies_S_P_no_foreign].[dbo].[Programmer]
SELECT
DATEPART(month, DOB) [month] --#d2 DOB '2019-01-01 14:30:14'
--where DOB is like 1
From Programmer
[Programmer_Id],[PName], [DOB], [DOB2], [DOJ], [DOJ2], [Gender], [Prof1], [Prof2], [Salary], [Salary2]
1 ANAND 1966-04-12 00:00:00.000 1966-04-01 1992-04-21 00:00:00.000 1992-04-21 M PASCAL BASIC 3200 3200.00
2 ALTAF 1964-07-02 00:00:00.000 1964-07-02 1990-11-13 00:00:00.000 1990-11-13 M CLIPPER COBOL 2800 2800.00
3 JULIANA 1960-01-31 00:00:00.000 1960-01-31 1990-04-21 00:00:00.000 1990-04-21 F COBOL DBASE 3000 3000.00
4 KAMALA 1968-10-30 00:00:00.000 1968-10-30 1992-01-02 00:00:00.000 1992-01-02 F C DBASE 2900 2900.00
5 MARY 1970-06-24 00:00:00.000 1970-06-24 1991-02-01 00:00:00.000 1991-02-01 F CPP ORACLE 4500 4500.00
6 NELSON 1985-09-11 00:00:00.000 1985-09-11 1989-10-11 00:00:00.000 1989-10-11 M COBOL DBASE 2500 2500.00
7 PATTRICK 1965-11-10 00:00:00.000 1965-11-10 1990-04-21 00:00:00.000 1990-04-21 M PASCAL CLIPPER 2800 2800.00
8 QADIR 1965-08-31 00:00:00.000 1965-08-31 1991-04-21 00:00:00.000 1991-04-21 M ASSEMBLY C 3000 3000.00
9 RAMESH 1967-05-03 00:00:00.000 1967-05-03 1991-02-28 00:00:00.000 1991-02-28 M PASCAL DBASE 3200 3200.00
10 REBECCA 1967-01-01 00:00:00.000 1967-01-01 1990-12-01 00:00:00.000 1990-12-01 F BASIC COBOL 2500 2500.00
11 REMITHA 1970-04-19 00:00:00.000 1970-04-19 1993-04-20 00:00:00.000 1993-04-20 F C ASSEMBLY 3600 3600.00
12 REVATHI 1969-12-02 00:00:00.000 1969-12-02 1992-01-02 00:00:00.000 1992-01-02 F PASCAL BASIC 3700 3700.00
13 VIJAYA 1965-12-14 00:00:00.000 1965-12-14 1992-05-02 00:00:00.000 1992-05-02 F FOXPRO C 3500 3500.00
NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL NULL
There are many ways you can do the comparison in SQL Server. Below are some of them.
DECLARE #table table(DOB DATETIME)
INSERT INTO #table
values ('2019-01-01 14:30:14')
SELECT * FROM #table where MONTH(DOB) = 1
SELECT * FROM #table where DATENAME(mm,dob) = 'January'
SELECT * FROM #table where DATEPART(mm,dob) = 1
If want to do similar to Oracle for 3 character month
SELECT * FROM #table where LEFT(DATENAME(mm,dob),3) = 'JAN'
CAVEAT Thanks to #AlwaysLearning
datename is set language-sensitive. e.g.: for January, French=janvier;
Italian=gennaio; German=Januar; Spanish=Enero. If you have a
multicultural database prefer to use month or datepart

How do I select / identify a row based on criteria in a different row in SQL

I've never posted on here before but, I am really stumped on this and looking for any assistance I get! I am not the best SQL code writer and I do not understand every concept but I am quick learner. So, I am not sure this is best way to accomplish my goal and if there is a more efficient way to complete this, I would be open to learning. I appreciate any help that can be provided.
Task:
I am attempting to write a SQL code that will help me place a number under the "Grab" column that allows me to exclude other rows out that are not needed.
Issue:
Pricing has a timeframe when it is applicable. The [PriceBookTable] captures the time frame range for each price book that is listed. However, as time goes on, some price books become outdated and do not need to be reviewed.
Based on today's date, I am trying to identify the previous version price book as well as the next version (if there is one).
Table Used: [PriceBookTable]
ID
Description
CategoryID
ParentID
StartDate
EndDate
412
56 MSRP
56
NULL
NULL
NULL
413
3 MSRP
3
NULL
NULL
NULL
414
61 MSRP
61
NULL
NULL
NULL
415
63 MSRP
63
NULL
NULL
NULL
419
58 MSRP
58
NULL
NULL
NULL
420
62 MSRP
62
NULL
NULL
NULL
430
67 MSRP
67
NULL
NULL
NULL
431
68 MSRP
68
NULL
NULL
NULL
505
2020 Version 1
56
412
2020-08-31
2020-12-31
537
2021 Version 1
56
412
2021-01-01
2021-03-31
586
2021 Version 2
56
412
2021-04-01
2021-04-13
622
2021 Version 3
56
412
2021-04-14
2021-07-31
688
2021 Version 4
56
412
2021-08-01
2021-12-31
Current Code:
USE [Database]
DECLARE #PriceBookID AS VARCHAR(10)
SET #PriceBookID = '412' --Parent Price Book ID
SELECT A.*,
[Grab] = CASE WHEN A.ParentID IS NULL AND A.StartDate IS NULL AND A.EndDate IS NULL THEN 1 -- Always needs to be #1
WHEN CAST(GETDATE() AS DATE) BETWEEN A.StartDate AND A.EndDate THEN 3 --Currently Active Price Book based on Today's Date
ELSE NULL END
FROM( SELECT ID,
ParentID,
[PriceBookDescription] = Description,
StartDate,
EndDate,
[ActivePriceBook] = CASE WHEN CAST(GETDATE() AS DATE) BETWEEN StartDate AND EndDate THEN 'Active' ELSE NULL END,
[PBOrder] = ROW_NUMBER() OVER (ORDER BY ID ASC)
FROM [PriceBookTable]
WHERE 1=1 AND ID IN (#PriceBookID) OR ParentID IN (#PriceBookID)) A
Current Output:
ID
ParentID
PriceBookDescription
StartDate
EndDate
ActivePriceBook
PBOrder
Grab
412
Null
MSRP
NULL
NULL
NULL
1
1
505
412
2020 Version 1
2020-08-31
2020-12-31
NULL
2
NULL
537
412
2021 Version 1
2021-01-01
2021-03-31
NULL
3
NULL
586
412
2021 Version 2
2021-04-01
2021-04-13
NULL
4
NULL
622
412
2021 Version 3
2021-04-14
2021-07-31
Active
5
3
688
412
2021 Version 4
2021-08-01
2021-12-31
NULL
6
NULL
Notes:
I originally was hoping that the "PBOrder" column would be useful for me but, as time goes on the list becomes bigger as more price books are created and, for example, row #4 [ID 586] will not always be relevant.
I would have just placed a "WHERE ID IN ('412','586','622','688')" statement but the ID's change based on different categories (not shown). So, I am stuck to the date range.
Desired Output:
ID
ParentID
PriceBookDescription
StartDate
EndDate
ActivePriceBook
PBOrder
Grab
412
Null
MSRP
NULL
NULL
NULL
1
1
586
412
2021 Version 2
2021-04-01
2021-04-13
NULL
4
2
622
412
2021 Version 3
2021-04-14
2021-07-31
Active
5
3
688
412
2021 Version 4
2021-08-01
2021-12-31
NULL
6
4
I hope this makes sense and please let me know if you have any questions regarding this.
Thank you again for any help!
Took me awhile to understand what you wanted, but after figuring it out I was able to address what you need. Basically, you want:
To identify a single active record within a category based on the current date.
Then get the adjacent inactive records, with respect to time, that share a parent record.
Then get the record for the parent category and include it in the result set.
The 'pbOrder' and 'grab' columns seem to be throughputs to achieve this goal. You don't need them in the output.
If this is all correct, then you can delegate your identification of an active record to a cross apply calculation, and then use lead and lag in addition to the raw result to identify the active record as well as the adjacent ones in time.
declare #PriceBookID int = 412; -- why varchar, I would use int
with rowsToGrab as (
select pbt.*,
ap.activePriceBook,
grab =
case
when pbt.ParentID is null then 1
when lead(ap.ActivePriceBook) over(order by pbt.startDate) is not null then 1
when lag(ap.ActivePriceBook) over(order by pbt.startDate) is not null then 1
when ap.ActivePriceBook is not null then 1
end
from #PriceBookTable pbt
cross apply (select ActivePriceBook =
case
when cast(getdate() as date) between startdate and enddate then 'Active'
end
) ap
where #PriceBookID in (ID, ParentID)
)
select id, ParentID, description as PriceBookDescription, StartDate, EndDate, ActivePriceBook
from rowsToGrab
where grab is not null
order by id, StartDate
This produces:
id
ParentID
PriceBookDescription
StartDate
EndDate
ActivePriceBook
412
56 MSRP
586
412
2021 Version 2
2021-04-01
2021-04-13
622
412
2021 Version 3
2021-04-14
2021-07-31
Active
688
412
2021 Version 4
2021-08-01
2021-12-31

For Each Loop in SQL Server using Cursor

I have a table with 4 cols.
HouseNo, Date, Time and Temp.
I have managed to obtain the different HouseNos in a separate table. Now i want to insert all the dates for all the house nos.
Sample Data from table. There are like a few million rows like this.
HouseNo Date Time Temp
102 1/1/2010 10:00 67
102 2/1/2010 10:00 73
102 3/1/2010 10:00 75
103 1/1/2010 10:00 69
103 2/1/2010 10:00 63
104 1/1/2010 10:00 71
104 2/1/2010 10:00 12
Expected Output is
table 1
102 1/1/2010
102 2/1/2010
102 3/1/2010
table 2
103 1/1/2010
103 2/1/2010
table 3
104 1/1/2010
104 2/1/2010
Then i want to be able to loop through each row in the tables derieved to perform some operation on the temperature field.
If you have one ON/OFF pair per day and the OFF is always before the ON, this gets you the duration.
SELECT
HouseNo,
Date,
DATEDIFF(s,
MIN(CASE WHEN Relay='OFF' THEN Time ELSE NULL END),
MIN(CASE WHEN Relay='ON' THEN Time ELSE NULL END)
) As OffDuration
FROM YourTable
GROUP BY HouseNo, Date
But any normal real life dataset will have multiple ON/OFF pairs. Can you give more detail?
something like this ?
CREATE TABLE new_table
AS (SELECT * FROM old_table);
you can also put some WHERE part and SELECT