我可以在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拒绝功能5和3合并的功能3和4,您要做的就是:

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

我使用视图是因为它很容易过滤数据并在jQuery网格中显式使用它,自动排序,限制记录数等。

但这只是一个丑陋的解决方法。有适当的方法来做到这一点吗?

Answers:


158

实际上,如果您创建func:

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;” 在调用创建视图之前的行上。
克莱顿·斯坦利

2
如果多个数据库租户同时调用此代码,是否有可能出现问题(租户数据混合)?
Gruber

2
@Mr_and_Mrs_D派生表需要一个别名。您可以随心所欲地调用它,但不能忽略它
Robin Kanters

4
此后,变量p1保留其值,因此,如果您再次使用该视图而不传递参数,它将使用先前传递的视图-可能会造成混淆!您可以像这样使用后“清除”它:从(select p1:= 12 p)pass中选择s。*,h_parm s,(select @ p1:=-1)clear; (假设-1为此无效值)
BuvinJ

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


SELECT Column FROM MyView WHERE Value = 1;

是MySQL中的正确解决方案,其他一些SQL则使您可以更精确地定义视图。

注意:除非View非常复杂,否则MySQL会对其进行优化。


1
就我而言,我要在其中使用参数的WHERE部分位于必需的select中,因此无法从视图外部对其进行过滤。
ssobczak

视图中实际上不允许进行选择,但是我将它们分为两个视图。V1过滤并汇总数据,并且在V1的顶部是V2。我无法从V1外部(在V2中)过滤数据,因为它们在外部可见为聚合数据。
ssobczak

2
然后,根本不需要使用视图,如果您需要精确控制,则每次都构建整个查询,或者在存储过程中构建查询。保存为视图似乎毫无意义。尽管如果您发布查询,您试图实现的目标也许可以建议其他/更好的路线。
MindStalker 2010年

我不想这样做,因为这会使我的简单问题变得非常复杂,但是如果您认为这可能有用,我将尝试。
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 order.order_id = param_table.order_id。
贾斯汀·斯旺哈特

克鲁德!但是可爱。
瑞克·詹姆斯
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.