web-dev-qa-db-fra.com

Puis-je créer une vue avec un paramètre dans MySQL?

J'ai une vue comme ça:

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

Je voudrais le rendre plus générique, cela signifie changer 2 en une variable. J'ai essayé ceci:

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

Mais MySQL ne le permet pas.

J'ai trouvé une solution de contournement laide:

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

Et puis la vue est:

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

Mais cela a l'air vraiment nul, et l'utilisation est aussi merdique - je dois régler la variable @MyVariable avant chaque utilisation de la vue.

Existe-t-il une solution que je pourrais utiliser comme ceci:

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

La situation concrète est la suivante: j'ai une table qui stocke des informations sur la demande refusée:

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;

Une multiplicité est un nombre de demandes identiques enregistrées dans la même seconde. Je souhaite afficher une liste de refus, mais parfois, lorsque l'application est refusée, elle réessaye plusieurs fois, simplement pour en être sûr. Donc, généralement, lorsque le même utilisateur obtient le refus 3 fois sur la même fonctionnalité en quelques secondes, il s’agit en réalité d’un seul refus. Si nous avions encore une ressource pour répondre à cette demande, les deux refus suivants ne se produiraient pas. Nous voulons donc regrouper les refus dans un rapport permettant à l'utilisateur de spécifier la période dans laquelle les refus doivent être groupés. Par exemple. si nous avons des refus (pour l'utilisateur 1 sur la caractéristique 1) dans les horodatages: 1, 2, 24, 26, 27, 45 et que l'utilisateur souhaite regrouper les refus qui sont plus proches les uns des autres que 4 secondes, il devrait obtenir quelque chose comme ceci: 1 (x2), 24 (x3), 45 (x1). Nous pouvons supposer que les espaces entre les refus réels sont beaucoup plus grands qu'entre les duplications. J'ai résolu le problème de la manière suivante:

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;

Ensuite, pour afficher les refus de l'utilisateur 1 et 2 sur les fonctionnalités 3 et 4 fusionnées toutes les 5 secondes, il vous suffit de:

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

J'utilise la vue, car elle permet de filtrer facilement les données et de les utiliser explicitement dans la grille jQuery, de les classer automatiquement, de limiter le nombre d'enregistrements, etc.

Mais c'est juste une solution de contournement laide. Y a-t-il une manière appropriée de faire ceci?

82
ssobczak

En fait, si vous créez func:

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

et voir:

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

Ensuite, vous pouvez appeler une vue avec un paramètre:

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

J'espère que ça aide.

135
Leonard Strashnoy
CREATE VIEW MyView AS
   SELECT Column, Value FROM Table;


SELECT Column FROM MyView WHERE Value = 1;

C'est la solution appropriée dans MySQL, d'autres instructions SQL vous permettent de définir les vues plus précisément.

Note: Si la vue n’est pas très compliquée, MySQL l’optimisera parfaitement.

19
MindStalker

Auparavant, je proposais une solution de contournement différente qui n'utilisait pas de procédures stockées, mais utilisait une table de paramètres et une certaine magie connection_id ().

EDIT (copié des commentaires)

créer une table contenant une colonne appelée connection_id (en faire un bigint). Placez les colonnes dans cette table pour les paramètres de la vue. Placez une clé primaire sur le connection_id. replace dans la table de paramètres et utilisez CONNECTION_ID() pour renseigner la valeur connection_id. Dans la vue, utilisez une jointure croisée avec la table de paramètres et mettez WHERE param_table.connection_id = CONNECTION_ID(). Cela fera une jointure croisée avec une seule ligne de la table de paramètres, ce qui est ce que vous voulez. Vous pouvez ensuite utiliser les autres colonnes de la clause where, par exemple, où orders.order_id = param_table.order_id.

1
Justin Swanhart