【问题标题】:Can I create view with parameter in MySQL?我可以在 MySQL 中创建带参数的视图吗?
【发布时间】:2011-01-17 22:07:27
【问题描述】:

我有这样的看法:

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、2、24、26、27、45 中有拒绝(对于功能 1 上的用户 1),并且用户想要将彼此接近 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 网格中显式使用它、自动排序、限制记录数等等。

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

【问题讨论】:

    标签: mysql stored-procedures view parameters


    【解决方案1】:

    其实如果你创建 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;
    

    希望对你有帮助。

    【讨论】:

    • 哇,这是我在 SQL 中见过的最骇人听闻的事情之一;)但这正是我想做的。
    • @MosheElisha 如果视图是您使用此函数的唯一位置,并且您知道该函数在视图中是确定性的,那么指定 DETERMINISTIC 会稍微提高性能,而且它非常安全这样做。我不相信 MySQL 会跨查询缓存函数结果。
    • 当创建的视图依赖于传递给存储过程的 varchar 时,此技术适用于在存储过程中创建视图。在这种情况下,我必须'设置@p1 = 12;'在调用创建视图之前的行。
    • 如果多个数据库租户同时调用此代码,是否会出现问题(租户数据混淆)?
    • 变量 p1 在此之后保留其值,因此如果您再次使用视图而不传递参数,它将使用之前传递的视图 - 这可能会令人困惑!你可以像这样使用它后“清除”它: select s.* from (select p1:=12 p) pass, h_parm s, (select @p1:=-1) clear; (假设 -1 是一个无效值)
    【解决方案2】:
    CREATE VIEW MyView AS
       SELECT Column, Value FROM Table;
    
    
    SELECT Column FROM MyView WHERE Value = 1;
    

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

    注意:除非View非常复杂,否则MySQL会优化这个就好了。

    【讨论】:

    • 在我的情况下,我想使用参数的 WHERE 部分位于嵌套选择中,因此无法从视图外部对其进行过滤。
    • 视图中实际上不允许嵌套选择,但我将它们分成两个视图。 V1 过滤和聚合数据,在 V1 之上还有 V2。我无法过滤来自 V1 外部的数据(在 V2 中),因为在外部它们以聚合的形式可见。
    • 那么根本不要使用视图,如果您需要精确控制每次都构建整个查询,或者在存储过程中构建查询。保存为视图似乎毫无意义。但是,如果您发布您试图实现的查询,某人可能会提出不同/更好的路线。
    • 我不想这样做,因为它会使我的简单问题变得相当复杂,但如果您认为它可能有用,我会尝试。
    • 这种格式不会让您根据参数更改结果集或表的名称
    【解决方案3】:

    我之前想出了一个不同的解决方法,它不使用存储过程,而是使用参数表和一些 connection_id() 魔术。

    编辑(从 cmets 复制)

    创建一个表,其中包含一个名为 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

    【讨论】:

    • 哪一个?请告诉我们更多信息。
    • 创建一个表,其中包含一个名为 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。
    • KLUDGE!但是很可爱。
    猜你喜欢
    • 1970-01-01
    • 2021-05-18
    • 2020-01-12
    • 2021-12-24
    • 2017-12-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2011-03-29
    相关资源
    最近更新 更多