August 13, 2007 at 8:54 am
I have this table
CREATE table1
Proc VARCHAR(10),
PrevProc VARCHAR(10)
Both fields are mandatory. In this table sequences are stored of jobs to run. PrevProc will hold the name of the job which has to finish before the job in Proc can run. What I need is a resultset per sequence, which start with a jobname in PrevProc not existing in Proc(this is the first job of this specific sequence) and next all following jobnames in 12 row:
Job1, Job2, Job3, Job4 etc.
I have been fiddling with a SP, where I have played with temptables and such, but what I get is far from complete and very messy. I wonder if this shoudl be possible to create in a view.
Any hints are welcome
Greetz,
Hans Brouwer
August 13, 2007 at 9:02 am
This is a hierarchy problem. This is pretty simple to solve with loops.
However one other solution I like is to have another design :
BatchID --ids the group of jobs to run
JobName --Name of the job to run
Sequence -- Order in which to run the jobs.
Once you add a unique constraint on BatchID, Sequence and another constraint on BatchID, JobName you will be sure to have valid data. Then the only trick is to make a small app that changes the order for ya. Takes about 1 hour to figure out and code.
Msg back if you need more help implementing this solution or if you need help implementing hierarchies (you can find a lot of solutions on this forum alone).
August 13, 2007 at 11:51 pm
I think in this case you would have to check the entire dataset as the depth of the hierarchies will have to be decided dynamically ..... If this is the case you would have to loop through the database for (depth-1) times.
And i believe it will have to be traversed through by either a while loop or a cursor.
Please correct me if i am wrong ... i would be glad to know any other work around in this case.
August 14, 2007 at 1:18 am
Ninja,
You are right, a construction like that would make things easier. However, I am not in the position to change the table as it is presented. Also, the table as it is is datamodellingwise enough: it contains everythings you need. It's just more difficult to extract the info I need.
I guess then that I am on the right track, it's not possible to do this in 1 statement, i.e. a view. Too bad...
Greetz,
Hans Brouwer
August 14, 2007 at 5:00 am
It's possible if you know the Max Depth of the tree... as you can do as many left joins as the max depth requires... but that also has some draw backs. This is one case where loops are pretty much the best solution.
August 21, 2007 at 8:53 am
Imagine this situation:
Job3 needs to start AFTER Job2 has finished AND Job4 has finished. Job2 can start AFTER Job7 has finished. Job7 and Job4 have no preceding job and can run independently from each other. It's difficult to explain here without graphic options, but I'll try. The sequence would be like this:
Job7 => Job2|
|=> Job3
Job4 => |
I hope I make myself clear. For a single sequence I can handle finding it in the given table. For this sequence, which is very common, I still need to find a good solution.
Greetz,
Hans Brouwer
August 21, 2007 at 9:10 am
No it's very clear. You just need to search these forums for hierarchies and you'll find plenty of solutions for sql 2000. In 2005 the new solution is to use CTE.
August 21, 2007 at 10:58 am
You may want something like the following. If not you will need to post test data and expected results in order for the problem to be understood.
DECLARE @t TABLE
(
[Proc] varchar(10) NOT NULL
,PrevProc varchar(10) NULL
)
INSERT INTO @t
SELECT 'Job3', 'Job1' UNION ALL
SELECT 'Job1', 'Job2' UNION ALL
SELECT 'Job3', 'Job4' UNION ALL
SELECT 'Job2', 'Job7' UNION ALL
SELECT 'Job7', NULL UNION ALL
SELECT 'Job4', NULL
;WITH Results (ROrder, LevelProc)
AS
(
SELECT 1, [Proc]
FROM @t
WHERE PrevProc IS NULL
UNION ALL
SELECT R.ROrder + 1
,T.[Proc]
FROM Results R
JOIN @t T
ON R.LevelProc = T.PrevProc
)
SELECT *
FROM Results R
WHERE NOT EXISTS (
SELECT *
FROM Results R1
WHERE R1.LevelProc = R.LevelProc
AND R1.ROrder > R.ROrder
)
August 21, 2007 at 11:29 am
Ken,
Your code seems to do the trick. I'm going to test with real data, but the first few tests look very good. Thanks a lot!
EDIT: Alas, when adding a few more sequences I receive a errormessage:
Msg 530, Level 16, State 1, Line 15
The statement terminated. The maximum recursion 100 has been exhausted before statement completion.
Is there a recursion limit somewhere to be set? I run this currently in SQL2005, although it is going to be implemented on SQL2K...
Forget about that, made a mistake. I have another problem now, I need to distinguish between different sequences. Suppose I have another sequence next to the previous 1:
Job5 =>Job6.
It needs to show up as a different sequence. Think I can figure this out myself, tho.
Tnx again.
Greetz,
Hans Brouwer
August 22, 2007 at 6:32 am
Ken,
I need yopur help again, I think. Your script runs in SQL2005 and I need to have it run on SQL2K.
Also, I still have no solution for distinguishing between the different sequences...
Greetz,
Hans Brouwer
August 22, 2007 at 7:23 am
In SQL2005, the best I can come up with for the sequence idea is a #temp table and two recursions. (As below)
If anyone can do any better I would be interested in knowing.
In SQL2000 you will have to use iteration. ie Create a #temp table for Results and populate it with a WHILE loop. There should be plenty of examples of doing this on the site.
CREATE TABLE #temp
(
ROrder int NOT NULL
,LevelProc varchar(10) COLLATE DATABASE_DEFAULT NULL
,PrevProc varchar(10) COLLATE DATABASE_DEFAULT NULL
,NextProc varchar(10) COLLATE DATABASE_DEFAULT NULL
)
-- *** Test Data ***
DECLARE @t TABLE
(
[Proc] varchar(10) NOT NULL
,PrevProc varchar(10) NULL
)
INSERT INTO @t
SELECT 'Job3', 'Job1' UNION ALL
SELECT 'Job1', 'Job2' UNION ALL
SELECT 'Job3', 'Job4' UNION ALL
SELECT 'Job2', 'Job7' UNION ALL
SELECT 'Job7', NULL UNION ALL
SELECT 'Job4', NULL UNION ALL
SELECT 'Job5', 'Job6' UNION ALL
SELECT 'Job6', NULL
-- *** End Test Data ***
;WITH Results (ROrder, LevelProc, PrevProc)
AS
(
SELECT 1
,[Proc]
,PrevProc
FROM @t
WHERE PrevProc IS NULL
UNION ALL
SELECT R.ROrder + 1
,T1.[Proc]
,T1.PrevProc
FROM Results R
JOIN @t T1
ON R.LevelProc = T1.PrevProc
)
INSERT INTO #temp
SELECT R.ROrder
,R.LevelProc
,R.PrevProc
,R1.LevelProc AS NextProc
FROM Results R
LEFT JOIN Results R1
ON R.LevelProc = R1.PrevProc
AND R.ROrder = R1.ROrder - 1
;WITH Results (Seq, ROrder, LevelProc)
AS
(
SELECT D.RowID, T.ROrder, T.LevelProc
FROM #temp T
JOIN (
SELECT ROW_NUMBER() OVER (ORDER BY D1.LevelProc) AS RowID
,D1.LevelProc
FROM (
SELECT DISTINCT T1.LevelProc
FROM #temp T1
WHERE T1.NextProc IS NULL
) D1
) D
ON T.LevelProc = D.LevelProc
UNION ALL
SELECT R.Seq, T2.ROrder, T2.LevelProc
FROM Results R
JOIN #temp T1
ON R.LevelProc = T1.LevelProc
AND R.ROrder = T1.ROrder
JOIN #temp T2
ON T1.PrevProc = T2.LevelProc
AND T1.ROrder = T2.ROrder + 1
)
SELECT Seq
,MAX(ROrder) AS ROrder
,LevelProc
FROM Results
GROUP BY Seq, LevelProc
ORDER BY Seq, ROrder
August 22, 2007 at 9:43 am
Ken,
If the purpose of the table is to hold 'precise' job sequences, then the fact that job3 is specified twice in your sample data seems to be incorrect. Thus there is no way for me to tell which goes first job1 or job4.
I think the data for this table comes from 'precise' job sequences, e.g. job2-job4-job7, job3-job1-job6, etc and is simply stored in this minimalist manner (a bit ridiculous in my opinion). Based on this, I think the values in column 'Proc' must be unique. Same goes for 'PrevProc' - but it can be NULL.
Maybe the OP can shed some light on where the data for this table comes from in the real world.
August 22, 2007 at 10:15 am
I tend to agree with you and Ninja has already pointed out to Hans that the data model is bad.
My understanding, based on the limited information provided, is that some procs can run in parallel while others have to wait until the previous proc completes. I was also assuming that a sequence is defined as a common end point. (eg Job3 and Job5) This could be completely wrong. I was just trying to use CTE's to get the desired results based on the information available.
August 22, 2007 at 11:35 am
I have just looked at this again and have managed to get nearly the same result with one CTE.
The difference is Job4 starts with Job1 and not Job7.
-- *** Test Data ***
DECLARE @t TABLE
(
[Proc] varchar(10) NOT NULL
,PrevProc varchar(10) NULL
)
INSERT INTO @t
SELECT 'Job3', 'Job1' UNION ALL
SELECT 'Job1', 'Job2' UNION ALL
SELECT 'Job3', 'Job4' UNION ALL
SELECT 'Job2', 'Job7' UNION ALL
SELECT 'Job7', NULL UNION ALL
SELECT 'Job4', NULL UNION ALL
SELECT 'Job5', 'Job6' UNION ALL
SELECT 'Job6', NULL
-- *** End Test Data ***
;WITH Results (Seq, ROrder, LevelProc, PrevProc)
AS
(
SELECT DENSE_RANK() OVER (ORDER BY T1.[Proc])
,1
,T1.[Proc]
,T1.PrevProc
FROM @t T1
LEFT JOIN @t T2
ON T1.[Proc] = T2.PrevProc
WHERE T2.PrevProc IS NULL
UNION ALL
SELECT R.Seq
,R.ROrder + 1
,T3.[Proc]
,T3.PrevProc
FROM Results R
JOIN @t T3
ON R.PrevProc = T3.[Proc]
)
SELECT D1.Seq
,D2.MaxROrder - D1.ROrder + 1 AS ROrder
,D1.LevelProc
FROM (
SELECT R1.Seq
,MIN(R1.ROrder) AS ROrder
,R1.LevelProc
FROM Results R1
GROUP BY R1.Seq, R1.LevelProc
) D1
JOIN (
SELECT R2.Seq
,MAX(R2.ROrder) AS MaxROrder
FROM Results R2
GROUP BY R2.Seq
) D2
ON D1.Seq = D2.Seq
ORDER BY Seq, ROrder
August 22, 2007 at 10:40 pm
..is that some procs can run in parallel while others have to wait until the previous proc completes.
This is exactly the case.
I cannot test your example today, will get back tomorrow.Tnx for input all.
I tested the code today and it looks like I can work with this set. Another problem is, that the script is to be used on different database platforms and needs to be as close to ANSI SQL as possible. I have grave difficulties to translate the specific SQL2005 functionality to ANSI SQL. Any1 can point me to info on how to translate those features?
Greetz,
Hans Brouwer
Viewing 15 posts - 1 through 15 (of 42 total)
You must be logged in to reply to this topic. Login to reply