【问题标题】:MySQL Database design advice - using joinsMySQL 数据库设计建议 - 使用连接
【发布时间】:2013-06-22 00:07:06
【问题描述】:

我正在构建一个类似于 AJAX 的搜索页面,它允许客户选择一个数字过滤器来缩小搜索范围。例如,用户选择了“iPhone 5”并具有额外的容量(32GB、64GB)和颜色(黑色、白色......)过滤器。

用户只能为每个类别选择一个单选框(因此他们可以选择 32GB 和黑色).. 但他们无法选择(32GB 和 64GB 和黑色,因为其中两个属于“容量”类别)。

我已经在 sqlfiddle 上添加了架构(请忽略我已经删除了它们存在于适当应用程序中的主键,它们刚刚与其他一些字段/数据一起被删除以最小化 sqlfiddle)

http://sqlfiddle.com/#!2/964425

任何人都可以建议创建查询以执行以下操作的最佳方法:

Get all the prices for device_id '2939' (iPhone 5) which has the 'attributes' of '32GB' AND 'Black'

我目前有这个 - 但这仅在选择单个属性时有效:

// search for device with '64GB' & 'Black' attributes (this currently doesn't return any rows)
SELECT `prices`.*
FROM (`prices`)
LEFT JOIN `prices_attributes` ON `prices_attributes`.`price_id` = `prices`.`id`
WHERE `prices`.`device_id` =  '2939'
AND `attribute_option_id` =  '19'
AND `attribute_option_id` =  '47';

// search for device with '64GB' attribute only (this currently DOES return a row)
SELECT `prices`.*
FROM (`prices`)
LEFT JOIN `prices_attributes` ON `prices_attributes`.`price_id` = `prices`.`id`
WHERE `prices`.`device_id` =  '2939'
AND `attribute_option_id` =  '19';

任何关于数据库设计的建议也将不胜感激

注意:我正在考虑在 'prices' 表中创建一个新列,该列具有匹配的属性 ID 序列化 - 但是这不利于优化(例如,它会比当前方法慢)

【问题讨论】:

    标签: mysql database database-design schema


    【解决方案1】:
    SELECT * FROM prices WHERE device_id=2939 AND id IN (SELECT price_id FROM prices_attributes WHERE attribute_option_id IN (19,47));
    

    这是你要找的吗?

    编辑:抱歉,没有注意到您要求使用联接进行查询

    【讨论】:

      【解决方案2】:

      我认为您应该对attribute_option_id 使用IN 运算符,并为查询动态设置值;此外,使用 group_by 每个价格只有一行,因此实际上您可以获得所有价格。除此之外,设计还可以。

      这里,我做了一个例子:

      SELECT `prices`.*
      FROM (`prices`)
      LEFT JOIN `prices_attributes` ON `prices_attributes`.`price_id` = `prices`.`id`
      WHERE `prices`.`device_id` =  '2939'
      and `attribute_option_id` in ('19','47')
      group by `prices`.`device_id`, `prices`.`price`;
      

      在这里,您还可以添加一个订单子句来按价格订购:

      order by `prices`.`price` desc;
      

      解决此问题的另一种方法是使用不同的价格,如下所示:

      select distinct(prices.price)
      from prices
      where prices.device_id = 2939
      and id in (select price_id from prices_attributes where attribute_option_id in (19,47));
      

      【讨论】:

      • 这看起来像是一种 SWEET 解决方案。谢谢!!完全忘记使用 IN 命令,我很快就会试一试,让你知道我是怎么做的。再次感谢你有时会喜欢 stackoverflow!
      • 如果我正确阅读了要求,那么这里的问题是它正在检查属性选项 ID 为 19 或 47 的设备,而不是两者。在最后一个示例中可以解决此问题的一种方法是选择 price_id 和子选择中的记录计数,并使用 HAVING 子句检查计数是否为 2(或者已选择了许多选项),尽管这会失败应该price_attributes 表中有重复记录。
      • 一些很棒的反应家伙!我正在使用 CodeIgniter 框架,因此将尝试使用 ActiveRecord 系统尽可能多地生成查询 - 但是如果需要,我将只使用自定义查询并进行相应修改。再次感谢。
      • 正如@Kickstart 所说,我试图让它同时返回,而不是任何一个选项。
      【解决方案3】:

      多次加入 devices_attributes_options 表,项目必须具有的每个属性一次

      类似这样的:-

      SELECT *
      FROM devices a
      INNER JOIN prices b ON a.id = b.device_id
      INNER JOIN prices_attributes c ON b.id = c.price_id
      INNER JOIN devices_attributes_options d ON c.attribute_option_id = d.id AND d.attribute_value = '32GB'
      INNER JOIN devices_attributes_options e ON c.attribute_option_id = e.id AND e.attribute_value = 'Black'
      WHERE a.id = 2939
      

      至于将序列化的细节放入字段中,这是一个非常糟糕的主意,将来会回来咬你!

      【讨论】:

      • 谢谢 - 我会尝试两种方法,看看哪种方法最符合我的要求 - 再次感谢大家的提示!
      【解决方案4】:

      由于attribute_option_id 是一个原子值,它在相同 行中不能有两个不同的值。所以你的WHERE 子句不能匹配任何记录:

      SELECT `prices`.*
      FROM (`prices`)
      LEFT JOIN `prices_attributes` ON `prices_attributes`.`price_id` = `prices`.`id`
      WHERE `prices`.`device_id` =  '2939'
      AND `attribute_option_id` =  '19'   # Here for one row, attribute_option_id is either 19
      AND `attribute_option_id` =  '47';  # of '47'. Cannot be the both
      

      如果您觉得子查询更具可读性,您可以尝试使用子查询,而不是 JOIN。我认为 MySQL 允许这种语法:

      SELECT `prices`.*
      FROM `prices`
      WHERE `prices`.`device_id` =  '2939'
      AND EXISTS (SELECT *
                     FROM prices_attributes
                     WHERE price_id = `prices`.`id`
                     AND attribute_option_id IN ('19', '47') )
      

      不知道MySQL会如何优化上述方案。另一种选择是:

      SELECT `prices`.*
      FROM `prices`
      WHERE `prices`.`id` IN (
         SELECT DISTINCT `price_id`
             FROM prices_attributes
             WHERE attribute_option_id IN ('19', '47')
      )
      

      【讨论】:

      • 我可以看到 IN 命令是简化查询以替换许多 OR 条件的好方法,因为我想在本质上使用许多 AND 条件是否有类似的 MySQL IN 命令可以执行 AND比 OR 的? (希望这是有道理的)本质上,如果用户选择“32GB”和“黑色”,它只会在查询中返回具有“32GB”和“黑色”属性的 iPhone 设备(例如,如果它是iPhone 32GB 白色)
      • 不是很直接,因为您想用 AND 检查 2 个不同的行。您需要多次连接属性,每个所需属性一次,或者连接具有所需属性值的属性的子选择,并检查计数是否与所需属性值的数量相同。
      • 我明白你的意思..这可能会变得棘手,因为属性的数量会有所不同(例如,有些手机没有容量,可能只有颜色)我会尝试做属性多次,看看它是如何工作的,正如你在这个评论正上方的评论中提到的那样。
      • 我没有使用这个解决方案,但纯粹是因为我学到了一些有价值的 SQL 技巧 - 谢谢
      • 出于兴趣,您使用的解决方案是什么?
      猜你喜欢
      • 2012-01-27
      • 2011-05-10
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多