Как использовать вывод таблицы из сохраненной процедуры MYSQL

Я искал последний час или около того и не нашел окончательного ответа на эту, казалось бы, простую проблему:

Как вы вызываете сохраненную функцию/процедуру MYSQL и используете ее вывод в дальнейших запросах SELECT?


Хотя это, очевидно, не работает, это то, что я хотел бы иметь:

SELECT P.`id` FROM (CALL test_proc()) AS P

Где test_proc() определяется:

DROP PROCEDURE IF EXISTS test_proc;
DELIMITER ;;
CREATE PROCEDURE test_proc()
BEGIN
    SELECT * FROM `table`;
END;;
DELIMITER ;

Просто как пример. Я был бы в порядке с использованием хранимой функции.


person Johannes    schedule 30.07.2013    source источник


Ответы (3)


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

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

Добавьте это в процедуру:

DROP TEMPORARY TABLE IF EXISTS foo;
CREATE TEMPORARY TABLE foo SELECT ... your existing select query here ...;

Когда ваша процедура завершится, SELECT * FROM foo; даст вам то, что вы получили бы от процедуры. Вы можете присоединиться к нему почти так же, как к любому столу.

Когда вы закончите, бросьте его, иначе он исчезнет сам по себе, когда вы отключитесь. Если вы снова запустите процедуру, она будет удалена и создана заново.

person Michael - sqlbot    schedule 01.08.2013
comment
Cпасибо, это именно то, что я искал :) - person Johannes; 01.08.2013
comment
Как я могу вызвать test_proc() при создании временной таблицы. Я хочу сохранить логику в SP отдельно и не хочу смешивать операторы выбора с временной таблицей. - person Himalaya Garg; 09.02.2017
comment
@HimalayaGarg, пожалуйста, объясните, что вы имеете в виду при создании — вы имеете в виду что-то вроде CREATE TEMPORARY TABLE t1 USING RESULT FROM CALL test_proc()? Извините, я только что это выдумал. Нет такого синтаксиса. Пожалуйста, объясните, что вы пытаетесь сделать. - person Michael - sqlbot; 11.02.2017
comment
@Michael-sqlbot Да, вы правильно поняли. Я хочу что-то подобное, как вы написали выше. Есть ли способ создать временную таблицу, вызвав SP. Я хочу использовать результаты ИП (не могу изменить ИП, так как он находится в другой БД, не администрируемой мной) - person Himalaya Garg; 11.02.2017
comment
Нет, это невозможно. Когда хранимая процедура возвращает набор результатов, который выглядит как таблица, этот результат фактически обходит некоторые внутренние механизмы MySQL Server, и результат просто записывается подключенному клиенту. Вот почему вы не можете делать такие вещи, как CALL test_proc() WHERE ... LIKE ... LIMIT ... и т. д. Вы можете только захватить этот вывод из клиентского соединения, а затем написать какой-нибудь Perl/Python/PHP/Ruby/что-то еще, чтобы отправить его обратно на сервер, создав операторы вставки для вставки полученные значения в созданную вами временную таблицу. - person Michael - sqlbot; 11.02.2017

Вот конкретный пример в MySql 8, основанный на предложении @Michael-sqlbot:

-- Create some test data
drop table if exists someData ;
create table someData (itemId int, itemName varcharacter(20), itemQty int, itemCat int) ;
insert into someData values
(1,'First', 9, 1)
,(2,'Second',50, 3)
,(3,'Third', 12, 5)
,(4,'Fourth', 7, 3)
,(5,'Fifth', 1, 2)
,(6,'Sixth', 1, 1)
,(7,'Seventh', 9, 3)
,(8,'Eighth', 0, 2)
;

-- Just checking that it's all there!
select * from someData ;

-- Define the proc
delimiter //
drop procedure if exists prcSomeData //
create procedure prcSomeData()
    comment 'Create a temporary table called "tmpSomeData"'
begin
    drop table if exists tmpSomeData ;
    create temporary table tmpSomeData as
        select itemCat
            , sum(itemQty) as 'itemsTot'
            , min(itemQty) as 'lowestQty'
            , max(itemQty) as 'highestQty'
            from someData
            group by itemCat
            order by itemCat
        ;
end //
delimiter ;

-- Gotta run the proc to instantiate the temp table
call prcSomeData() ;        -- produces a temporary table "tmpSomeData", exists for the session
-- Now it's usable
select * from tmpSomedata ;

Что производит:

itemCat|itemsTot|lowestQty|highestQty|
-------|--------|---------|----------|
      1|      10|        1|         9|
      2|       1|        0|         1|
      3|      66|        7|        50|
      5|      12|       12|        12|
person sinecospi    schedule 07.09.2019

Еще лучшим решением является создание временной таблицы вне хранимой процедуры, и процедура просто обновляет/вставляет в нее. Таким образом, результаты нескольких вызовов могут быть сохранены в одной таблице (при условии, что вставлены одни и те же столбцы) и извлечены в конце с помощью простого SELECT.

Вот пример с использованием тех же данных, что и #sinecospi выше:

/* The data */
drop table if exists someData ;
create table someData (itemId int, itemName varchar(20), itemQty int, itemCat int) ;
insert into someData values
(1,'First', 9, 1)
,(2,'Second',50, 3)
,(3,'Third', 12, 5)
,(4,'Fourth', 7, 3)
,(5,'Fifth', 1, 2)
,(6,'Sixth', 1, 1)
,(7,'Seventh', 9, 3)
,(8,'Eighth', 0, 2);

/* The stored procedure */
DROP PROCEDURE IF EXISTS sproc;

DELIMITER //

CREATE PROCEDURE sproc(
  IN fld CHAR(64),
  IN tbl CHAR(64)
)

BEGIN
  SET @s = CONCAT('INSERT INTO results
    SELECT \'',fld,'\' AS fld, x.* FROM (
      SELECT ',fld,' AS bin, COUNT(',fld,') AS ct 
          FROM ',tbl,' 
          GROUP BY bin 
          ORDER BY bin) x;');
  PREPARE stmt FROM @s;
  EXECUTE stmt;
  DEALLOCATE PREPARE stmt;
  
END //

DELIMITER ;

/* Calls */

CREATE TEMPORARY TABLE IF NOT EXISTS results (
  fld VARCHAR(30), bin VARCHAR(30), ct FLOAT);


CALL sproc('itemQty','someData');
CALL sproc('itemCat','someData');

SELECT * FROM results;

В итоге временная таблица results имеет результат 2 вызовов.

person Michal J Figurski    schedule 16.10.2020