我正在为我的一个表实现排名解决方案,以优化读取查询,以摆脱使用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子句,但问题仍然存在。我的问题是......如何在单个查询中分配位置?
答案 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