Чи можу я створити подання з параметром у MySQL?


91

У мене такий вигляд:

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

Я хотів би зробити його більш загальним, це означає змінити 2 на змінну. Я спробував це:

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

Але MySQL цього не дозволяє.

Я знайшов некрасивий обхідний шлях:

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

І тоді вигляд такий:

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

Але це виглядає по-справжньому безглуздо, і використання також є безглуздим - я повинен встановлювати @MyVariable перед кожним використанням подання.

Чи є рішення, яке я міг би використати таким чином:

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

Конкретна ситуація така: у мене є таблиця, в якій зберігається інформація про відхилений запит:

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;

Кратність - це кількість однакових запитів, записаних в одну секунду. Я хочу відобразити список відмов, але іноді, коли програмі відмовляють, вона повторює пару разів, щоб переконатися. Тому зазвичай, коли один і той самий користувач отримує відмову 3 рази на одній і тій же функції за пару секунд, це насправді один відмова. Якби у нас був ще один ресурс, щоб виконати цей запит, наступні два відмови не відбулися б. Отже, ми хочемо згрупувати відмови у звіті, дозволяючи користувачеві вказати проміжок часу, в якому відмовлення слід групувати. Наприклад, якщо у нас є відмови (для користувача 1 за функцією 1) у позначках часу: 1,2,24,26,27,45, і користувач хоче згрупувати відмови, які знаходяться ближче один до одного, ніж 4 секунди, він повинен отримати щось подібне: 1 (x2), 24 (x3), 45 (x1). Можна припустити, що пробілів між реальними запереченнями набагато більше, ніж між дублюваннями.

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;

Потім, щоб показати відмови від користувачів 1 та 2 щодо об’єднаних функцій 3 та 4 кожні 5 секунд, потрібно лише:

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

Я використовую подання, оскільки в ньому легко фільтрувати дані та явно використовувати їх у сітці jQuery, автоматично впорядковувати, обмежувати кількість записів тощо.

Але це просто потворне обхідне рішення. Чи є правильний спосіб зробити це?

Відповіді:


158

Насправді, якщо ви створюєте функцію:

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

і переглянути:

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

Тоді ви можете викликати подання з параметром:

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

Сподіваюся, це допоможе.


30
Ого, це одне з найбільш хакі, що я коли-небудь бачив у SQL;) Але це саме те, що я хотів зробити.
ssobczak

2
Цей прийом працює під час створення подання в межах збереженої процедури, коли створений подання залежить від varchar, переданого збереженій процедурі. У цьому випадку мені довелося 'встановити @ p1 = 12;' на рядку перед дзвінком, щоб створити представлення.
Clayton Stanley

2
Чи є якісь проблеми (змішування даних орендаря), якщо кілька орендарів баз даних одночасно викликають цей код?
Gruber

2
@Mr_and_Mrs_D похідна таблиця потребує псевдонім. ви можете називати це як завгодно, але не можете пропустити
Робін Кантерс,

4
Змінна p1 зберігає своє значення після цього, тому, якщо ви знову використовуєте подання, не передаючи параметр, він буде використовувати попередній переданий - що може заплутати! Ви можете "очистити" його після використання таким чином: виберіть s. * З (виберіть p1: = 12 p) передайте, h_parm s, (select @ p1: = - 1) очистіть; (Припускаючи, що для цієї мети значення -1 є недійсним)
BuvinJ

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


SELECT Column FROM MyView WHERE Value = 1;

Це правильне рішення в MySQL, деякі інші SQL дозволяють точніше визначати подання.

Примітка. Якщо вигляд не дуже складний, MySQL оптимізує це.


1
У моєму випадку частина WHERE, в якій я хочу використовувати параметр, знаходиться у вкладеному виділенні, тому неможливо відфільтрувати його ззовні зору подання.
ssobczak

Насправді акуратні виділення заборонені для переглядів, але я розділив їх на два перегляди. V1 фільтрує та агрегує дані, а поверх V1 є V2. Я не можу фільтрувати дані з V1 поза ним (у V2), оскільки зовні вони видно як агреговані.
ssobczak

2
Тоді взагалі не використовуйте подання, якщо вам потрібен точний контроль, кожен раз будуйте весь запит або будуйте запит усередині збереженої процедури. Зберігати як погляд здається безглуздим. Хоча, якщо ви розміщуєте запити, яких ви намагаєтесь досягти, хтось може запропонувати інший / кращий маршрут.
MindStalker

Я хотів цього не робити, оскільки це зробить моє просте запитання досить складним, але якщо ви вважаєте, що це може бути корисним, я спробую.
ssobczak

1

Раніше я придумав інший обхідний шлях, який не використовує збережені процедури, а замість цього використовує таблицю параметрів і деяку магію connection_id ().

РЕДАГУВАТИ (скопійовано з коментарів)

створити таблицю, яка містить стовпець з назвою connection_id(зробіть його bigint). Розмістіть у цій таблиці стовпці для параметрів подання. Покладіть первинний ключ на connection_id. замінити в таблицю параметрів і використовувати CONNECTION_ID()для заповнення значення connection_id. У поданні використовуйте перехресне приєднання до таблиці параметрів і ставите WHERE param_table.connection_id = CONNECTION_ID(). Це призведе до перехресного об’єднання лише з одним рядком з таблиці параметрів, що саме вам потрібно. Потім ви можете використовувати інші стовпці у реченні where, наприклад where orders.order_id = param_table.order_id.


5
Який? Будь ласка, розкажіть нам дещо більше.
marzapower

1
створити таблицю, яка містить стовпець з назвою connection_id (зробіть його bigint). Розмістіть у цій таблиці стовпці для параметрів подання. Помістіть первинний ключ на connection_id. замініть у таблицю параметрів та використовуйте CONNECTION_ID () для заповнення значення connection_id. У поданні використовуйте перехресне приєднання до таблиці параметрів і поставте WHERE param_table.connection_id = CONNECTION_ID (). Це призведе до перехресного з’єднання лише з одним рядком з таблиці параметрів, що саме вам потрібно. Потім ви можете використовувати інші стовпці в реченні where, наприклад, where orders.order_id = param_table.order_id.
Джастін Суонхарт,

КЛУДЖ! Але мило.
Рік Джеймс,
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.