简体   繁体   English

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

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


I have the following data structure (simplified), which includes a polymorphic association in the purchases table (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

I would like to select categories from the db, ordered by total sales (purchases.amount). 我想从数据库中选择按总销售额(purchases.amount)排序的类别。 How can I do this with joins and aggregate functions? 如何使用联接和聚合函数做到这一点?

I made a sqlfiddle for this: 我为此做了一个sqlfiddle:

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

Polymorphic IDs are similar to inheritance structures in a DB, in that to query across all of them often you have to use a union. 多态ID与数据库中的继承结构相似,因为要查询所有多态ID,通常都必须使用并集。

However, if your Books/Shirts/DVDs tables have no common columns, it makes absolutely no sense to query across them. 但是,如果您的Books / Shirts / DVDs表没有公共列,则在它们之间进行查询绝对没有任何意义。 If they did have common columns, probably makes more sense to pull that out into a parent Product table that acts as a base for the others. 如果它们确实具有公共列,那么将其拉入作为其他产品基础的父产品表中可能更有意义。

If you for example wanted total sales per product, you'd just do 例如,如果您想要每种产品的总销售额,则只需

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

You could pull in product titles like this: 您可以输入以下产品标题:

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

It might be more performant to do the union first, then the group by outside of that. 首先执行工会,然后在外面进行分组可能会更有表现。

My experience with inheritance hierarchies is that you should do whatever you can to avoid unions. 我对继承层次结构的经验是,您应该尽一切可能避免合并。 They are a pain to query, result in non-DRY queries, and perform badly. 它们使查询很痛苦,导致非DRY查询,并且性能很差。 This means pulling out commonalities such as Title into a proper normalized structure to avoid having to query across the concrete/derived types. 这意味着将诸如Title之类的共性提取到适当的规范化结构中,以避免必须查询具体/派生类型。 This is essentially what Wrikken mention in comments "Usually, a better way for this would be a Products table with the shared information types" 这基本上就是Wrikken在注释中提到的“通常,对此更好的方法是使用共享信息类型的Products表”

Here's an example of how you could achieve this using temporary tables . 这是一个如何使用临时表实现此目的的示例。 The principle is that you create a "temporary" table and populate it with the data that you want, in the structure that you want, when your query is run. 原理是创建一个“临时”表,并在运行查询时在所需的结构中以所需的数据填充该 You can modify the data, and return it at the end and the table is then destroyed when the connection closes. 您可以修改数据,并在最后将其返回,然后在连接关闭时销毁表。

Anyway - you can set up your temporary table like with the schema combining a new unique, product id, product type, product name and category id. 无论如何-您可以像结合新的唯一标识,产品ID,产品类型,产品名称和类别ID的架构那样设置临时表。 You then populate the table with a union query between all the records in your books, dvds and shirts tables to suit that format: 然后,在表,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
);

Now you can access the data in this table as you normally would. 现在,您可以像往常一样访问该表中的数据。 The benefit of using temporary tables is that you don't have to change your database structure - in your case it's maybe not ideal to have it set up the way it is, but doing something like this could help you to select data efficiently without changing it. 使用临时表的好处是您不必更改数据库结构-在您的情况下,按原样设置它可能并不理想,但是这样做可以帮助您有效地选择数据而无需更改它。

To select data, you could use a query like this: 要选择数据,可以使用如下查询:

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)

You said you want to order your results by amount, etc. Add your conditions and ordering to the query above as you want to. 您说要按数量等对结果进行排序。您可以根据需要在上面的查询中添加条件和排序。

Example output: 输出示例:

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      

These can actually be very efficient to run, for example running this on my local server is executing in 0.001s - it will scale very nicely. 这些实际上可以非常有效地运行,例如在我的本地服务器上运行此命令的时间为0.001秒-它将很好地扩展。 I would've set you up a fiddle for this, but SQLFiddle doesn't seem to support temporary tables very well. 我会为此设置一个小提琴,但是SQLFiddle似乎不太支持临时表。 Just run the CREATE and SELECT statements at the same time on your local server to see. 只需在本地服务器上同时运行CREATE和SELECT语句即可查看。

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

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