高效的 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_idcolor 上添加复合索引,但即使是该索引也必须扫描数千或数百万条记录才能确定,因为只有少数几种颜色可以分组。

这可以接受吗?是否有一种特殊的索引可以在这里有效,或者我需要添加触发器/过程来更新每次插入时缓存的列或表?

我对触发器的意思是这样的:

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) &gt; 1WHERE col != 'value' LIMIT 1 慢很多吗?我已经有了可行的解决方案,我只是想知道是否有适合数百万行的快速解决方案。 @Unixmonkey - 你知道一个“价值”吗?首先获取“值”会更慢。在您的第一个示例中,COUNT(DISTINCT value) 是帐户 1 的 3;并且是 1 对于帐户 2。COUNT(value)&gt;1 对于每个帐户。

以上是关于高效的 SQL 查询或索引来查找所有列是不是只有 1 个值的主要内容,如果未能解决你的问题,请参考以下文章

sql 曾经需要找到哪些表引用某个列?此SQL脚本将查询系统表以查找对g的所有引用

SQL - 查询字符串是不是包含列中的部分值

SQL 查询以查找具有相同列值的多行

「Mysql索引原理(七)」覆盖索引

高性能索引-高性能索引策略二

SQL查询以查找列和行中的最大值