【问题标题】:Advanced SQL query with lots of joins具有大量连接的高级 SQL 查询
【发布时间】:2010-06-09 11:39:11
【问题描述】:

我正在开发自行车的高级搜索。我有很多表格需要加入才能找到所有表格,比如说红色和棕色的自行车。一辆自行车可能有不止一种颜色!我现在已经做了这个查询:

SELECT DISTINCT p.products_id,                    #simple product id
                products_name,                    #product name
                products_attributes_id,           #color id
                pov.products_options_values_name  #color name
FROM   products p
       LEFT JOIN products_description pd
         ON p.products_id = pd.products_id
       INNER JOIN products_attributes pa
         ON pa.products_id = p.products_id
       LEFT JOIN products_options_values pov
         ON pov.products_options_values_id = pa.options_values_id
       LEFT JOIN products_options_search pos
         ON pov.products_options_values_id = pos.products_options_values_id
WHERE  pos.products_options_search_id = 4         #code for red
       OR pos.products_options_search_id = 5      #code for brown

我首先关心的是许多连接。 Products 表主要保存产品 ID 及其图像,Products Description 表保存更多描述性信息,例如名称(当然还有产品 ID)。

然后我有一个 Products Options Values 表,其中包含所有颜色及其 ID。 Products Options Search 包含颜色 ID 和颜色组 ID (products_options_search_id)。红色的颜色组代码为 4(棕色为 5)。

产品和颜色在Products Attributes内部管理着多对多的关系。

所以我的问题首先是:进行这么多连接可以吗?我会影响性能吗?

第二:如果一辆自行车有红色和棕色两种颜色,即使我使用SELECT DISTINCT,它也会出现两次。认为这是因为INNER JOIN。这是否可以避免,我是否必须删除我的 PHP 代码中的双精度值?

第三:自行车可以是双色的(即黑色和蓝色)。这意味着该自行车有两排。一个说颜色是黑色,一个说是蓝色。 (见第二个问题)。但是,如果我在 WHERE 子句中替换 OR ,它会删除两行,因为它们都不满足条件 - 只有产品。解决方法是什么?

【问题讨论】:

    标签: sql search join


    【解决方案1】:

    好的,首先 SQL 是用来做连接的,你的查询不是很大。如果索引正确,这实际上应该会提高您的性能,因为它更容易获取正确的数据。

    您可以通过使用子查询来删除您获得的重复项,如下所示:

    SELECT DISTINCT p.products_id,                    #simple product id
                    products_name,                    #product name
                    products_attributes_id,           #color id
                    pov.products_options_values_name  #color name
    FROM   products p
           LEFT JOIN products_description pd
             ON p.products_id = pd.products_id
    WHERE p.products_id in (
           Select products_id from products_attributes pa #This will give you the ID forall bikes that have either red or brown in them
           INNER JOIN products_options_values pov
             ON pov.products_options_values_id = pa.options_values_id
           INNER JOIN products_options_search pos
             ON pov.products_options_values_id = pos.products_options_values_id
           WHERE  pos.products_options_search_id = 4         #code for red
                OR pos.products_options_search_id = 5      #code for brown)
    

    当然,您将无法将颜色作为结果的一部分返回,因为颜色没有单一值。如果需要,您可以通过编写函数将颜色组合到一个字段中。

    第三个选项是在 PHP 代码中汇总数据(在结果集中多次返回项目,但循环并仅显示每个项目一次),然后显示返回的颜色列表以不同的方式(作为附加表格或逗号分隔列表或任何适合您的方式。

    如果您将 OR 设为 AND,它当然意味着所有的自行车都是红色和棕色的。如果这是您正在寻找的东西,这将是正确的,但听起来您想要两者之一而不是两者。

    【讨论】:

    • 喜欢关于子查询的想法!我想我会继续处理这个结果!我用最后一个问题想通了。我实际上永远不需要找到双色的自行车...... :)
    【解决方案2】:

    这不是很多连接。假设索引不错,它不会对任何负面影响。

    distinct 确实选择了select 子句中所有字段的不同组合。所以,是的,如果你有不止一种颜色,就会出现不止一辆自行车,因为你已经包含了颜色字段。如果您只想要一种颜色,您应该告诉它您想要哪种颜色(例如,具有最大代码的颜色或其他颜色)。或者不要选择颜色代码(因为你似乎并不关心它实际上是什么)。好的经验法则:只选择您实际需要的字段。

    您的最后一个问题不清楚。如果你用什么替换or?如果您尝试将其设为and 不,它将无法正常工作,因为没有单行具有两种颜色代码(因为这是不可能的)。

    【讨论】:

      【解决方案3】:

      绝对不会有太多的连接。除非ON 子句不相等,否则连接通常会很好地限制结果集,尤其是在正确的索引可用时。

      您想要的类似于在 SO 上找到至少带有一个标签列表的问题,所以我写了该查询作为比较:https://data.stackexchange.com/stackoverflow/query/2695/so3005416-comparison-select-questions-with-any-selected-tags

      忽略将标签放入临时表的代码,这实际上只是确认来自another question's answer 的代码。只需比较最后的两个 Select 语句即可。

      【讨论】:

      • 谢谢,好灵感!在 SELECT 语句中需要考虑一些好事情。
      【解决方案4】:

      正如 Donnie 和 Cobusve 所指出的,这不是很多连接。但是,在关系(即规范化)模式中,产品属性(例如名称和颜色)通常存储在产品表中,而不是单独的表中。

      假设您无法对表结构做任何事情,替代查询可能是:

      SELECT p.products_id,                          #simple product id
             products_name,                          #product name
             min(products_attributes_id),            #lowest color ID
             max(products_attributes_id),            #highest color ID
             min(pov.products_options_values_name),  #lowest color name
             max(pov.products_options_values_name)   #highest color name
      FROM   products p
             LEFT JOIN products_description pd
               ON p.products_id = pd.products_id
             INNER JOIN products_attributes pa
               ON pa.products_id = p.products_id
             INNER JOIN products_options_values pov
               ON pov.products_options_values_id = pa.options_values_id
             INNER JOIN products_options_search pos
               ON (pov.products_options_values_id = pos.products_options_values_id AND
              pos.products_options_search_id IN (4, 5) )         #codes for red, brown
      group by p.products_id, products_name
      

      根据您使用的 SQL 方言(SQLServer、Oracle、MySQL 等),最终条件的语法可能需要稍有不同。

      显然,如果只返回一种颜色,则最低值和最高值将相同。

      【讨论】:

      • 这是一个我正在更新的旧网站。所以是的,很难改变结构。我在这里和那里添加了一些列和表,但整体结构不是我想搞砸的。我不太明白最小值/最大值是什么。什么目的? IN (4,5) 的想法非常好。这么简单,我还没想好。肯定会用的顺便说一句,查询工作得很好。那里没有错。
      • 最小/最大的想法是,当颜色不同时,一种(棕色)会出现在最低处,一种(红色)会出现在最高处,所以两种颜色都会出现在一个上线。显然,这种闪避只适用于最多选择两种颜色的情况。我会建议使用条件结构——例如在 Oracle 中解码,或在许多其他 SQL 中使用大小写——但这些往往会因 SQL 的不同方言而异,而且我不知道您使用的是哪种 SQL。
      猜你喜欢
      • 1970-01-01
      • 2012-02-12
      • 2012-07-10
      • 2017-08-17
      • 2018-01-13
      • 1970-01-01
      • 1970-01-01
      • 2021-08-19
      • 1970-01-01
      相关资源
      最近更新 更多