如何使用多态has_many'到'进行连接?

时间:2014-08-14 20:55:08

标签: mysql


我有以下数据结构(简化),其中包含购买表(MySql)中的多态关联

Purchases
  product_type (this refers to a table, book, dvd, shirt,...)
  product_id (this refers to the product's id in the table in product_type)
  amount

Books
  category_id

DVDs
  category_id

Shirts
  category_id

Categories
  name

我想从数据库中选择类别,按总销售额(purchases.ountount)排序。如何使用连接和聚合函数执行此操作?

我为此做了一个方便的小说:

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

2 个答案:

答案 0 :(得分:0)

多态ID与数据库中的继承结构类似,因为要查询所有这些,通常必须使用联合。

但是,如果您的书籍/衬衫/ DVD表没有公共列,那么查询它们是完全没有意义的。如果他们确实有共同的列,那么将它拉出到父Product表中可能更有意义,该表作为其他列的基础。

例如,如果您想要每个产品的总销售额,那么您只需

Select sum(amount), product_id -- or product_type if you wanted sales per type
From Purchases
Group By product_id -- product_type

您可以提取以下产品标题:

Select sum(p.amount), p.product_id, b.Title
From Purchases p 
Inner Join Books b on b.category_id = p.product_id 
Group By product_id
    Union
Select sum(p.amount), p.product_id, s.Title    
From Purchases p
Inner Join Shirts s on s.category_id = p.product_id 
Group By product_id

可能可能更先进行联合,然后再进行组合。

我对继承层次结构的体验是,你应该尽一切可能避免工会。它们很难查询,导致非DRY查询,并且表现不佳。这意味着将诸如Title之类的共性转换为适当的规范化结构,以避免必须跨具体/派生类型进行查询。这基本上是Wrikken在评论中提到的“通常,更好的方法是使用共享信息类型的Products表”

答案 1 :(得分:0)

以下是使用temporary tables如何实现此目标的示例。原则是您create a "temporary" table并在运行查询时在所需的结构中填充所需的数据。您可以修改数据,并在结束时将其返回,然后在连接关闭时销毁该表。

无论如何 - 您可以设置临时表,就像使用新的唯一,产品ID,产品类型,产品名称和类别ID的架构一样。然后,您在书籍,dvds和衬衫表中的所有记录之间使用union query填充表格,以符合以下格式:

-- delete the temp table in case it's still there
DROP TEMPORARY TABLE IF EXISTS all_products;

-- create your new temp table
CREATE TEMPORARY TABLE IF NOT EXISTS all_products (
    new_id INT(11) NOT NULL AUTO_INCREMENT PRIMARY KEY,
    product_id INT(11) NOT NULL,
    product_type VARCHAR(50) NOT NULL,
    product_name VARCHAR(128) NOT NULL,
    category_id INT(11) NOT NULL
) AS (
    -- populate it with unioned queries
    SELECT * FROM (
        (SELECT NULL AS new_id, id AS product_id, 'book' AS product_type, `name` AS product_name, category_id FROM books)
        UNION ALL
        (SELECT NULL AS new_id, id AS product_id, 'dvd' AS product_type, `name` AS product_name, category_id FROM dvds)
        UNION ALL
        (SELECT NULL AS new_id, id AS product_id, 'shirt' AS product_type, `name` AS product_name, category_id FROM shirts)
    ) AS temp -- an alias is required - doesn't matter what it is
);

现在您可以像往常一样访问此表中的数据。使用临时表的好处是你不必改变你的数据库结构 - 在你的情况下,它可能不是理想的设置它的方式,但做这样的事情可以帮助你在不改变数据的情况下有效地选择数据。

要选择数据,您可以使用如下查询:

SELECT
    purchase.*,
    product.product_name,
    category.name
FROM purchases AS purchase
-- get your product info from the temp table
INNER JOIN all_products AS product
    ON (product.product_id = purchase.product_id AND product.product_type = purchase.product_type)
-- get the category name
INNER JOIN categories AS category
    ON (category.id = product.category_id)

您说您想按金额等订购结果。根据需要在上面的查询中添加条件和订购。

示例输出:

id  product_id  product_type  amount  product_name  name         
1   1           book          10      how to        educational  
2   1           dvd           10      video how to  educational  
3   1           shirt         10      tshirt        clothes      

这些实际上可以非常有效地运行,例如在我的本地服务器上运行它在0.001s内执行 - 它将非常好地扩展。我会为你设置一个小提琴,但SQLFiddle似乎并不能很好地支持临时表。只需在本地服务器上同时运行CREATE和SELECT语句即可查看。