SQL排名解决方案

时间:2009-06-25 11:08:16

标签: mysql

我正在为我的一个表实现排名解决方案,以优化读取查询,以摆脱使用COUNT(*),LIMIT和OFFSET子句的昂贵查询。我的问题是我不知道为什么位置计算不正确。请看我的例子来重现问题。

CREATE TABLE `acl`
(
    `id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
    `name` VARCHAR(32) NOT NULL,
    `limiter` INTEGER(11) SIGNED NULL,
    PRIMARY KEY (`id`)
)
ENGINE=INNODB;

CREATE TABLE `quote`
(
    `id` MEDIUMINT(8) UNSIGNED NOT NULL AUTO_INCREMENT,
    `created_at` INTEGER(11) UNSIGNED NOT NULL,
    `reputation` INTEGER(11) SIGNED NOT NULL,
    PRIMARY KEY (`id`)
)
ENGINE=INNODB;

INSERT INTO `acl` (`name`, `limiter`) VALUES ('Users', 0), ('Staff', null);
INSERT INTO `quote` (`created_at`, `reputation`) 
  VALUES (UNIX_TIMESTAMP(), 0), (UNIX_TIMESTAMP()+1, 0);

SET @acl_id := 0, @position := 0;
SELECT acl.id AS acl_id, quote.id AS quote_id, 
  GREATEST(@position := IF(@acl_id = acl.id, @position + 1, 1), 
    LEAST(0, @acl_id := acl.id)) AS position 
FROM acl JOIN quote 
  ON (acl.limiter IS NULL OR quote.reputation >= acl.limiter) 
ORDER BY acl.id ASC, quote.created_at DESC;

我希望选择查询来获取所有acl行并同时将它们与引用行连接,设置它们的位置,但我得到的是每行的position = 1。有人建议我将变量赋值移动到JOIN或ORDER子句,但问题仍然存在。我的问题是......如何在单个查询中分配位置?

2 个答案:

答案 0 :(得分:0)

你正在使用太多的魔法,并尝试使用像传统的基于循环的编程语言之类的SQL语句。您需要将SQL视为声明性语言。这些行是自己评估的,而不是相对于先前评估的行。

(在某些情况下,您可以使用用户变量产生特殊效果,但您需要非常小心地使用此技术。很容易弄错,因为您需要了解评估行的顺序,这可能不是是您在ORDER BY子句中指定的顺序。)

感谢您提供完整的SQL,以显示您的表结构和示例数据。你已经展示了一个SQL查询无法做你想要的,你说你想要一些排名解决方案,但按什么标准排名?您希望行以什么顺序排列,以及您想要哪些行子集?当你提出这样的问题时,你需要明确自己的目标。

请修改上述问题并添加更多信息。期望输出的示例将是最佳的。如果你能做到这一点,我会检查并尝试进一步帮助。

PS:我知道在你有50个声望点之前你不能发表评论。尝试编辑原始问题。

答案 1 :(得分:0)

我博客中的文章略微修改过的查询:

SELECT  q.*,
        @r := @r + 1
FROM    (
        SELECT  @_acl_id := -1,
                @r := 0
        ) vars
STRAIGHT_JOIN
        (
        SELECT  acl.id AS acl_id, quote.id AS quote_id
        FROM    acl
        JOIN    quote
        ON      (acl.limiter IS NULL OR quote.reputation >= acl.limiter)
        ORDER BY
                acl.id ASC, quote.created_at DESC
        ) q
WHERE   CASE WHEN @_acl_id <> acl_id THEN @r := 0 ELSE 0 END IS NOT NULL
        AND (@_acl_id := acl_id) IS NOT NULL