多表选择使用where where子句,有没有比自联接更好的方法?

时间:2009-06-18 17:03:09

标签: sql mysql database

我有一堆表,我正在使用一个唯一的商品ID加入。大多数where子句条件将以编程方式从用户求和表单(搜索框)构建,并且通常会针对同一个表测试多个条件,在本例中为项目标记。

我对SQL的经验很少,但我理解基础知识。我想找到标记有特定类型标记的活动(status = 1)项的ID,其值为“cats”和“kittens”。标签存储为(id,product_id,tag_type_id,value),id是唯一需要唯一值的列。我的第一次尝试是;

   select 
      distinct p2c.product_id 
   from '.TABLE_PRODUCT_TO_CATEGORY.' p2c
      inner join '.TABLE_PRODUCT.' p on p2c.product_id = p.id 
      inner join '.TABLE_PRODUCT_TAG.' pt on p.id = pt.product_id
      inner join '.TABLE_TAG_TYPE.' tt on pt.tag_type_id = tt.id
   where 
      tt.id = '.PRODUCT_TAG_TYPE_FREE_TAG.'
      and p.status = 1
      and lower(pt.value) = "cats"
      and lower(pt.value) = "kittens"

但是没有回复。我意识到最终的AND条件是问题,所以尝试使用自连接;

   select 
      distinct p2c.product_id 
   from '.TABLE_PRODUCT_TO_CATEGORY.' p2c
      inner join '.TABLE_PRODUCT.' p on p2c.product_id = p.id 
      inner join '.TABLE_PRODUCT_TAG.' pt on p.id = pt.product_id
      inner join '.TABLE_PRODUCT_TAG.' pt2 on p.id = pt2.product_id
      inner join '.TABLE_TAG_TYPE.' tt on pt.tag_type_id = tt.id
   where 
      tt.id = '.PRODUCT_TAG_TYPE_FREE_TAG.'
      and p.status = 1
      and lower(pt.value) = "cats"
      and lower(pt2.value) = "kittens"

现在一切都按预期工作,结果集正确。那么我想知道什么?要重新迭代,我所追求的结果是活动(状态= 1)项目的ID,这些项目已使用特定类型的标记进行标记,值为“cats”和“kittens”......

  1. 自我加入是实现这些结果的最佳方式吗?
  2. 这个查询有可能是巨大的(我省略了一个类别条件,其中可能有~300),这种自连接方法也可以很好地扩展吗?如果没有,有替代方案吗?
  3. 如果我允许用​​户指定复杂的标签搜索,自加入方法是否是最好的前进方式(假设有替代方法)?即“猫”和(“小猫”或“狗”)不是“鹦鹉”。

7 个答案:

答案 0 :(得分:4)

这不会在你的第一个查询中起作用吗?

而不是

and lower(pt.value) = "cats"
and lower(pt.value) = "kittens"

这样做

and lower(pt.value) in ("cats","kittens")

答案 1 :(得分:3)

select 
  distinct p2c.product_id
from '.TABLE_PRODUCT_TO_CATEGORY.' p2c
  inner join '.TABLE_PRODUCT.' p on p2c.product_id = p.id
  inner join '.TABLE_PRODUCT_TAG.' pt on p.id = pt.product_id
  inner join '.TABLE_TAG_TYPE.' tt on pt.tag_type_id = tt.id   
where 
  tt.id = '.PRODUCT_TAG_TYPE_FREE_TAG.'
  and p.status = 1  
  and (lower(pt.value) = "cats" or lower(pt.value) = "kittens")

答案 2 :(得分:0)

初始查询的问题是:

  and lower(pt.value) = "cats"
  and lower(pt.value) = "kittens"

不存在值为“cats”和“kittens”的标记,因此不会返回任何记录。使用IN子句作为SQLMenace建议将是解决方案 - 这就是你说的,“给我回复任何被标记为'cats'或'kittens'的活动项目。”

但是如果你想要任何有BOTH标签的活动项目 - 那么你需要做类似你的第二个查询。你的问题并不完全清楚,那就是你所追求的。

类似于问题#3:

  

“猫”和(“小猫”或“狗”)不是“鹦鹉”。

你会想要pt1,pt2和(在子查询中)pt3,以及类似的东西:

and lower(pt1.value) = "cats"
and lower(pt2.value) in ("kittens", "dogs")
and not exists (select * from '.TABLE_PRODUCT_TAG.' pt3 where pt3.product_id = p.id and lower(pt3.value) = "parrots")

广泛的一般情况可能会变得非常混乱......

答案 3 :(得分:0)

您的回答是“是的,这是一种可扩展的技术”。至于增加复杂性,我认为在你遇到有效查询问题之前,你会超出用户理解他们正在做什么的能力。

答案 4 :(得分:0)

您正在构建另一个实体 - 属性 - 值数据模型。由于您询问了可伸缩性,这里有一个警告:EAV模型通常不会扩展,也不会在RDBMS之上执行。最终,这种“灵活”的数据模型最终会破坏优化器,您将扫描数百万行来获取您的少数狗和小猫。维基百科有一个topic covering this model and some of the downsides。不知道你的目标数据库是什么,例如SQL Server CAT published a white paper在EAV模型中有常见问题。

答案 5 :(得分:0)

好的,让我重新陈述这个问题,以确保我理解:

您正在尝试显示所有具有两种不同特定标签的产品(“猫”和“小猫”),但标签存储在1对多的表格中。

双连接确实有效,但这是另一种选择:

SELECT ...
FROM P
WHERE p.status = 1
  AND p.ProductID IN (SELECT Product_ID FROM tags WHERE value = "cats")
  AND p.ProductID IN (SELECT Product_ID FROM tags WHERE value = "kittens")

根据用户选择的选项添加其他AND语句。

SQL优化器实际上应该以对待连接的方式对待它,所以我认为性能不会比你的版本扩展任何更差。但是,值得测试您的数据集,以确保。

答案 6 :(得分:0)

AIR CODE

select 
  distinct p2c.product_id 
from '.TABLE_PRODUCT_TO_CATEGORY.' p2c
  inner join '.TABLE_PRODUCT.'     p  on p2c.product_id = p.id 
where 
  and p.status = 1
  and 2 = (
      SELECT  COUNT(1)
      FROM '.TABLE_PRODUCT_TAG.' pt
        INNER JOIN  '.TABLE_TAG_TYPE.' tt ON pt.tag_type_id = tt.id
      WHERE tt.id = '.PRODUCT_TAG_TYPE_FREE_TAG.'
      AND pt.product_id = p.id /* edit */
      lower(pt.value) IN( "cats", "kittens" )
)