高效的 SQL 查询或索引来查找所有列是不是只有 1 个值
Posted
技术标签:
【中文标题】高效的 SQL 查询或索引来查找所有列是不是只有 1 个值【英文标题】:Efficient SQL query or index to find if all of a column has only 1 value高效的 SQL 查询或索引来查找所有列是否只有 1 个值 【发布时间】:2021-05-19 15:50:29 【问题描述】:我有一个带有 items
表的 mysql 数据库,每个项目都限定为一个帐户。
这些项目每个都可以有几种颜色中的一种,如下所示:
项目
id | account_id | color
------------------------
1 | 1 | white
2 | 1 | white
3 | 1 | blue
4 | 1 | red
5 | 2 | white
6 | 2 | white
7 | 2 | white
在项目的显示页面上,我们希望显示项目的颜色,但仅当帐户曾经使用过多种颜色时。一旦他们开始在该帐户中使用另一种颜色,我们希望为所有项目显示颜色。
例如:
当显示 ID 为1
的项目时,我们会显示它是
white
,因为该帐户有混合白色、蓝色的项目
和红色。
当显示 ID 为 7
的项目时,我们不会显示颜色,
因为帐户中的所有颜色都相同(白色),所以有
无需使用颜色来区分它们。
为了进行这项检查,我目前正在选择帐户中的任何项目(第一个,但可能是任何项目),并运行查询以查看帐户中是否有任何颜色不同的项目,像这样:
// Get any color in the account:
SELECT color FROM items WHERE account_id = 1 LIMIT 1;
// See if any other color is used ("red" was returned from the previous query):
SELECT 1 AS one FROM `items` WHERE account_id = 1 AND color != 'red' LIMIT 1;
这很有效,并且对于循环使用一堆颜色的帐户相当有效,但有些帐户可能有数千或数百万个项目,并且可能只使用一种颜色,因此它可能必须扫描一个帐户,看看有没有不同的。
我已经尝试在 account_id
和 color
上添加复合索引,但即使是该索引也必须扫描数千或数百万条记录才能确定,因为只有少数几种颜色可以分组。
这可以接受吗?是否有一种特殊的索引可以在这里有效,或者我需要添加触发器/过程来更新每次插入时缓存的列或表?
我对触发器的意思是这样的:
DELIMITER $$
CREATE TRIGGER items_after_insert_update_account_colors
AFTER INSERT
ON items FOR EACH ROW
BEGIN
// Insert row, but ignore if the exact row already exists.
INSERT IGNORE INTO account_colors
SET color = NEW.color, account_id = NEW.account_id;
END$$
DELIMITER;
然后通过如下查询检查该表以确定是否显示颜色:
// True if other color is found for account.
SELECT 1 AS one FROM account_colors WHERE account_id = 1 AND color != 'red' LIMIT 1;
有没有一种索引技术我可以用来有效地做到这一点,或者正在使用像上面这样的触发器来缓存表的当前状态,并在每次插入时更新它,这是我能做的最好的吗?
总而言之,我想我想问是否有办法使索引像上面那样工作,即使对于数百万条记录,索引也很小,只需要保存这样的最少信息:
account_id | uses_color
-----------------------
1 | white
1 | blue
1 | red
2 | white
或
account_id | used_colors
-------------------------------------
1 | ['blue', 'white', 'red']
2 | ['white']
【问题讨论】:
触发器与此有什么关系?您想从查询中得到什么结果? 幸运的是,到目前为止提供的所有帐户都是如此 @GordonLinoff 该触发器只是一个示例,说明如果没有一个可以快速处理数百万行的良好索引策略,我可以如何实现这一点。我想要一个查询来快速告诉我整个帐户中是否只使用了一种颜色。 请提供各种用例的示例数据,并显示所需的输出。 @RickJames 我在示例中添加了更多列以显示具有所有匹配颜色的帐户,并解释了我想要的结果。感谢您让我知道我不清楚的地方。 【参考方案1】:我建议exists
:
select i.*
from items i
where exists (select 1
from items i2
where i2.account_id = i.account_id and
i2.color <> i.color
)
order by i2.account_id;
那么为了提高性能,您需要在items(account_id, color)
上建立索引。
【讨论】:
感谢您展示我如何在一个查询中完成此任务,但这不是我的目标。我已经添加了这个索引,虽然它确实表现得更好,但它仍然比我想要的慢。我正在寻找一种可能像触发器一样工作的紧凑索引技术,我已经用更多细节和示例更新了这个问题。【参考方案2】:SELECT COUNT(DISTINCT(col)) FROM table;
如果该列中只有一个不同的值,则返回 1。
可以将WHERE
子句附加到它以以某种方式限制它。您可以在子查询中使用结果。
【讨论】:
COUNT(DISTINCT(col))
不会比COUNT(col) > 1
或WHERE col != 'value' LIMIT 1
慢很多吗?我已经有了可行的解决方案,我只是想知道是否有适合数百万行的快速解决方案。
@Unixmonkey - 你知道一个“价值”吗?首先获取“值”会更慢。在您的第一个示例中,COUNT(DISTINCT value)
是帐户 1 的 3;并且是 1 对于帐户 2。COUNT(value)
是 >1
对于每个帐户。以上是关于高效的 SQL 查询或索引来查找所有列是不是只有 1 个值的主要内容,如果未能解决你的问题,请参考以下文章