>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
  • ... firebirdbest...@yahoo.com [firebird-support]
    • ... liviusliv...@poczta.onet.pl [firebird-support]
      • ... liviusliv...@poczta.onet.pl [firebird-support]
    • ... Svein Erling Tysvær svein.erling.tysv...@kreftregisteret.no [firebird-support]

Reply via email to