>In a table called "Steps", I have the following fields: > >ID, B4Me, Dsc, > >-ID field contains a unique ID for each record >-B4Me contains the ID of some other record in the table that MUST appear in a >result set, BEFORE this record. B4Me may be null. This is called the "B4Me" >order. >-Records will be entered in random order, so Natural order cannot be relied >upon as a substitute for B4Me > >I need an SQL statement that will return all records with a user supplied >search word (such as "oil", or "glycol" or "micron") in the Dsc field, which >could return >a variable number of records, but what ever the number of records returned, >the *order* must be such that each record is preceded by the record who's ID >matches the B4Me value of a given record.&nbs p; > >Important Notes: > >- The SQL may return one or more records with a blank B4Me. In which case, >Natural order can be used for those records, and they are all considered >"First" or >rather, "Before everything else". Once all the records with a blank B4Me are >listed, the remaining must be in the B4Me order. > >- It is possible, though unlikely, that following the B4Me order will result >in a circular reference. (Two or more records that reference each other, or >the last >record in a large result set, references the first (or any other in the result >set) record in the B4Me field). In these cases, the SQL must either list all >records until >a circular reference is detected, then don't list the "offending" record, or >display an error message, but it must not hang FB and get an "out of memory" >error. > >Thanks in advance for any help you can provide.
The possibility of a circular reference, makes this a difficult task. SQL is great for working with sets of data, iteration and recursion are more difficult, but doable with CTEs or stored procedures, but I cannot think of how to exit a circular reference "gracefully" within one statement. Hence, I'd recommend using a global temporary table: CREATE GLOBAL TEMPORARY TABLE MyTmp (ID INTEGER, OrderNo INTEGER) ON COMMIT DELETE ROWS; --doesn't really matter whether you commit or delete rows in this particular case... and then EXECUTE BLOCK or a STORED PROCEDURE; EXECUTE BLOCK(MySearch varchar(50) = :MySearch) Returns(OutID Integer) as declare variable ID integer; declare variable ID2 integer; declare variable OrderNo integer; begin delete from MyTmp; --start afresh every time you execute this block OrderNo = 1000; for select ID, B4Me from "Steps" where Dsc containing :MySearch into :ID, :ID2 do begin while (:ID is not null) do begin if exists(select * from MyTmp where ID = :ID) then begin --This is how to escape from a circular reference id = null; end else begin INSERT INTO MyTmp(ID, OrderNo) VALUES(:ID, :OrderNo); OrderNo = OrderNo - 1; ID = ID2; if (ID is not null) then begin SELECT B4Me from "Steps" where ID = :ID into :ID2; end end end end for select ID from MyTmp order by OrderNo into :OutID do suspend; end I've interpreted "Before everything else" to mean "before any of its own children", not "before any children regardless of heritage" - or, to put it simple - you would appear before all your children, but not necessarily before my children. Hopefully, this can serve as a template for how you can solve your situation, despite being more than one statement. Set