简体   繁体   English

如何在MySQL中使用表A与自身以一对多关系联接两个表?

[英]How do I join two tables using Table A in a one-to-many relationship with itself in MySQL?

I have two tables. 我有两张桌子。

The 'Store' table is a demographics table, with fields like these: “商店”表是一个受众特征表,其中包含以下字段:

ID, ParentID, ConsolidationType ID,ParentID,ConsolidationType

ID is the unique identifier for a given store. ID是给定商店的唯一标识符。

ParentID may be either NULL or contain the ID of its parent store if it has a ConsolidationType of 'Secondary'. 如果ParentID的ConsolidationType为'Secondary',则可以为NULL或包含其父存储的ID。

ConsolidationType is either NULL, 'Primary', or 'Secondary'. ConsolidationType为NULL,“ Primary”或“ Secondary”。

The 'Sales' table has fields like these: “销售”表具有以下字段:

StoreID, SalesDate, SalesAmount StoreID,SalesDate,SalesAmount

StoreID refers to an ID in the Store table. StoreID是指Store表中的ID。


I'm trying to get the total consolidated and individual sales for a given store in a single row of a single query. 我正在尝试在单个查询的单行中获取给定商店的总合并销售量和个人销售量。 I've written a SQL query like the following: 我编写了如下的SQL查询:

SELECT 
Store.ID AS 'Store ID',
SUM(IF(YEAR(main.SalesDate) = 2012 AND QUARTER(main.SalesDate) = 1,main.SalesAmount,0)) AS 'Individual Sales',
SUM(IF(YEAR(main.SalesDate) = 2012 AND QUARTER(main.SalesDate) = 1 AND YEAR(secondaries.SalesDate) = 2012 AND QUARTER(secondaries.SalesDate) = 1,main.SalesAmount + secondaries.SalesAmount,0) AS 'Consolidated Sales'
FROM Store 
LEFT JOIN Sales AS 'main' ON Store.ID = main.StoreID 
LEFT JOIN Sales AS 'secondaries' ON Store.ParentID = secondaries.StoreID 
GROUP BY Store.ID

I can't figure out why this doesn't work as intended. 我不明白为什么这不能按预期工作。 What am I missing? 我想念什么? What's wrong with my logic? 我的逻辑怎么了?

The problem is that your query is generating a Cartesian-product (-like) result set. 问题是您的查询正在生成笛卡尔乘积(类似)结果集。

Basically, a row from 'main' is getting repeated multiple times, for each matching row from 'secondaries'. 基本上,对于“次要”中的每个匹配行,“ main”中的一行将重复多次。

To get the result you want, join to the sales table just one time, and match on both the StoreID and the ParentID. 要获得所需的结果,只需加入一次sales表,然后在StoreID和ParentID上进行匹配。

To get Individual Sales , only include in the SUM rows where the StoreID matches, something like this: 要获得Individual Sales ,只能在StoreID匹配的SUM行中包括以下内容:

SELECT Store.ID AS `Store ID`
     , SUM(IF(main.StoreID = Store.ID,
       IF(YEAR(main.SalesDate) = 2012 AND QUARTER(main.SalesDate) = 1,main.SalesAmount,0)
       ,0)) AS `Individual Sales`
     , SUM(
       IF(YEAR(main.SalesDate) = 2012 AND QUARTER(main.SalesDate) = 1,main.SalesAmount,0)
       ) AS `Consolidated Sales`
  FROM Store 
  LEFT JOIN Sales AS `main` ON main.StoreID = Store.ID OR main.StoreID = Store.ParentID
 GROUP BY Store.ID

UPDATE 更新

My bad. 我的错。 (DOH!) ParentID is on the Store table, not the Sales table. (DOH!) ParentIDStore表上,而不在Sales表上。

The query above does not return the specified result. 上面的查询不返回指定的结果。 (Working on it.) (正在努力。)

I think you had the solution already... 我认为您已经有了解决方案...

Use the ParentID column in place of the ID column from the Store table. 使用 ParentID列代替 Store表中的 ID列。 Where the ParentID column is NULL, we use the value from the ID column. 当ParentID列为NULL时,我们使用 ID列中的值。

 
 
 
  
  SELECT IFNULL(Store.ParentID,Store.ID) AS `Store ID` , SUM(IF(main.StoreID = Store.ID, IF(YEAR(main.SalesDate) = 2012 AND QUARTER(main.SalesDate) = 1,main.SalesAmount,0) ,0)) AS `Individual Sales` , SUM( IF(YEAR(main.SalesDate) = 2012 AND QUARTER(main.SalesDate) = 1,main.SalesAmount,0) ) AS `Consolidated Sales` FROM Store s LEFT JOIN Sales AS `main` ON main.StoreID = Store.ID OR main.StoreID = Store.ParentID GROUP BY IFNULL(Store.ParentID,Store.ID)
 
  


This query returns the specified result set: 此查询返回指定的结果集:

 SELECT t.StoreID AS `Store ID` , SUM(IF(t.source='p', IF(YEAR(t.SalesDate) = 2012 AND QUARTER(t.SalesDate) = 1,t.SalesAmount,0) ,0)) AS `Individual Sales` , SUM( IF(YEAR(t.SalesDate) = 2012 AND QUARTER(t.SalesDate) = 1,t.SalesAmount,0) ) AS `Consolodiated Sales` FROM ( SELECT 'p' AS source , a.StoreID , a.SalesDate , a.SalesAmount FROM Sales a UNION ALL SELECT 's' AS source , s.ParentID , b.SalesDate , b.SalesAmount FROM Sales b JOIN Store s ON s.ID = b.StoreID WHERE s.ParentID IS NOT NULL ) t GROUP BY t.StoreID ORDER BY t.StoreID 

This is not the most efficient, the inline view (or "derived table") is on the order of the size of the Sales table. 这不是最有效的,内联视图(或“派生表”)按Sales表的大小顺序排列。 It would be more efficient to push the predicates on the date down into the inline view, or to summarize by month, or quarter, in the derived table. 将日期上的谓词下推到内联视图中,或在派生表中按月或季度进行汇总,会更有效。

I would more likely return the "quarter" as part of the result set, to allow me to pull more than one quarter. 我更可能将“四分之一”作为结果集的一部分返回,以使我可以拉出四分之一以上。

 SELECT t.StoreID AS `Store ID` , t.SalesQuarter , SUM(IF(t.source='p',t.SalesAmount,0)) AS `Individual Sales` , SUM(t.SalesAmount) AS `Consolodiated Sales` FROM ( SELECT 'p' AS source , a.StoreID , ADDDATE(MAKEDATE(YEAR(a.SalesDate),1), INTERVAL FLOOR(MONTH(a.SalesDate)/4) QUARTER) AS SalesQuarter , SUM(a.SalesAmount) AS SalesAmount FROM Sales a -- WHERE a.SalesDate >= '2012-01-01' -- AND a.SalesDate < '2012-04-01' GROUP BY a.StoreID, SalesQuarter UNION ALL SELECT 's' AS source , s.ParentID , ADDDATE(MAKEDATE(YEAR(b.SalesDate),1), INTERVAL FLOOR(MONTH(b.SalesDate)/4) QUARTER) AS SalesQuarter , SUM(b.SalesAmount) AS SalesAmount FROM Sales b JOIN Store s ON s.ID = b.StoreID WHERE s.ParentID IS NOT NULL -- AND a.SalesDate >= '2012-01-01' -- AND a.SalesDate < '2012-04-01' GROUP BY s.ParentID, SalesQuarter ) t GROUP BY t.StoreID, t.SalesQuarter ORDER BY t.StoreID, t.SalesQuarter 

I usually try to keep the filtering logic in the WHERE clause. 我通常尝试将过滤逻辑保留在WHERE子句中。 Your original query might have too many JOINS and cases where there are no children might not give you what you expect. 您的原始查询可能有太多的JOINS并且在没有孩子的情况下可能无法满足您的期望。 The IS NULL check in the WHERE below allows for stores with no children. 下面的WHERE中的IS NULL检查允许存储没有子项的商店。

SELECT
  s.ID
  , SUM(s.SalesAmount) AS `Individual Sales`
  , SUM(s.SalesAmount) + SUM(c.SalesAmount) AS `Consolidated Sales`
FROM Store AS s
LEFT OUTER JOIN Store AS c ON c.ParentID = s.ID
WHERE YEAR(s.SalesDate) = 2012 AND QUARTER(s.SalesDate) = 1
  AND (c.SalesDate IS NULL OR
    (YEAR(c.SalesDate) = 2012 AND QUARTER(c.SalesDate) = 1))
GROUP BY s.ID

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

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