繁体   English   中英

如何使用多态has_many'through'进行联接?

[英]how do I do a join with a polymorphic has_many 'through'?


我具有以下数据结构(简化),该数据结构在购买表(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.amount)排序的类别。 如何使用联接和聚合函数做到这一点?

我为此做了一个sqlfiddle:

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

多态ID与数据库中的继承结构相似,因为要查询所有多态ID,通常都必须使用并集。

但是,如果您的Books / Shirts / DVDs表没有公共列,则在它们之间进行查询绝对没有任何意义。 如果它们确实具有公共列,那么将其拉入作为其他产品基础的父产品表中可能更有意义。

例如,如果您想要每种产品的总销售额,则只需

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表”

这是一个如何使用临时表实现此目的的示例。 原理是创建一个“临时”表,并在运行查询时在所需的结构中以所需的数据填充该 您可以修改数据,并在最后将其返回,然后在连接关闭时销毁表。

无论如何-您可以像结合新的唯一标识,产品ID,产品类型,产品名称和类别ID的架构那样设置临时表。 然后,在表,DVD和衬衫表中的所有记录之间使用联合 查询填充表以适应该格式:

-- 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.001秒-它将很好地扩展。 我会为此设置一个小提琴,但是SQLFiddle似乎不太支持临时表。 只需在本地服务器上同时运行CREATE和SELECT语句即可查看。

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM