¿Puedo crear una vista con parámetro en MySQL?

91

Tengo una vista como esta:

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

Me gustaría hacerlo más genérico, significa cambiar 2 en una variable. Probé esto:

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

Pero MySQL no permite esto.

Encontré una fea solución:

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

Y luego la vista es:

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

Pero se ve realmente horrible, y el uso también es malo: tengo que configurar @MyVariable antes de cada uso de la vista.

¿Hay una solución que pueda usar así?

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

La situación concreta es la siguiente: tengo una tabla que almacena información sobre la solicitud denegada:

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;

Una multiplicidad es una cantidad de solicitudes idénticas registradas en el mismo segundo. Quiero mostrar una lista de rechazos, pero a veces, cuando se rechaza la solicitud, vuelve a intentarlo un par de veces solo para asegurarme. Por lo general, cuando el mismo usuario recibe una denegación 3 veces en la misma función en un par de segundos, en realidad es una denegación. Si tuviéramos un recurso más, para cumplir con esta solicitud, las siguientes dos denegaciones no sucederían. Por lo tanto, queremos agrupar las denegaciones en un informe, lo que permite al usuario especificar el período de tiempo en el que se deben agrupar las denegaciones. Por ejemplo, si tenemos denegaciones (para el usuario 1 en la función 1) en las marcas de tiempo: 1, 2, 24, 26, 27, 45 y el usuario desea agrupar las denegaciones que están más cerca entre sí que 4 segundos, debería obtener algo como esto: 1 (x2), 24 (x3), 45 (x1). Podemos suponer que los espacios entre negaciones reales son mucho más grandes que entre duplicaciones.

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;

Luego, para mostrar las denegaciones del usuario 1 y 2 en las funciones 3 y 4 fusionadas cada 5 segundos, todo lo que tiene que hacer es:

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

Utilizo la vista porque en ella es fácil filtrar datos y usarlos explícitamente en la cuadrícula jQuery, ordenar automáticamente, limitar el número de registros, etc.

Pero es solo una fea solución. ¿Existe una forma adecuada de hacer esto?

ssobczak
fuente

Respuestas:

158

En realidad, si creas func:

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

y ver:

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

Entonces puede llamar a una vista con un parámetro:

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

Espero que ayude.

Leonard Strashnoy
fuente
30
Vaya, esta es una de las cosas más hacky que he visto en SQL;) Pero es exactamente lo que quería hacer.
ssobczak
2
Esta técnica funciona cuando se crea una vista dentro de un procedimiento almacenado, cuando la vista creada depende de un varchar pasado al procedimiento almacenado. En este caso, tuve que 'establecer @ p1 = 12;' en la línea antes de la llamada para crear la vista.
Clayton Stanley
2
¿Existe algún problema potencial (confusión de datos de inquilinos) si varios inquilinos de la base de datos llaman a este código al mismo tiempo?
Gruber
2
@Mr_and_Mrs_D la tabla derivada necesita un alias. puedes llamarlo como quieras, pero no puedes omitirlo
Robin Kanters
4
La variable p1 retiene su valor después de esto, por lo que si usa la vista nuevamente sin pasar el parámetro, usará el anterior, ¡lo cual puede ser confuso! Puede "borrarlo" después de usarlo así: seleccione s. * De (seleccione p1: = 12 p) pase, h_parm s, (seleccione @ p1: = - 1) borrar; (Suponiendo que -1 es un valor no válido para este propósito)
BuvinJ
21
CREATE VIEW MyView AS
   SELECT Column, Value FROM Table;


SELECT Column FROM MyView WHERE Value = 1;

Es la solución adecuada en MySQL, algunos otros SQL le permiten definir Vistas con mayor precisión.

Nota: A menos que la vista sea muy complicada, MySQL optimizará esto muy bien.

MindStalker
fuente
1
En mi caso, la parte DONDE, en la que quiero usar el parámetro, está en la selección necesaria, por lo que es imposible filtrarla desde fuera de la vista.
ssobczak
En realidad, las selecciones necesarias no se permiten en las vistas, pero las dividí en dos vistas. V1 filtra y agrega datos, y además de V1 está V2. No puedo filtrar datos de V1 fuera de él (en V2), porque fuera de ellos son visibles como agregados.
ssobczak
2
Entonces, no use una vista en absoluto, si necesita un control exacto, cree la consulta completa cada vez, o cree la consulta dentro de un procedimiento almacenado. Ahorrar como vista parece inútil. Aunque si publica las consultas que está tratando de lograr, alguien podría sugerir una ruta diferente / mejor.
MindStalker
No quería hacer esto, porque hará que mi simple pregunta sea bastante compleja, pero si crees que puede ser útil, lo intentaré.
ssobczak
1

Anteriormente se me ocurrió una solución alternativa diferente que no usa procedimientos almacenados, sino que usa una tabla de parámetros y algo de magia connection_id ().

EDITAR (copiado de los comentarios)

cree una tabla que contenga una columna llamada connection_id(hágalo un bigint). Coloque columnas en esa tabla para los parámetros de la vista. Ponga una clave principal en el connection_id. reemplácelo en la tabla de parámetros y utilícelo CONNECTION_ID()para completar el valor de connection_id. En la vista, use una combinación cruzada con la tabla de parámetros y coloque WHERE param_table.connection_id = CONNECTION_ID(). Esto cruzará la unión con solo una fila de la tabla de parámetros que es lo que desea. Luego puede usar las otras columnas en la cláusula where, por ejemplo where orders.order_id = param_table.order_id.

Justin Swanhart
fuente
5
¿Cúal? Cuéntanos algo más.
marzapower
1
cree una tabla que contenga una columna llamada connection_id (conviértala en un bigint). Coloque columnas en esa tabla para los parámetros de la vista. Ponga una clave principal en connection_id. reemplácelo en la tabla de parámetros y use CONNECTION_ID () para completar el valor de connection_id. En la vista, use una combinación cruzada con la tabla de parámetros y coloque WHERE param_table.connection_id = CONNECTION_ID (). Esto cruzará la unión con solo una fila de la tabla de parámetros que es lo que desea. A continuación, puede utilizar las otras columnas en la cláusula where, por ejemplo, where orders.order_id = param_table.order_id.
Justin Swanhart
¡KLUDGE! Pero mono.
Rick James