【问题标题】:sql optimization with anti join带反连接的sql优化
【发布时间】:2015-03-10 01:27:59
【问题描述】:

我有一个类别的递归表和一个包含以下字段的公司表:

category(id, name, parent) // parent is foreign key to category id :)
company(id, category_1, category_2, category_3) // category_* is foreign key to category id

类别树的最大深度=3;

类别 cx -> 类别 cy -> 类别 cz

知道公司类别总是链接到最后一个类别 (c3),我想要一个公司链接到的所有类别 (c1z, c2z, c3z, c1y, c2y, c3y, c1x, c2x, c3x) 用于我的搜索引擎。 //c1y是category_1的父类,c1x是category_1的父类...

我想出的最好的查询是:

SELECT
  ID,
  NAME
FROM category c3
WHERE ID IN (
    select category_1 from company where id=:companyId
  union
    select category_2 from company where id=:companyId
  union
    select category_3 from company where id=:companyId
  union
        select parent from category where id in (
          select category_1 from company where id=:companyId
          union
          select category_2 from company where id=:companyId
          union
          select category_3 from company where id=:companyId
        )
  union 
        select parent from category where id in (
          select parent from category where id in (
          select category_1 from company where id=:companyId
          union
          select category_2 from company where id=:companyId
          union
          select category_3 from company where id=:companyId
          )
        )
  )

它有很多重复。一个用于公司中的 category_*。一个用于重复多次。

有什么方法可以删除所有这些重复项?

--更新--

假设我们使用两个表来解决 category-* 字段,那么具有 3 级类别的递归问题呢?

例如,如果只有一个类别,它看起来像

SELECT
  ID,
  NAME
FROM category
WHERE ID IN (
  select category_1 from company where id=:companyId
  union
  select parent from category where id in (
    select category_1 from company where id=:companyId
  )
  union
  select parent from category where id in (
    select parent from category where id in (
      select category_1 from company where id=:companyId
    )
  )
);

【问题讨论】:

  • 您使用的是哪个 DBMS?后格雷斯?甲骨文?
  • 我正在使用 oracle,但我不希望它与数据库相关,因为它很可能在功能中进行更改。
  • @MaciejLos 如何选择不同的帮助我处理这种情况?

标签: sql query-optimization anti-join


【解决方案1】:

如果你想加入数据,使用类似这样的东西(SQL server 示例):

DECLARE @category TABLE (id INT IDENTITY(1,1), name VARCHAR(30), parent INT) -- parent is foreign key to category id :)
DECLARE @company TABLE (id INT IDENTITY(1,1), category_1 INT, category_2 INT, category_3 INT) --category_* is foreign key to category->id


INSERT INTO @category (name, parent )
VALUES('Top category', null), ('Cars', 1)

INSERT INTO @company (category_1, category_2 , category_3 )
VALUES(2, null, null), (2, 2, null), (2, 2, 2)


SELECT t1.*, t2.*
FROM @category AS t1 INNER JOIN @company AS t2 ON t1.id = t2.category_1 or t1.id = t2.category_2  or t1.id = t2.category_3 

以上代码产生:

id  name    parent  id  category_1  category_2  category_3
2   Cars    1   1   2   NULL    NULL
2   Cars    1   2   2   2   NULL
2   Cars    1   3   2   2   2

但是,这样的数据库结构是错误的!

而不是一张桌子

company(id, category_1, category_2, category_3)

创建两个表

company(id, name)
comp_cat(id, comp_id, cat_id)

为什么?我不想直接回答,所以我问你:1)当公司与超过3个类别相关时会发生什么? 2)为什么在没有设置第二和第三类的情况下保存空值?

如果是 SQL Server,你可以使用Common Table Expressions:

;WITH CTE AS
(
    SELECT id, category_1 AS cat_id
    FROM @company 
    WHERE NOT category_1 IS NULL
    UNION ALL
    SELECT id, category_2 AS cat_id
    FROM @company 
    WHERE NOT category_2 IS NULL
    UNION ALL
    SELECT id, category_3 AS cat_id
    FROM @company 
    WHERE NOT category_3 IS NULL
)
SELECT DISTINCT t1.*, t2.*
FROM CTE AS t1 INNER JOIN @category AS t2 ON t1.cat_id = t2.id 

干杯, 马切耶

【讨论】:

  • 我知道很多这些类别将是空的。我想改成 2 张桌子。不应超过 3 个类别。所以我想也许这样它会有更好的性能而不是使用 2 个表
  • @alizezele,正如我所提到的,当您使用关系数据库时,使用一个表是不好的编程习惯。
  • 我看到了通用表表达式,但我不想使用任何 dbms 特定语法。
  • @alizezele,公共表表达式在 MSSQL、Oracle(称为子查询分解)和 Postgres 中可用。它是 SQL-99 标准的一部分。只有 MySQL 缺少该功能。
【解决方案2】:

我使用common table expressions 进行查询。这是我提出的最后一个查询

 with cte as (
    select category_1 as id  from company where id=:companyId and category_1 is not null
    union
    select category_2 as id from company where id=:companyId and category_2 is not null
    union
    select category_3 as id from company where id=:companyId and category_3 is not null
  ) select id, name FROM category WHERE id IN (
      select id from cte 
    union
      select parent from category where id in (select id from cte)
    union
      select parent from category where id in (
       select parent from category where id in (select id from cte)
     )
   );

这是我能想到的最好方法。感谢 @Maciej 为您指明方向,感谢 @Nicholai 提供有关 DBMS 支持的信息。

只有当有一种方法可以像 matlab 那样将行转换为列时...:P

【讨论】:

  • 可以将行转换/转置为列,反之亦然。请参阅 Pivot 和 Unpivot 语句。
猜你喜欢
  • 2019-04-11
  • 1970-01-01
  • 2018-10-30
  • 2012-06-28
  • 2018-03-24
  • 2017-09-16
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多