将 SQL 分组和未分组的结果与交叉连接组合?

Combining SQL grouped and ungrouped results with a cross join?

我继承了两个 table,其中一个的数据以小时为单位,另一个的数据以天为单位。

一个 table 有计划的资源使用,另一个有实际花费的时间

Internal_Resources

| PeopleName | NoOfDays | TaskNo |
|------------|----------|--------|
| Fred       |        1 |    100 |
| Bob        |        3 |    100 |
| Mary       |        2 |    201 |
| Albert     |       10 |    100 |


TimeSheetEntries

| UserName | PaidHours | TaskNumber |
|----------|-----------|------------|
| Fred     |         7 |        100 |
| Fred     |        14 |        100 |
| Fred     |         7 |        100 |
| Bob      |         7 |        100 |
| Bob      |        21 |        100 |
| Mary     |         7 |        201 |
| Mary     |        14 |        100 |

我需要的是计划时间与花费时间的比较。

| name   | PlannedDays | ActualDays |
|--------|-------------|------------|
| Albert |          10 |       NULL |
| Bob    |           3 |       4.00 |
| Fred   |           1 |       4.00 |
| Mary   |       NULL  |       2.00 |

我拼凑了一些几乎可以解决问题的东西:

SELECT
    UserName, 
    ( SELECT
        NoOfDays FROM Internal_Resources as r
        WHERE r.PeopleName = e.UserName AND r.TaskNumber = ? ) AS PlannedDays,
    SUM ( Round( PaidHours / 7 , 2 ) ) as ActualDays
    FROM TimeSheetEntries e WHERE TaskNo = ? 
    GROUP BY UserName

任务 100 的返回结果如下:

| UserName | PlannedDays | ActualDays |
|----------|-------------|------------|
| Bob      |           3 |          4 |
| Fred     |           1 |          4 |
| Mary     |           0 |          2 |

但是懒惰的阿尔伯特没有特色!我想要:

| UserName | PlannedDays | ActualDays |
|----------|-------------|------------|
| Albert   |          10 |          0 |
| Bob      |           3 |          4 |
| Fred     |           1 |          4 |
| Mary     |           0 |          2 |

我试过在

上使用变体
SELECT * FROM ( SELECT ... ) AS plan
    INNER JOIN ( [second-query] ) AS actual
    ON plan.PeopleName = actual.UserName

应该做什么?我怀疑我需要在某处挤压交叉连接,但我无处可去...

( 这将是 运行 在 FileMaker ExecuteSQL() 调用中,所以我需要非常普通的 SQL... 不,我没有控制权在列或 table 名称上 :-( )

编辑:

为了清楚起见,我需要结果集包括已计划好几天但尚未执行任务的用户,以及已完成任务但未计划好几天的用户...

编辑 2:

我可以手动得到我想要的东西,但看不出如何组合下面的语句:

SELECT people.name, PlannedDays, ActualDays FROM
( SELECT PeopleName as name FROM Internal_Resources WHERE TaskNo = 100
UNION
SELECT DISTINCT UserName as name FROM TimeSheetEntries WHERE TaskNumber = 100 
ORDER BY Name) AS people

让我明白:

+--------+
| name   |
+--------+
| Albert |
| Bob    |
| Fred   |
| Mary   |
+--------+

和:

( SELECT PeopleName AS name, NoOfDays AS PlannedDays
FROM Internal_Resources WHERE TaskNo = 100 ) AS actual

让我明白:

+--------+-------------+
| name   | PlannedDays |
+--------+-------------+
| Fred   |           1 |
| Bob    |           3 |
| Albert |          10 |
+--------+-------------+

最后,

( SELECT UserName AS name, SUM( Round( PaidHours / 7, 2 ) ) AS ActualDays
FROM TimeSheetEntries
WHERE TaskNumber = 100 GROUP BY UserName ) AS planned

让我明白:

+------+------------+
| name | ActualDays |
+------+------------+
| Bob  |       4.00 |
| Fred |       4.00 |
| Mary |       2.00 |
+------+------------+

现在所有(全部!哈!)我想要的是将这些组合成这个:

+--------+-------------+------------+
| name   | PlannedDays | ActualDays |
+--------+-------------+------------+
| Albert |          10 |       NULL |
| Bob    |           3 |       4.00 |
| Fred   |           1 |       4.00 |
| Mary   |       NULL  |       2.00 |
+--------+-------------+------------+

编辑 3:

我试过将它与以下内容结合起来:

SELECT people.name, PlannedDays, ActualDays
FROM ( SELECT PeopleName as name FROM Internal_Resources WHERE TaskNo = 100
  UNION
  SELECT DISTINCT UserName as name FROM TimeSheetEntries WHERE TaskNumber = 100 
  ORDER BY Name) AS people

LEFT JOIN ( SELECT PeopleName AS name, NoOfDays AS PlannedDays FROM Internal_Resources WHERE TaskNo = 100 ) AS actual, 
ON people.name = actual.name

LEFT JOIN ( SELECT UserName AS name, SUM( Round( PaidHours / 7, 2 ) ) AS ActualDays FROM TimeSheetEntries WHERE TaskNumber = 100 GROUP BY UserName ) AS planned
ON people.name = planned.name;

但语法显然不可靠。

反转逻辑以在外部查询中从 Internal_resources 读取:

SELECT ir.UserName, NoOfDays as PlannedDays,
       (SELECT SUM ( Round( PaidHours / 7 , 2 ))
        FROM TimeSheetEntries e
        WHERE e.TaskNo = ? AND ir.PeopleName = e.UserName
       ) as ActualDays
FROM Internal_Resources ir
WHERE ir.TaskNumber = ?
GROUP BY ir.UserName, NoOfDays;

filemaker 不支持 LEFT OUTER JOIN 吗?

SELECT
    PeopleName,
    NoOfDays AS PlannedDays
    ROUND(SUM(PaidHours) / 7, 2) AS ActualDays
FROM
    Internal_Resources AS planned
-- left join should not discard Albert's record from Internal_Resources
LEFT JOIN TimeSheetEntries AS actual
    ON planned.PeopleName = actual.UserName
    AND planned.TaskNo = actual.TaskNumber
WHERE
    planned.TaskNo = ?
GROUP BY PeopleName, NoOfDays

好的 - 这有效:

SELECT people.name, COALESCE(PlannedDays, 0) as planned, COALESCE(ActualDays, 0) as actual
FROM ( SELECT PeopleName as name FROM Internal_Resources WHERE TaskNo = 100
  UNION
  SELECT DISTINCT UserName as name FROM TimeSheetEntries WHERE TaskNumber = 100 
  ORDER BY Name) AS people

LEFT JOIN ( SELECT PeopleName AS name, NoOfDays AS PlannedDays FROM Internal_Resources WHERE TaskNo = 100 ) AS ir 
ON people.name = ir.name

LEFT JOIN ( SELECT UserName AS name, SUM( Round( PaidHours / 7, 2 ) ) AS ActualDays FROM TimeSheetEntries WHERE TaskNumber = 100 GROUP BY UserName ) AS ts
ON people.name = ts.name;

给予:

+--------+---------+--------+
| name   | planned | actual |
+--------+---------+--------+
| Albert |      10 |   0.00 |
| Bob    |       3 |   4.00 |
| Fred   |       1 |   4.00 |
| Mary   |       0 |   2.00 |
+--------+---------+--------+

我想一定有更简单的方法,这个看起来更简单:

SELECT name, SUM(x) AS planned, SUM(y) AS actual 
FROM (

  SELECT PeopleName AS name, NoOfDays AS x, 0 AS y
  FROM Internal_Resources WHERE TaskNo = 100

  UNION

  SELECT UserName AS name, 0 AS x, SUM( PaidHours / 7 ) AS y
  FROM TimeSheetEntries WHERE TaskNumber = 100 GROUP BY UserName) AS source

GROUP BY name;

但令人沮丧的是 - 两者都在 MySQL 中工作,但在 FileMaker 的缩减 SQL 版本中都失败了 - 从派生的 table 中选择似乎不受支持。

最后 - 让它在 FileMaker 中工作的技巧 SQL - IN 和 NOT IN 支持子查询...所以三个查询的联合 - 计划好几天并完成一些工作的人, 做过计划外工作的人, 和没做过计划内工作的人:

SELECT PeopleName as name, NoOfDays as planned, Sum( PaidHours / 7 ) as actual 
    FROM Internal_Resources 
    JOIN TimeSheetEntries 
    ON PeopleName = UserName 
    WHERE TaskNumber = 100 AND TaskNo = 100 GROUP BY PeopleName

UNION

SELECT UserName as name, 0 as planned, Sum( PaidHours / 7 ) as actual
    FROM TimeSheetEntries 
    WHERE TaskNumber = 100 
    AND UserName NOT IN (
        SELECT PeopleName FROM Internal_Resources WHERE TaskNo = 100
    )

UNION

SELECT PeopleName as name, NoOfDays as planned, 0 as actual 
    FROM Internal_Resources WHERE TaskNo = 100
    AND PeopleName NOT IN (
        SELECT PeopleName as name
        FROM Internal_Resources JOIN TimeSheetEntries 
        ON PeopleName = UserName 
        WHERE TaskNumber = 100 AND TaskNo =  100 
        GROUP BY PeopleName
    )

ORDER BY name;

希望这对某人有所帮助。