【问题标题】:How come RAND() is messing up in SQL subquery?为什么 RAND() 在 SQL 子查询中搞砸了?
【发布时间】:2016-10-22 00:16:57
【问题描述】:

我的目标是选择一个随机企业,然后使用该企业的 id 获取他们所有的广告。我从我的查询中得到了意想不到的结果。返回的广告行数始终是我假设的“SELECT id FROM Business ORDER BY RAND() LIMIT 1”的值。我有 3 个商家,只有 1 个商家有广告行(其中 5 个),但它始终显示同一商家的 5 个广告中的 1-3 个。

SELECT * FROM Advertisement WHERE business_id=(SELECT id FROM Business ORDER BY RAND() LIMIT 1) ORDER BY priority

业务表:

广告表:

广告和商业表格的数据:

INSERT INTO `Advertisement` (`id`, `business_id`, `image_url`, `link_url`, `priority`) VALUES
(1, 1, 'http://i64.tinypic.com/2w4ehqw.png', 'https://www.dennys.com/food/burgers-sandwiches/spicy-sriracha-burger/', 1),
(2, 1, 'http://i65.tinypic.com/zuk1w1.png', 'https://www.dennys.com/food/burgers-sandwiches/prime-rib-philly-melt/', 2),
(3, 1, 'http://i64.tinypic.com/8yul3t.png', 'https://www.dennys.com/food/burgers-sandwiches/cali-club-sandwich/', 3),
(4, 1, 'http://i64.tinypic.com/o8fj9e.png', 'https://www.dennys.com/food/burgers-sandwiches/bacon-slamburger/', 4),
(5, 1, 'http://i68.tinypic.com/mwyuiv.png', 'https://www.dennys.com/food/burgers-sandwiches/the-superbird/', 5);

INSERT INTO `Business` (`id`, `name`) VALUES
(1, 'Test Dennys'),
(2, 'Test Business 2'),
(3, 'Test Business 3');

【问题讨论】:

  • 如果我没记错的话,为了随机订购你需要SELECT RAND() AS r .... ORDER BY r;否则,ORDER BY RAND() 按来自RAND() 的单个结果排序,类似于ORDER BY 1ORDER BY NOW()当然,使用该子查询会产生问题。
  • 我不确定我是否理解。 “SELECT id FROM Business ORDER BY RAND() LIMIT 1”在单独使用时似乎可以正常工作(在我的情况下,对于 3 个企业返回 1 到 3 之间的数字)。我不明白为什么这个号码没有正确分配给子查询之外的 business_id。
  • 能否请您发布您正在尝试查询的数据,因为我已尝试从广告中返回 5 of 5。
  • 您可以发布您正在使用的查询吗?我会试试看它是否适合我。
  • 我用数据编辑了 OP。

标签: mysql sql


【解决方案1】:

你假设你的查询做了一些它没有做的事情。

(SELECT id FROM Business ORDER BY RAND() LIMIT 1) 在查询开始时没有具体化。 为每一行评估...因此,对于每一行,我们正在测试该 business_id 是否与新执行的子查询实例的结果相匹配。更全面的测试数据(包括不止一项业务)应该可以揭示这一点。

您需要将结果具体化为派生表,然后加入它。

SELECT a.* 
  FROM Advertisement a
  JOIN (
        SELECT (SELECT id 
                  FROM Business 
                 ORDER BY RAND() 
                 LIMIT 1) AS business_id
        ) b ON b.business_id = a.business_id;

( SELECT ... ) x 构造创建一个仅在查询期间存在的临时表,并使用别名 x。这样的表可以像真正的表一样连接。

MySQL 将此称为Subquery in the FROM Clause

【讨论】:

  • Michael,RAND() 的使用会导致这种行为吗?因为根据我的经验,当子查询是相关的时,通常 MySQL 只会为外部查询的每一行评估一个子查询。
  • @Uueerdo,这是正确的......但正常行为是因为优化器尝试缓存非相关子查询的确定性结果,将它们解析为常量值...... RAND() 当然不是确定性的。在这种情况下,查询计划将显示 UNCACHEABLE SUBQUERY 的 select_type。从技术上讲,我认为可以说您总是要求服务器评估每一行的子查询,但优化器足够聪明,很少实际这样做,因为非相关子查询的结果通常不会变化。
  • 这太棒了。非常感谢!
【解决方案2】:

尝试以下查询

SELECT * FROM Advertisement WHERE business_id = (select floor(1 + rand()* (select count(*) from Business)));

【讨论】:

  • 我试过了,它给我的问题和我原来的查询一样:(
【解决方案3】:

要以随机顺序检索行,请使用 SELECT * 而不是 Id,然后查询其 id。

SELECT * FROM Advertisement WHERE business_id=(SELECT ID FROM (SELECT * FROM Business ORDER BY RAND() LIMIT 1) as table1)

在这种情况下,对于您的示例数据,只有当 rand 返回 1 时,您才会得到结果。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-05-11
    • 1970-01-01
    • 2021-12-17
    • 1970-01-01
    • 2018-11-05
    相关资源
    最近更新 更多