Могу ли я создать представление с параметром в MySQL?

93

У меня такое мнение:

CREATE VIEW MyView AS
   SELECT Column FROM Table WHERE Value = 2;

Я хотел бы сделать его более общим, это означает преобразование 2 в переменную. Я пробовал это:

CREATE VIEW MyView AS
   SELECT Column FROM Table WHERE Value = @MyVariable;

Но MySQL этого не позволяет.

Я нашел уродливое решение:

CREATE FUNCTION GetMyVariable() RETURNS INTEGER DETERMINISTIC NO SQL
BEGIN RETURN @MyVariable; END|

И тогда вид такой:

CREATE VIEW MyView AS
   SELECT Column FROM Table WHERE Value = GetMyVariable();

Но это выглядит действительно дрянно, и использование также дрянное - я должен устанавливать @MyVariable перед каждым использованием представления.

Есть ли решение, которое я мог бы использовать вот так:

SELECT Column FROM MyView(2) WHERE (...)

Конкретная ситуация такова: у меня есть таблица, в которой хранится информация об отклоненном запросе:

CREATE TABLE Denial
(
    Id INTEGER UNSIGNED AUTO_INCREMENT,
        PRIMARY KEY(Id),
    DateTime DATETIME NOT NULL,
    FeatureId MEDIUMINT UNSIGNED NOT NULL,
        FOREIGN KEY (FeatureId)
            REFERENCES Feature (Id)
            ON UPDATE CASCADE ON DELETE RESTRICT,
    UserHostId MEDIUMINT UNSIGNED NOT NULL,
        FOREIGN KEY (UserHostId)
            REFERENCES UserHost (Id)
            ON UPDATE CASCADE ON DELETE RESTRICT,
    Multiplicity MEDIUMINT UNSIGNED NOT NULL DEFAULT 1,
    UNIQUE INDEX DenialIndex (FeatureId, DateTime, UserHostId)
) ENGINE = InnoDB;

Кратность - это количество идентичных запросов, записанных за одну секунду. Я хочу отобразить список отказов, но иногда, когда приложение получает отказ, оно повторяет попытку пару раз, чтобы убедиться. Обычно, когда один и тот же пользователь получает отказ 3 раза на одной и той же функции за пару секунд, на самом деле это один отказ. Если бы у нас был еще один ресурс для выполнения этого запроса, следующих двух отказов бы не произошло. Итак, мы хотим сгруппировать отказы в отчете, позволяя пользователю указать временной интервал, в который должны быть сгруппированы отказы. Например, если у нас есть отказы (для пользователя 1 на функции 1) в метках времени: 1,2,24,26,27,45 и пользователь хочет сгруппировать отказы, расположенные ближе друг к другу, чем на 4 секунды, он должен получить что-то вроде этого: 1 (х2), 24 (х3), 45 (х1). Можно предположить, что промежутки между реальными отказами намного больше, чем между дублированием.

CREATE FUNCTION GetDenialMergingTime()
    RETURNS INTEGER UNSIGNED
    DETERMINISTIC NO SQL
BEGIN
    IF ISNULL(@DenialMergingTime) THEN
        RETURN 0;
    ELSE
        RETURN @DenialMergingTime;
    END IF;
END|

CREATE VIEW MergedDenialsViewHelper AS
    SELECT MIN(Second.DateTime) AS GroupTime,
        First.FeatureId,
        First.UserHostId,
        SUM(Second.Multiplicity) AS MultiplicitySum
    FROM Denial AS First 
        JOIN Denial AS Second 
            ON First.FeatureId = Second.FeatureId
                AND First.UserHostId = Second.UserHostId
                AND First.DateTime >= Second.DateTime
                AND First.DateTime - Second.DateTime < GetDenialMergingTime()
    GROUP BY First.DateTime, First.FeatureId, First.UserHostId, First.Licenses;

CREATE VIEW MergedDenials AS
    SELECT GroupTime, 
        FeatureId,
        UserHostId, 
        MAX(MultiplicitySum) AS MultiplicitySum
    FROM MergedDenialsViewHelper
    GROUP BY GroupTime, FeatureId, UserHostId;

Затем, чтобы отображать отказы от пользователя 1 и 2 на функциях 3 и 4, объединяемых каждые 5 секунд, все, что вам нужно сделать, это:

SET @DenialMergingTime := 5;
SELECT GroupTime, FeatureId, UserHostId, MultiplicitySum FROM MergedDenials WHERE UserHostId IN (1, 2) AND FeatureId IN (3, 4);

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

Но это просто уродливый обходной путь. Есть ли правильный способ сделать это?

ссобчак
источник

Ответы:

162

На самом деле, если вы создаете func:

create function p1() returns INTEGER DETERMINISTIC NO SQL return @p1;

и посмотреть:

create view h_parm as
select * from sw_hardware_big where unit_id = p1() ;

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

select s.* from (select @p1:=12 p) parm , h_parm s;

Я надеюсь, что это помогает.

Леонард Страшной
источник
31
Вау, это одна из самых хитрых вещей, которые я когда-либо видел в SQL;) Но это именно то, что я хотел сделать.
ssobczak
2
Этот метод работает при создании представления внутри хранимой процедуры, когда созданное представление зависит от varchar, переданного хранимой процедуре. В этом случае мне пришлось «установить @ p1 = 12;» в строке перед вызовом, чтобы создать представление.
Clayton Stanley
2
Есть ли вероятность возникновения проблем (смешение данных клиента), если несколько клиентов базы данных вызывают этот код одновременно?
Gruber
2
@Mr_and_Mrs_D производной таблице нужен псевдоним. Вы можете называть это как хотите, но вы не можете пропустить это
Робин Кантерс
4
После этого переменная p1 сохраняет свое значение, поэтому, если вы снова используете представление без передачи параметра, оно будет использовать предыдущее переданное, что может сбивать с толку! Вы можете "очистить" его после использования следующим образом: select s. * From (select p1: = 12 p) pass, h_parm s, (select @ p1: = - 1) clear; (Предполагая, что -1 - недопустимое значение для этой цели)
BuvinJ
21
CREATE VIEW MyView AS
   SELECT Column, Value FROM Table;


SELECT Column FROM MyView WHERE Value = 1;

Это подходящее решение в MySQL, некоторые другие SQL позволяют более точно определять представления.

Примечание: если представление не очень сложное, MySQL отлично его оптимизирует.

MindStalker
источник
1
В моем случае часть WHERE, в которой я хочу использовать параметр, находится в neasted select, поэтому его невозможно отфильтровать извне представления.
ssobczak
На самом деле обработанные выборки не допускаются в представлениях, но я разделил их на два представления. V1 фильтрует и агрегирует данные, а поверх V1 находится V2. Я не могу фильтровать данные из V1 вне его (в V2), потому что снаружи они видны как агрегированные.
ssobczak
2
Тогда вообще не используйте представление, если вам нужен точный контроль, каждый раз создавайте весь запрос или создавайте запрос внутри хранимой процедуры. Сохранять как вид кажется бессмысленным. Хотя, если вы публикуете запросы, которые пытаетесь выполнить, кто-то может предложить другой / лучший маршрут.
MindStalker
Я не хотел этого делать, потому что это сделает мой простой вопрос довольно сложным, но если вы думаете, что это может быть полезно, я попробую.
ssobczak
Этот формат не позволит вам изменить набор результатов или имя таблицы в зависимости от параметра
MMEL
1

Ранее я придумал другой обходной путь, который не использует хранимые процедуры, а вместо этого использует таблицу параметров и некоторую магию connection_id ().

РЕДАКТИРОВАТЬ (скопировано из комментариев)

создайте таблицу, содержащую столбец с именем connection_id(сделайте его bigint). Поместите столбцы в эту таблицу для параметров представления. Поместите первичный ключ в connection_id. заменить в таблицу параметров и использовать CONNECTION_ID()для заполнения значения connection_id. В представлении используйте перекрестное соединение с таблицей параметров и поместите WHERE param_table.connection_id = CONNECTION_ID(). Это приведет к перекрестному соединению только с одной строкой из таблицы параметров, которую вы хотите. Затем вы можете использовать другие столбцы в предложении where, например where orders.order_id = param_table.order_id.

Джастин Свонхарт
источник
5
Который из? Расскажите, пожалуйста, еще кое-что.
marzapower
1
создайте таблицу, содержащую столбец с именем connection_id (сделайте его bigint). Поместите столбцы в эту таблицу для параметров представления. Поместите первичный ключ в connection_id. заменить в таблице параметров и использовать CONNECTION_ID () для заполнения значения connection_id. В представлении используйте перекрестное соединение с таблицей параметров и поместите WHERE param_table.connection_id = CONNECTION_ID (). Это приведет к перекрестному соединению только с одной строкой из таблицы параметров, которую вы хотите. Затем вы можете использовать другие столбцы в предложении where, например, где orders.order_id = param_table.order_id.
Джастин Суонхарт
КЛУДЖ! Но мило.
Рик Джеймс