Упорядочивание результатов SQL на основе предшественника - PullRequest
3 голосов
/ 09 июня 2011

В моей базе данных есть следующая таблица:

create table (
    name        varchar(25),
    value       int,
    predecessor varchar(25)
);

с данными образца:

name           | value | predecessor
---------------+-------+---------------
buyingredients | 10    | null
eat            |  3    | cook
cook           | 12    | mixingredients
mixingredients |  5    | buyingredients

Я хочу запрос SQL, который выбирает name и value, упорядоченный таким образом, чтобы элемент с предшественником null был первым, а затем строка с именем предшественника, равным названию этой строки, следующим и т. Д. (т. е. покупать ингредиенты, смешивать ингредиенты, готовить, есть).

Порядок строго линейный - поведение, если две строки имеют одинаковое значение предшественника, не определено.

Я изо всех сил пытаюсь найти запрос SQL, который произведет порядок, который я хочу. Я не знаю, с чего начать.

Я использую базу данных Informix, хотя любой вариант SQL был бы полезной отправной точкой.

обновлен, чтобы отразить факт, что порядок не в алфавитном порядке

Ответы [ 4 ]

2 голосов
/ 10 июня 2011

Informix не поддерживает предложение WITH, а тем более WITH RECURSIVE.Это делает эти подходы неработоспособными.Нет особо чистого способа сделать это.Не совсем ясно, что должно произойти, если у вас есть два или три списка, и не совсем ясно, имеете ли вы дело с простыми списками (пример - список) или с более общей древовидной структурой.

Однако,Вы можете создать временную таблицу и заполнить ее циклом в хранимой процедуре, а затем выбрать из временной таблицы в соответствующем порядке.Это сложнее, чем ужасно сложно.Вы делаете поиск таблицы в ширину.Я отмечаю, что вы не дали своей таблице имя - поэтому в дальнейшем она называется «анонимная».

CREATE TEMP TABLE Flattened
(
    Hierarchy   SERIAL,
    Level       INTEGER,
    Name        VARCHAR(25),
    Value       INTEGER,
    Predecessor VARCHAR(25)
);

CREATE TEMP TABLE Intermediate
(
    Hierarchy   SERIAL,
    Level       INTEGER,
    Name        VARCHAR(25),
    Value       INTEGER,
    Predecessor VARCHAR(25)
);

INSERT INTO Flattened(Hierarchy, Level, Name, Value, Predecessor)
   SELECT 0, 0, name, value, predecessor
     FROM Anonymous
    WHERE Predecessor IS NULL;

WHILE ...any rows were inserted into table...
    INSERT INTO Intermediate(Hierarchy, Level, Name, Value, Predecessor)
        SELECT F.Hierarchy, F.Level + 1, A.Name, A.Value, A.Predecessor
          FROM Flattened AS F, Anonymous AS A
         WHERE F.Name = A.Predecessor
           AND F.Level = (SELECT MAX(Level) FROM Flattened);
    INSERT INTO Flattened SELECT * FROM Intermediate;
    DELETE FROM Intermediate;
END WHILE

DROP TABLE Intermediate;

Теперь вы можете использовать:

SELECT Name, Value, Predecessor
  FROM Flattened
 ORDER BY Hierarchy, Level, Name;

Единственный остаточный хитрый битвыяснить, сколько строк было вставлено.В хранимой процедуре это, вероятно, проще всего сделать:

SELECT COUNT(*) INTO n_count FROM Flattened;

и

LET o_count = n_count - 1;
WHILE o_count != n_count
    ...as above - two INSERT operations and a DELETE operation
    LET o_count = n_count;
    SELECT COUNT(*) INTO n_count FROM Flattened;
END WHILE;

Собрав все это вместе, это работает для меня (в зарегистрированной базе данных).

BEGIN;

CREATE TABLE Anonymous
(
     Name        VARCHAR(25),
     Value       INTEGER,
     Predecessor VARCHAR(25)
);

INSERT INTO Anonymous VALUES("buyingredients", 10, NULL);
INSERT INTO Anonymous VALUES("eat", 3, "cook");
INSERT INTO Anonymous VALUES("cook", 12, "mixingredients");
INSERT INTO Anonymous VALUES("mixingredients", 5, "buyingredients");

CREATE PROCEDURE Flatten_Anonymous()

    DEFINE old_count INTEGER;
    DEFINE new_count INTEGER;

    CREATE TEMP TABLE Flattened
    (
        Hierarchy   SERIAL,
        Level       INTEGER,
        Name        VARCHAR(25),
        Value       INTEGER,
        Predecessor VARCHAR(25)
    );

    CREATE TEMP TABLE Intermediate
    (
        Hierarchy   SERIAL,
        Level       INTEGER,
        Name        VARCHAR(25),
        Value       INTEGER,
        Predecessor VARCHAR(25)
    );

    INSERT INTO Flattened(Hierarchy, Level, Name, Value, Predecessor)
       SELECT 0, 0, name, value, predecessor
         FROM Anonymous
        WHERE Predecessor IS NULL;

    SELECT COUNT(*) INTO new_count FROM Flattened;
    LET old_count = new_count - 1;
    WHILE old_count != new_count
        INSERT INTO Intermediate(Hierarchy, Level, Name, Value, Predecessor)
            SELECT F.Hierarchy, F.Level + 1, A.Name, A.Value, A.Predecessor
              FROM Flattened AS F, Anonymous AS A
             WHERE F.Name = A.Predecessor
               AND F.Level = (SELECT MAX(Level) FROM Flattened);
        INSERT INTO Flattened SELECT * FROM Intermediate;
        DELETE FROM Intermediate;
        LET old_count = new_count;
        SELECT COUNT(*) INTO new_count FROM Flattened;
    END WHILE

    DROP TABLE Intermediate;

END PROCEDURE;

EXECUTE PROCEDURE Flatten_Anonymous();

SELECT Name, Value, Predecessor
  FROM Flattened
 ORDER BY Hierarchy, Level, Name;

DROP TABLE Flattened;

ROLLBACK;

Вывод:

buyingredients     10
mixingredients      5  buyingredients
cook               12  mixingredients
eat                 3  cook

Тестовая платформа: Informix 11.70.FC2, работающий на MacOS X 10.6.7.

Формально не протестировано с несколькими независимыми иерархиямиили с древовидной иерархией вместо простых списков.

2 голосов
/ 09 июня 2011

В Transact-SQL с общими табличными выражениями:

WITH LinkedList (name, value, predecessor, ordering)
AS
(
    SELECT a.name, a.value, a.predecessor, 0
    FROM   YourTable a
    WHERE  a.predecessor IS NULL
    UNION ALL
    SELECT b.name, b.value, b.predecessor, c.ordering + 1
    FROM   YourTable b
    INNER JOIN LinkedList c
    ON     b.predecessor = c.name
)
SELECT d.name, d.value
FROM   LinkedList d
ORDER BY d.ordering, d.name

Я не знаю, имеет ли Informix такую ​​конструкцию, но то, что вы спрашиваете, по сути, является рекурсивным запросом, который дают вам общие табличные выраженияв Transact-SQL.

1 голос
/ 09 июня 2011

Я использую базу данных Informix, хотя любой вариант SQL был бы полезной отправной точкой.

Я ничего не знаю о Informix, поэтому здесь есть версия для SQL Server.@T - переменная таблицы, которую я использую в качестве тестовой таблицы.Для объединения строк я использую рекурсивный CTE.

declare @T table
(
  name varchar(25),
  value int,
  predecessor varchar(25)
)  

insert into @T
select 'buyingredients', 10, null union all
select 'cook',           12, 'mixingredients' union all
select 'mixingredients',  5, 'buyingredients' union all
select 'eat',             3, 'cook'

;with cte as 
(
  select T.name,
         T.value,
         T.predecessor,
         1 as sortorder
  from @T as T
  where T.predecessor is null
  union all
  select T.name,
         T.value,
         T.predecessor,
         C.sortorder+1 as sortorder
  from @T as T
    inner join cte as C
      on T.predecessor = C.name
)
select C.name,
       C.value,
       C.predecessor
from cte as C
order by C.sortorder

Вы можете попробовать это здесь: http://data.stackexchange.com/stackoverflow/q/102534/recursive-cte-to-build-a-chain

Редактировать

Я все ещеничего не знаю о Informix, но, возможно, вы можете что-то сделать, используя Node DataBlade Module

0 голосов
/ 09 июня 2011

Я думаю, что способ сделать то, что вы хотите, состоит в том, чтобы создать временную таблицу с полем «глубина» для каждого элемента, а затем использовать это поле глубины, чтобы упорядочить результаты.

...