2010-02-17 4 views
71

Ho una visione come questa:Posso creare una vista con parametro in MySQL?

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 consente questo.

Ho trovato una brutta soluzione:

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

E poi vista è:

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

ma sembra davvero scadente, e l'uso è anche scadente - devo impostare il @MyVariable prima 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 la memorizzazione di informazioni su richiesta negata:

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; 

molteplicità è numero di richieste identiche registrate nello stesso secondo. Voglio visualizzare un elenco di smentite, ma a volte, quando l'applicazione viene negata, riprova un paio di volte solo per essere sicuro. Di solito, quando lo stesso utente rifiuta 3 volte la stessa funzione in pochi secondi, in realtà è un rifiuto. Se avessimo una risorsa in più, per soddisfare questa richiesta, i prossimi due dinieghi non accadranno. Pertanto, desideriamo raggruppare i dinieghi nel rapporto che consentono all'utente di specificare l'intervallo di tempo in cui i dinieghi dovrebbero essere raggruppati. Per esempio. se abbiamo smentite (per l'utente 1 sulla funzione 1) in data e ora: 1,2,24,26,27,45 e l'utente vuole raggruppare le smentite che sono più vicine tra loro di 4 sec, dovrebbe ottenere qualcosa di simile a questo: 1 (x2), 24 (x3), 45 (x1). Possiamo supporre che gli spazi tra smentite reali siano molto più grandi che tra duplicazioni. Ho risolto il problema in modo seguente:

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; 

Poi per mostrare smentite da utente 1 e 2 sulle caratteristiche 3 e 4 si è fusa ogni 5 secondi tutto quello che dovete fare è:

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

Io uso vista , perché in esso è facile filtrare i dati e usarli esplicitamente nella griglia jQuery, ordinare automaticamente, limitare il numero di record e così via.

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

risposta

115

In realtà se si crea func:

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

e panorama:

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

quindi è possibile chiamare una vista con un parametro:

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

spero che aiuta.

+21

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

+0

Non dovrebbe essere NON DETERMINISTICO? – MosheElisha

+0

@MichaelMior I documenti MySQL dicono: "Una routine è considerata" deterministica "se produce sempre lo stesso risultato per gli stessi parametri di input, e" non deterministica "in caso contrario: se non viene definito DETERMINISTICO o NON DETERMINISTICO nella definizione di routine, l'impostazione predefinita NON è DETERMINISTICA. Per dichiarare che una funzione è deterministica, è necessario specificare DETERMINISTIC in modo esplicito. " - come ho capito, non è solo durante l'esecuzione della visualizzazione. – MosheElisha

18
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 viste in modo più preciso.

Nota: A meno che la vista non sia molto complicata, MySQL ottimizzerà questo aspetto.

+0

Nel mio caso la parte WHERE, in cui voglio utilizzare il parametro è selezionata in neasted, quindi non è possibile filtrarla dall'esterno della vista. – ssobczak

+0

I selezioni effettivamente non selezionati non sono consentiti nelle viste, ma li ho divisi in due viste. V1 filtra e aggrega i dati, e sopra a V1 c'è V2. Non posso filtrare i dati da V1 al di fuori di esso (in V2), perché all'esterno sono visibili come aggregati. – ssobczak

+1

Quindi non utilizzare affatto una vista, se è necessario il controllo esatto, generare l'intera query ogni volta oppure creare la query all'interno di una stored procedure. Salvare come una vista sembra inutile. Anche se pubblichi le domande che stai cercando di raggiungere qualcuno, potresti essere in grado di suggerire un percorso diverso/migliore. – MindStalker

1

In precedenza avevo trovato una soluzione alternativa che non utilizzava stored procedure, ma utilizzava invece una tabella dei parametri e qualche magic_id_connessione().

EDIT (Copiato dal commento)

creare una tabella che contiene una colonna chiamata connection_id (ne fanno un bigint). Posiziona le colonne in quella tabella per i parametri per la vista. Inserisci una chiave primaria su connection_id. sostituirlo 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 attraverserà il join con una sola riga dalla tabella dei parametri che è ciò che vuoi. È quindi possibile utilizzare le altre colonne nella clausola where, ad esempio, dove orders.order_id = param_table.order_id.

+5

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

+1

crea una tabella che contiene una colonna chiamata connection_id (rendi un bigint). Posiziona le colonne in quella tabella per i parametri per la vista. Metti una chiave primaria su 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 attraverserà il join con una sola riga dalla tabella dei parametri che è ciò che vuoi. È quindi possibile utilizzare le altre colonne nella clausola where, ad esempio, dove orders.order_id = param_table.order_id. –

+0

KLUDGE! Ma carino. –