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

时间:2015-03-05 15:21:11

标签: sql subquery filemaker

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

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

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 ...而且,不,我无法控制列或表名:-()

编辑:

要明确的是,我需要将结果集包括计划天数和未完成任务的用户,以及那些在没有计划天数的情况下完成任务的人......

编辑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;

但语法显然很不稳定。

3 个答案:

答案 0 :(得分:2)

好的 - 这有效:

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版本中都失败 - 从派生表中选择似乎不受支持。

最后 - 让它在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;

希望这有助于某人。

答案 1 :(得分:1)

反转逻辑以从外部查询中的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;

答案 2 :(得分:1)

文件制作者不支持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