Czy mogę utworzyć widok z parametrem w MySQL?

93

Mam taki widok:

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

Chciałbym uczynić go bardziej ogólnym, to znaczy zmienić 2 na zmienną. Próbowałem tego:

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

Ale MySQL na to nie pozwala.

Znalazłem brzydkie obejście:

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

A potem widok jest:

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

Ale wygląda to naprawdę kiepsko, a użycie jest również kiepskie - muszę ustawić @MyVariable przed każdym użyciem widoku.

Czy jest jakieś rozwiązanie, którego mógłbym użyć w ten sposób:

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

Konkretna sytuacja jest następująca: Mam tabelę przechowującą informacje o odrzuconym wniosku:

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;

Wielokrotność to liczba identycznych żądań zarejestrowanych w tej samej sekundzie. Chcę wyświetlić listę odmów, ale czasami, gdy aplikacja zostanie odrzucona, ponawia kilka prób, aby się upewnić. Więc zazwyczaj, gdy ten sam użytkownik otrzymuje odmowę 3 razy tej samej funkcji w ciągu kilku sekund, jest to w rzeczywistości jedna odmowa. Gdybyśmy mieli jeszcze jeden zasób, aby spełnić to żądanie, kolejne dwie odmowy nie miałyby miejsca. Dlatego chcemy pogrupować odmowy w raporcie, umożliwiając użytkownikowi określenie przedziału czasu, w którym powinny być grupowane. Np. Jeśli mamy odmowy (dla użytkownika 1 w funkcji 1) w sygnaturach czasowych: 1,2,24,26,27,45 i użytkownik chce zgrupować odmowy, które są bliżej siebie niż 4 sekundy, powinien otrzymać coś takiego: 1 (x2), 24 (x3), 45 (x1). Można założyć, że odstępy między prawdziwymi zaprzeczeniami są znacznie większe niż między duplikatami.

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;

Następnie, aby co 5 sekund wyświetlać odmowy ze strony użytkownika 1 i 2 dotyczące funkcji 3 i 4, wystarczy, że:

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

Używam widoku, ponieważ w nim łatwo jest filtrować dane i używać ich jawnie w siatce jQuery, automatycznie porządkować, ograniczać liczbę rekordów i tak dalej.

Ale to tylko brzydkie obejście. Czy jest na to właściwy sposób?

ssobczak
źródło

Odpowiedzi:

162

Właściwie, jeśli utworzysz func:

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

i zobacz:

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

Następnie możesz wywołać widok z parametrem:

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

Mam nadzieję, że to pomoże.

Leonard Strashnoy
źródło
31
Wow, to jedna z najbardziej hakerskich rzeczy, jakie kiedykolwiek widziałem w SQL;) Ale właśnie to chciałem zrobić.
ssobczak
2
Ta technika działa podczas tworzenia widoku w ramach procedury składowanej, gdy utworzony widok jest zależny od zmiennej varchar przekazanej do procedury składowanej. W tym przypadku musiałem ustawić @ p1 = 12; na linii przed wywołaniem, aby utworzyć widok.
Clayton Stanley
2
Czy istnieje możliwość wystąpienia problemów (pomieszanie danych dzierżawców), jeśli kilku dzierżawców bazy danych wywoła ten kod jednocześnie?
Gruber
2
@Mr_and_Mrs_D tabela pochodna wymaga aliasu. możesz to nazwać, jak chcesz, ale nie możesz tego pominąć
Robin Kanters
4
Zmienna p1 zachowuje po tym swoją wartość, więc jeśli użyjesz widoku ponownie bez przekazywania parametru, użyje on poprzednio przekazanego - co może być mylące! Możesz go "wyczyścić" po jego użyciu w ten sposób: select s. * From (select p1: = 12 p) pass, h_parm s, (select @ p1: = - 1) clear; (Zakładając, że -1 jest niepoprawną wartością do tego celu)
BuvinJ
22
CREATE VIEW MyView AS
   SELECT Column, Value FROM Table;


SELECT Column FROM MyView WHERE Value = 1;

To właściwe rozwiązanie w MySQL, inne SQL pozwalają na dokładniejsze definiowanie widoków.

Uwaga: O ile widok nie jest bardzo skomplikowany, MySQL zoptymalizuje to dobrze.

MindStalker
źródło
1
W moim przypadku część GDZIE, w której chcę użyć parametru, jest w neasted select, więc nie można go filtrować spoza widoku.
ssobczak
W rzeczywistości wybrane elementy nie są dozwolone w widokach, ale podzieliłem je na dwa widoki. V1 filtruje i agreguje dane, a nad V1 jest V2. Nie mogę filtrować danych z V1 poza nim (w V2), ponieważ na zewnątrz są widoczne jako zagregowane.
ssobczak
2
Więc nie używaj w ogóle widoku, jeśli potrzebujesz dokładnej kontroli, zbuduj za każdym razem całe zapytanie lub utwórz zapytanie w procedurze składowanej. Zapisywanie jako widok wydaje się bezcelowe. Chociaż jeśli opublikujesz zapytania, które próbujesz osiągnąć, ktoś może zaproponować inną / lepszą trasę.
MindStalker
Nie chciałem tego robić, ponieważ skomplikuje to moje proste pytanie, ale jeśli uważasz, że może być przydatne, spróbuję.
ssobczak
Ten format nie pozwala na zmianę zestawu wyników lub nazwy tabeli w zależności od parametru
MMEL
1

Wcześniej wymyśliłem inne obejście, które nie używa procedur składowanych, ale zamiast tego używa tabeli parametrów i pewnej magii connection_id ().

EDYTUJ (skopiowane z komentarzy)

utwórz tabelę zawierającą kolumnę o nazwie connection_id(zmień ją na bigint). Umieść kolumny w tej tabeli dla parametrów widoku. Umieść klucz podstawowy na connection_id. zamienić w tabeli parametrów i użyć CONNECTION_ID()do wypełnienia wartości connection_id. W widoku użyj połączenia krzyżowego do tabeli parametrów i wstaw WHERE param_table.connection_id = CONNECTION_ID(). Spowoduje to sprzężenie krzyżowe tylko z jednym wierszem z tabeli parametrów, czego chcesz. Następnie możesz użyć innych kolumn w klauzuli where, na przykład gdzie orders.order_id = param_table.order_id.

Justin Swanhart
źródło
5
Który? Powiedz nam coś więcej.
marzapower
1
utwórz tabelę zawierającą kolumnę o nazwie connection_id (zmień ją na bigint). Umieść kolumny w tej tabeli dla parametrów widoku. Umieść klucz podstawowy na connection_id. zamienić w tabeli parametrów i użyć CONNECTION_ID (), aby wypełnić wartość connection_id. W widoku użyj sprzężenia krzyżowego do tabeli parametrów i wpisz WHERE param_table.connection_id = CONNECTION_ID (). Spowoduje to sprzężenie krzyżowe tylko z jednym wierszem z tabeli parametrów, czego chcesz. Następnie możesz użyć innych kolumn w klauzuli Where, na przykład, gdzie orders.order_id = param_table.order_id.
Justin Swanhart
KLUDGE! Ale urocze.
Rick James,