Posso creare una vista con parametri in MySQL?


91

Ho una visione come questa:

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

Vorrei renderlo più generico, significa cambiare 2 in una variabile. Ho provato questo:

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

Ma MySQL non lo consente.

Ho trovato una brutta soluzione alternativa:

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

E poi la vista è:

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

Ma sembra davvero scadente, e anche l'uso è scadente: devo impostare @MyVariable prima di ogni utilizzo della vista.

C'è una soluzione che potrei usare in questo modo:

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

La situazione concreta è la seguente: ho una tabella che memorizza le informazioni sulla richiesta rifiutata:

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 molteplicità è un numero di richieste identiche registrate nello stesso secondo. Voglio visualizzare un elenco di rifiuti, ma a volte, quando l'applicazione viene rifiutata, riprova un paio di volte solo per esserne sicuro. Quindi, di solito, quando lo stesso utente viene negato 3 volte sulla stessa funzione in un paio di secondi, in realtà è un rifiuto. Se avessimo una risorsa in più, per soddisfare questa richiesta, i prossimi due dinieghi non accadrebbero. Quindi vogliamo raggruppare i rifiuti in report consentendo all'utente di specificare il periodo di tempo in cui raggruppare i rifiuti. Ad esempio, se abbiamo rifiuti (per l'utente 1 sulla funzione 1) in timestamp: 1,2,24,26,27,45 e l'utente desidera raggruppare i rifiuti che sono più vicini tra loro di 4 secondi, dovrebbe ottenere qualcosa del genere: 1 (x2), 24 (x3), 45 (x1). Possiamo presumere che gli spazi tra le smentite reali siano molto più grandi che tra le duplicazioni.

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;

Quindi per mostrare i dinieghi dell'utente 1 e 2 sulle funzioni 3 e 4 unite ogni 5 secondi, tutto ciò che devi fare è:

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

Uso la vista perché in essa è facile filtrare i dati e utilizzarli esplicitamente nella griglia jQuery, ordinare automaticamente, limitare il numero di record e così via.

Ma è solo una brutta soluzione alternativa. C'è un modo corretto per farlo?

Risposte:


158

In realtà se crei func:

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

e visualizza:

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

Quindi puoi chiamare una vista con un parametro:

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

Spero possa essere d'aiuto.


30
Wow, questa è una delle cose più hacker che abbia mai visto in SQL;) Ma è esattamente quello che volevo fare.
ssobczak

2
Questa tecnica funziona quando si crea una vista all'interno di una stored procedure, quando la vista creata dipende da un varchar passato alla stored procedure. In questo caso, ho dovuto "impostare @ p1 = 12;" sulla linea prima della chiamata per creare la vista.
Clayton Stanley

2
È possibile che si verifichino problemi (confusione dei dati del tenant) se più tenant del database chiamano questo codice contemporaneamente?
Gruber

2
@Mr_and_Mrs_D la tabella derivata necessita di un alias. puoi chiamarlo come vuoi, ma non puoi ometterlo
Robin Kanters

4
La variabile p1 mantiene il suo valore dopo questo, quindi se usi di nuovo la vista senza passare il parametro, userà quello precedente passato - il che potrebbe confondere! Puoi "cancellarlo" dopo che è stato usato in questo modo: seleziona s. * Da (seleziona p1: = 12 p) passa, h_parm s, (seleziona @ p1: = - 1) cancella; (Supponendo che -1 sia un valore non valido per questo scopo)
BuvinJ

21
CREATE VIEW MyView AS
   SELECT Column, Value FROM Table;


SELECT Column FROM MyView WHERE Value = 1;

È la soluzione corretta in MySQL, alcuni altri SQL consentono di definire le visualizzazioni in modo più preciso.

Nota: a meno che la vista non sia molto complicata, MySQL lo ottimizzerà perfettamente.


1
Nel mio caso la parte WHERE, in cui voglio usare il parametro è in neasted select, quindi è impossibile filtrarla dall'esterno della vista.
ssobczak

In realtà le selezioni neasted non sono consentite nelle visualizzazioni, ma le ho divise in due visualizzazioni. V1 filtra e aggrega i dati e sopra V1 c'è V2. Non riesco a filtrare i dati da V1 all'esterno (in V2), perché all'esterno sono visibili come aggregati.
ssobczak

2
Quindi non utilizzare affatto una vista, se hai bisogno di un controllo esatto, crea l'intera query ogni volta o crea la query all'interno di una stored procedure. Il salvataggio come vista sembra inutile. Tuttavia, se pubblichi le domande che stai cercando di raggiungere, qualcuno potrebbe essere in grado di suggerire un percorso diverso / migliore.
MindStalker

Non volevo farlo, perché renderà la mia semplice domanda piuttosto complessa, ma se pensi che possa essere utile, ci proverò.
ssobczak

1

In precedenza ho escogitato una soluzione alternativa diversa che non utilizza stored procedure, ma utilizza invece una tabella dei parametri e un po 'di magia connection_id ().

EDIT (Copiato dai commenti)

crea una tabella che contenga una colonna chiamata connection_id(rendila bigint). Posizionare le colonne in quella tabella per i parametri per la vista. Metti una chiave primaria nel file connection_id. sostituire nella tabella dei parametri e utilizzare CONNECTION_ID()per popolare il valore connection_id. Nella vista utilizzare un cross join alla tabella dei parametri e inserire WHERE param_table.connection_id = CONNECTION_ID(). Questo si unirà a croce con una sola riga dalla tabella dei parametri che è ciò che desideri. È quindi possibile utilizzare le altre colonne nella clausola where, ad esempio where orders.order_id = param_table.order_id.


5
Quale? Per favore, dicci qualcosa di più.
marzapower

1
crea una tabella che contenga una colonna chiamata connection_id (rendila bigint). Posiziona le colonne in quella tabella per i parametri per la vista. Metti una chiave primaria in connection_id. sostituire nella tabella dei parametri e utilizzare CONNECTION_ID () per popolare il valore connection_id. Nella vista utilizzare un cross join alla tabella dei parametri e inserire WHERE param_table.connection_id = CONNECTION_ID (). Questo si unirà a croce con una sola riga dalla tabella dei parametri che è ciò che desideri. È quindi possibile utilizzare le altre colonne nella clausola where, ad esempio where orders.order_id = param_table.order_id.
Justin Swanhart

KLUDGE! Ma carino.
Rick James
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.