Kann ich in MySQL eine Ansicht mit Parametern erstellen?

91

Ich habe eine Ansicht wie diese:

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

Ich möchte es allgemeiner machen, es bedeutet, 2 in eine Variable umzuwandeln. Ich habe es versucht:

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

Aber MySQL erlaubt das nicht.

Ich habe eine hässliche Problemumgehung gefunden:

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

Und dann ist die Ansicht:

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

Aber es sieht wirklich beschissen aus und die Verwendung ist auch beschissen - ich muss die @MyVariable vor jeder Verwendung der Ansicht festlegen.

Gibt es eine Lösung, die ich so verwenden könnte:

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

Die konkrete Situation ist wie folgt: Ich habe eine Tabelle, in der Informationen über die abgelehnte Anfrage gespeichert sind:

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;

Eine Vielzahl ist eine Anzahl identischer Anforderungen, die in derselben Sekunde aufgezeichnet wurden. Ich möchte eine Liste von Ablehnungen anzeigen, aber manchmal, wenn die Anwendung abgelehnt wird, wird sie ein paar Mal wiederholt, nur um sicherzugehen. Wenn also derselbe Benutzer innerhalb weniger Sekunden dreimal die gleiche Funktion verweigert, handelt es sich in der Regel tatsächlich um eine Ablehnung. Wenn wir noch eine Ressource hätten, um diese Anfrage zu erfüllen, würden die nächsten beiden Ablehnungen nicht passieren. Daher möchten wir die Ablehnungen im Bericht gruppieren, damit der Benutzer die Zeitspanne angeben kann, in der Ablehnungen gruppiert werden sollen. Wenn wir beispielsweise in Zeitstempeln Ablehnungen (für Benutzer 1 in Merkmal 1) haben: 1,2,24,26,27,45 und Benutzer Ablehnungen gruppieren möchten, die näher als 4 Sekunden beieinander liegen, sollte er Folgendes erhalten: 1 (x2), 24 (x3), 45 (x1). Wir können davon ausgehen, dass die Zwischenräume zwischen realen Ablehnungen viel größer sind als zwischen Duplikaten.

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;

Um die Ablehnungen von Benutzer 1 und 2 für die Funktionen 3 und 4 anzuzeigen, die alle 5 Sekunden zusammengeführt wurden, müssen Sie lediglich Folgendes tun:

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

Ich verwende die Ansicht, weil es einfach ist, Daten zu filtern und explizit im jQuery-Raster zu verwenden, automatisch zu ordnen, die Anzahl der Datensätze zu begrenzen und so weiter.

Aber es ist nur eine hässliche Problemumgehung. Gibt es einen richtigen Weg, dies zu tun?

ssobczak
quelle

Antworten:

156

Eigentlich, wenn Sie func erstellen:

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

und Ansicht:

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

Dann können Sie eine Ansicht mit einem Parameter aufrufen:

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

Ich hoffe, es hilft.

Leonard Strashnoy
quelle
30
Wow, das ist eines der hackigsten Dinge, die ich je in SQL gesehen habe;) Aber genau das wollte ich tun.
ssobczak
2
Diese Technik funktioniert beim Erstellen einer Ansicht innerhalb einer gespeicherten Prozedur, wenn die erstellte Ansicht von einem Varchar abhängt, der an die gespeicherte Prozedur übergeben wird. In diesem Fall musste ich '@ p1 = 12 setzen;' in der Zeile vor dem Aufruf, um die Ansicht zu erstellen.
Clayton Stanley
2
Gibt es ein Potenzial für Probleme (Verwechslung von Mandantendaten), wenn mehrere Datenbankmandanten diesen Code gleichzeitig aufrufen?
Gruber
2
@Mr_and_Mrs_D Die abgeleitete Tabelle benötigt einen Alias. Sie können es nennen, wie Sie wollen, aber Sie können es nicht auslassen
Robin Kanters
4
Die Variable p1 behält danach ihren Wert bei. Wenn Sie also die Ansicht erneut verwenden, ohne den Parameter zu übergeben, wird die zuvor übergebene verwendet - was möglicherweise verwirrend ist! Sie können es "löschen", nachdem es wie folgt verwendet wurde: Wählen Sie s. * Aus (wählen Sie p1: = 12 p) pass, h_parm s, (wählen Sie @ p1: = - 1) clear; (Angenommen, -1 ist ein ungültiger Wert für diesen Zweck)
BuvinJ
21
CREATE VIEW MyView AS
   SELECT Column, Value FROM Table;


SELECT Column FROM MyView WHERE Value = 1;

Ist die richtige Lösung in MySQL, können Sie mit einigen anderen SQLs Ansichten genauer definieren.

Hinweis: Sofern die Ansicht nicht sehr kompliziert ist, wird MySQL dies problemlos optimieren.

MindStalker
quelle
1
In meinem Fall befindet sich der WHERE-Teil, in dem ich Parameter verwenden möchte, in neasted select, sodass es unmöglich ist, ihn von außerhalb der Ansicht zu filtern.
ssobczak
Tatsächlich sind verschachtelte Auswahlen in Ansichten nicht zulässig, aber ich habe sie in zwei Ansichten aufgeteilt. V1 filtert und aggregiert Daten, und über V1 befindet sich V2. Ich kann keine Daten von V1 außerhalb (in V2) filtern, da sie außerhalb als aggregiert sichtbar sind.
ssobczak
2
Verwenden Sie dann überhaupt keine Ansicht, wenn Sie die genaue Abfrage jedes Mal genau steuern oder die Abfrage in einer gespeicherten Prozedur erstellen müssen. Das Speichern als Ansicht erscheint sinnlos. Wenn Sie jedoch die Fragen stellen, die Sie erreichen möchten, kann jemand möglicherweise eine andere / bessere Route vorschlagen.
MindStalker
Ich wollte das nicht tun, weil es meine einfache Frage ziemlich komplex macht, aber wenn Sie denken, dass es nützlich sein könnte, werde ich es versuchen.
ssobczak
1

Ich habe mir zuvor eine andere Problemumgehung ausgedacht, bei der keine gespeicherten Prozeduren verwendet werden, sondern stattdessen eine Parametertabelle und etwas Magie von connection_id () verwendet werden.

BEARBEITEN (aus Kommentaren kopiert)

Erstellen Sie eine Tabelle, die eine Spalte mit dem Namen connection_id(machen Sie sie zu einem Bigint) enthält. Platzieren Sie in dieser Tabelle Spalten für Parameter für die Ansicht. Legen Sie einen Primärschlüssel auf die connection_id. In die Parametertabelle ersetzen und CONNECTION_ID()zum Auffüllen des Wertes connection_id verwenden. Verwenden Sie in der Ansicht einen Cross-Join zur Parametertabelle und setzen Sie WHERE param_table.connection_id = CONNECTION_ID(). Dadurch wird die Verknüpfung mit nur einer Zeile aus der Parametertabelle gekreuzt, was Sie möchten. Sie können dann die anderen Spalten in der where-Klausel verwenden, z. B. where orders.order_id = param_table.order_id.

Justin Swanhart
quelle
5
Welcher? Bitte erzählen Sie uns etwas mehr.
Marzapower
1
Erstellen Sie eine Tabelle, die eine Spalte mit dem Namen connection_id enthält (machen Sie sie zu einem Bigint). Platzieren Sie in dieser Tabelle Spalten für Parameter für die Ansicht. Geben Sie einen Primärschlüssel in die connection_id ein. Ersetzen Sie ihn in die Parametertabelle und füllen Sie den Wert connection_id mit CONNECTION_ID (). Verwenden Sie in der Ansicht einen Cross-Join zur Parametertabelle und setzen Sie WHERE param_table.connection_id = CONNECTION_ID (). Dadurch wird die Verknüpfung mit nur einer Zeile aus der Parametertabelle gekreuzt, was Sie möchten. Sie können dann die anderen Spalten in der where-Klausel verwenden, z. B. "orders.order_id = param_table.order_id".
Justin Swanhart
KLUDGE! Aber süß.
Rick James