Как передать результат запроса функции, являющейся частью того же самого?

Это с Informix 11.70.FC6GE в Linux.

Предполагая, что таблица mytable со столбцом value varchar(16) и функцией выглядит следующим образом:

create function myfunc(str varchar(16)) returning varchar(16)
  define result varchar(16);

  while (<some-condition>)
    let result = ...;
    return result with resume;
  end while;

end function;

Когда я делаю

select * from mytable, table(myfunc(value)) vt(result);

я получил

Not implemented yet. [SQL State=IX000, DB Errorcode=-999]

... - :-S.

Делает

select * from mytable, table(myfunc('some literal')) vt(result);

работает.

Есть ли шанс получить это в данной среде? А если нет: На какую версию Informix мне нужно перейти?


person alk    schedule 13.10.2015    source источник
comment
Какая у вас версия Informix?   -  person Jonathan Leffler    schedule 14.10.2015
comment
Я подозреваю, что ваш вопрос относится к выпуску, который называется Informix Growth Edition V11.70 @JonathanLeffler.   -  person alk    schedule 14.10.2015
comment
Меня интересовала версия 11.70 против 12.10 или одна из многих старых версий. Издание мало что меняет в этом контексте. Мне придется поэкспериментировать. Я подозреваю, что проблема связана с выполнением коррелированного запроса, но с попыткой рассматривать одну из таблиц как переменную. У меня даже нет хороших слов, чтобы описать это.   -  person Jonathan Leffler    schedule 14.10.2015
comment
Извините за огромную задержку с созданием ответа на этот вопрос.   -  person Jonathan Leffler    schedule 09.07.2016


Ответы (1)


Я не думаю, что есть способ сделать то, что вы хотите; Я уверен, что нет простого способа сделать что-то подобное.

Настраивать

Рассмотрим базу данных с двумя таблицами: таблица (химических) элементов — периодическая таблица — и таблица (США) состояний.

CREATE TABLE elements
(
    atomic_number   INTEGER NOT NULL PRIMARY KEY
                    CHECK (atomic_number > 0 AND atomic_number < 120),
    symbol          CHAR(3) NOT NULL UNIQUE,
    name            CHAR(20) NOT NULL UNIQUE,
    atomic_weight   DECIMAL(8, 4) NOT NULL,
    pt_period       SMALLINT NOT NULL
                    CHECK (pt_period BETWEEN 1 AND 7),
    pt_group        CHAR(2) NOT NULL
                    -- 'L' for Lanthanoids, 'A' for Actinoids
                    CHECK (pt_group IN ('1', '2', 'L', 'A', '3', '4', '5', '6',
                                        '7', '8', '9', '10', '11', '12', '13',
                                        '14', '15', '16', '17', '18')),
    stable          CHAR(1) DEFAULT 'Y' NOT NULL
                    CHECK (stable IN ('Y', 'N'))
);

CREATE TABLE US_States
(
    code    CHAR(2) NOT NULL PRIMARY KEY,
    name    VARCHAR(15) NOT NULL UNIQUE
);

Я предполагаю, что вы можете заполнить две таблицы правильными данными (см. веб-элементы для периодической таблицы; демонстрационная база данных Informix «сохраняет» таблицу state, изоморфную используемой здесь таблице US_States).

Теперь рассмотрим процедуру states_starting():

CREATE FUNCTION states_starting(initial CHAR(1)) RETURNING VARCHAR(15);

    DEFINE result VARCHAR(15);

    FOREACH SELECT Name
              INTO result
              FROM US_States
              WHERE Code[1] = initial
              ORDER BY Name
        RETURN result WITH RESUME;
     END FOREACH;

END FUNCTION;

Адаптация запросов в вопросе

Я был немного удивлен, что нотация vt(result) работает — но она работает, обозначая псевдоним таблицы vt и имя столбца result. Следовательно, адаптация сработавшего запроса:

SELECT *
  FROM Elements, TABLE(states_starting('M')) vt(result)

Это генерирует декартово произведение 118 элементов и 8 состояний с именами, начинающимися с «M», всего для 944 строк. Чуть более разумный запрос:

SELECT *
  FROM Elements JOIN TABLE(states_starting('M')) AS vt(result)
    ON Elements.Symbol[1] = vt.result[1]
 ORDER BY Elements.Atomic_Number

Это генерирует результаты для 6 элементов (магний, марганец, мейтнерий, менделевий, молибден, московий: ртуть не фигурирует, потому что его символ — Hg, который не начинается с буквы М) и 8 состояний, как и раньше, для 48 строк.

Адаптация запроса, который вы хотите выполнить, выглядит примерно так:

SELECT *
  FROM Elements AS e
  JOIN TABLE(states_starting(e.name[1])) AS vt(result)
    ON Elements.Symbol[1] = vt.result[1]
 ORDER BY Elements.Atomic_Number

Однако это не работает, выдавая ошибку:

-217: Column (name) not found in any table in the query (or SLV is undefined).

Это не идентично ошибке в вопросе; У меня не получилось это воспроизвести. Но это симптом проблем, с которыми сталкивается этот запрос.

Проблема в том, что в TABLE(…) имя e неизвестно, но удаление e. из аргумента ничего не меняет.

Кроме того, чтобы сгенерировать полностью правильный результат, выражение TABLE(…) должно быть оценено несколько раз, по одному разу для каждой отдельной начальной буквы. Итак, вам понадобится набор результатов таблицы от нескольких вызовов функции в выражении TABLE(…) с разными аргументами. Но это не то, как таблицы работают в SQL. Они должны быть «фиксированными». Постоянный аргумент функции дает один результирующий набор и выглядит как таблица. Но попытка вызвать его несколько раз с разными аргументами и работа с результирующим набором будет (в лучшем случае) сложной задачей — это не то, как работает SQL.

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

Я пробовал множество вариантов. Например, если вы создаете GROUP_CONCAT агрегат, вы можете попробовать:

SELECT group_concat(states_starting(a))
  FROM (SELECT DISTINCT NAME[1] AS a FROM us_states)
 GROUP BY a

но это генерирует:

-686: Function (states_starting) has returned more than one row.

Я не думаю, что создание SET или LIST из результата функции помогает.


Тестирование в Mac OS X 10.11.5 с Informix 12.10.FC6 (и ClientSDK 4.10.FC6, а также SQLCMD 90.01 — не имеет отношения к одноименному johnny-come-lately от Microsoft).

person Jonathan Leffler    schedule 09.07.2016