可用版本: Dev (3.21) | 最新 (3.20) | 3.19 | 3.18 | 3.17 | 3.16 | 3.15 | 3.14 | 3.13 | 3.12 | 3.11

SQL: SELECT DISTINCT

适用于 ✅ 开源版   ✅ 专业版   ✅ 企业版

SELECT DISTINCT 语法有时可能用于删除重复项,以防您的模式未规范化。 例如,在日志表中,您可能想要查询不同的 IP 地址

SELECT DISTINCT ip FROM access_log
create.selectDistinct(ACCESS_LOG.IP).from(ACCESS_LOG).fetch();

这对于临时查询特别有用。

很多时候,在应用程序查询中删除此类重复项时,您需要添加聚合函数(例如,每个 IP 的日志条目数,每个 IP 的不同登录数等),因此您将使用GROUP BY,而不是DISTINCT 子句。

但是,请注意使用 DISTINCT 来删除您不完全确定为什么会发生的重复项,即通常在非常大型和复杂的查询中,并且很多时候在存在(INNER) JOIN 表达式时,实际上应该有SEMI JOIN 表达式。 例如,以下查询查找以字母 A 开头的书店中所有书籍的作者

SELECT DISTINCT
  author.first_name,
  author.last_name
FROM author
JOIN book ON author.id = book.author_id
JOIN book_to_book_store
  ON book.id = book_to_book_store.book_id
WHERE book_to_book_store.name LIKE 'A%'
create.selectDistinct(
          AUTHOR.FIRST_NAME,
          AUTHOR.LAST_NAME)
      .from(AUTHOR)
      .join(BOOK).on(AUTHOR.ID.eq(BOOK.AUTHOR_ID))
      .join(BOOK_TO_BOOK_STORE)
        .on(BOOK.ID.eq(BOOK_TO_BOOK_STORE.BOOK_ID))
      .where(BOOK_TO_BOOK_STORE.NAME.like("A%")).fetch();

现在,由于我们模式的性质

  • 作者写了很多书
  • 书籍在多个书店有售

因此,同一个作者很可能在结果中多次出现,因此使用 DISTINCT。 从数据库性能的角度来看,这可能非常糟糕,因为作者在从结果中再次删除之前会被复制到一些内部数据结构中。 此外,根据优化器,即使我们已经找到作者,我们也可能必须扫描所有书籍和所有书店。 优化器非常不同,因此这在每个 RDBMS 中可能或可能不是严重程度相同的问题,但肯定需要注意。

这里更好的解决方案是使用SEMI JOIN,例如使用IN 谓词EXISTS 谓词(它们在语义上是等效的)

SELECT
  author.first_name,
  author.last_name
FROM author
WHERE author.id IN (
  SELECT book.author_id
  FROM book
  JOIN book_to_book_store
    ON book.id = book_to_book_store.book_id
  WHERE book_to_book_store.name LIKE 'A%'
)
create.select(
          AUTHOR.FIRST_NAME,
          AUTHOR.LAST_NAME)
      .from(AUTHOR)
      .where(AUTHOR.ID).in(
          select(BOOK.AUTHOR_ID)
          .from(BOOK)
          .join(BOOK_TO_BOOK_STORE)
            .on(BOOK.ID.eq(BOOK_TO_BOOK_STORE.BOOK_ID))
          .where(BOOK_TO_BOOK_STORE.NAME.like("A%")
      ).fetch();

现在,我们永远不会得到任何重复的作者。

如果有疑问,请检查您的执行计划和执行指标,了解这里最好的查询是什么,但在很多情况下,DISTINCT 提示正在进行一些不必要的操作,然后再次删除重复项。

反馈

您对此页面有任何反馈吗? 我们很乐意听取您的意见!

The jOOQ Logo