【问题标题】:Using GROUP BY and ORDER BY on an INNER JOIN SQL query在 INNER JOIN SQL 查询上使用 GROUP BY 和 ORDER BY
【发布时间】:2013-02-09 20:26:28
【问题描述】:

我正在使用以下查询从三个表中对客户的工作时间和费用进行分组,一张用于客户,一张用于工作时间,一张用于费用:

SELECT  a.*,
        COALESCE(b.totalCount, 0) AS CountWork,
        COALESCE(b.totalAmount, 0) AS WorkTotal,
        COALESCE(c.totalCount, 0) AS CountExpense,
        COALESCE(c.totalAmount, 0) AS ExpenseTotal
FROM    clients A
        LEFT JOIN
        (
            SELECT  Client, 
                    COUNT(*) totalCount,
                    SUM(Amount) totalAmount
            FROM    work_times
            WHERE   DATE BETWEEN '2013-01-01' AND '2013-02-01'
            GROUP   BY Client
        ) b ON a.Client = b.Client
        LEFT JOIN
        (
            SELECT  Client, 
                    COUNT(*) totalCount,
                    SUM(Amount) totalAmount
            FROM    expenses
            WHERE   DATE BETWEEN '2013-01-01' AND '2013-02-01'
            GROUP   BY Client
        ) c ON a.Client = c.Client
WHERE   b.Client IS NOT NULL OR
        c.Client IS NOT NULL

您可以看到查询在小提琴here 中工作。

我正在尝试修改此查询,以便每个客户每个月都有一行,按月排序,然后按客户排序。我正在尝试使用以下修改后的查询来做到这一点:

SELECT  a.*,
        COALESCE(b.totalCount, 0) AS CountWork,
        COALESCE(b.totalAmount, 0) AS WorkTotal,
        COALESCE(c.totalCount, 0) AS CountExpense,
        COALESCE(c.totalAmount, 0) AS ExpenseTotal
FROM    clients A
        LEFT JOIN
        (
            SELECT  Client, 
                    COUNT(*) totalCount,
                    SUM(Amount) totalAmount,
                    SUBSTR(Date, 1, 7) as Month
            FROM    work_times
            GROUP   BY Month,Client
            ORDER BY Month
        ) b ON a.Client = b.Client
        LEFT JOIN
        (
            SELECT  Client, 
                    COUNT(*) totalCount,
                    SUM(Amount) totalAmount,
                    SUBSTR(Date, 1, 7) as Month
            FROM    expenses
            GROUP   BY Month,Client
            ORDER BY Month,Client
        ) c ON a.Client = c.Client
WHERE   b.Client IS NOT NULL OR
        c.Client IS NOT NULL

您可以看到修改后的查询在运行here

但它的工作并不完全正确。即使有 2013 年 1 月的工作时间和 2013 年 2 月的费用(因此应该有 2 行),客户 B 也只返回一行,并且看起来这些行是由客户而不是按月排序的。有人可以建议如何修改查询以获得所需的输出,对于第二个小提琴上的示例来说,它是:

╔════════╦═══════════╦═══════════╦══════════════╦══════════════╗
║ CLIENT ║ COUNTWORK ║ WORKTOTAL ║ COUNTEXPENSE ║ EXPENSETOTAL ║
╠════════╬═══════════╬═══════════╬══════════════╬══════════════╣
║ A      ║         1 ║        10 ║            1 ║           10 ║
║ B      ║         1 ║        20 ║            0 ║            0 ║
║ A      ║         1 ║        15 ║            0 ║            0 ║
║ B      ║         0 ║        0  ║            1 ║           10 ║
║ C      ║         1 ║        10 ║            0 ║            0 ║
╚════════╩═══════════╩═══════════╩══════════════╩══════════════╝

【问题讨论】:

  • 两个子选择不需要ORDER BY。把它作为最终答案。
  • 如果 date 是 datetime 数据类型,为什么/如何对它执行 substring()?
  • 您的表格有多余的数据、数量和日期。您需要进一步规范您的结构并消除冗余。查询过于模糊,您希望查看特定数据,但除了 client id is not null 之外的任何选择中都没有任何条件。如果您想查看指定日期范围内的数据,按月份分组,然后查询日期。我会退后一步,将规范化视为我的第一个问题。
  • @JustAguy 我不确定你的意思。我使用金额来计算 SUM 和按月分组的日期。

标签: sql inner-join


【解决方案1】:

除非我在要求中遗漏了某些内容,否则您需要做的是获取客户列表和日期,然后将其加入您的子查询。所以您的查询将是:

SELECT a.*,
  COALESCE(b.totalCount, 0) AS CountWork,
  COALESCE(b.totalAmount, 0) AS WorkTotal,
  COALESCE(c.totalCount, 0) AS CountExpense,
  COALESCE(c.totalAmount, 0) AS ExpenseTotal
FROM 
(
  select distinct c.Client, d.Month
  from clients c
  cross join
  (
    select SUBSTR(Date, 1, 7) as Month
    from work_times
    union 
    select SUBSTR(Date, 1, 7) as Month
    from expenses
  ) d
) A
LEFT JOIN
(
  SELECT  Client, 
    COUNT(*) totalCount,
    SUM(Amount) totalAmount,
    SUBSTR(Date, 1, 7) as Month
  FROM    work_times
  GROUP   BY Month,Client
  ORDER BY Month,Client
) b 
  ON a.Client = b.Client
  and a.month = b.month
LEFT JOIN
(
  SELECT  Client, 
    COUNT(*) totalCount,
    SUM(Amount) totalAmount,
    SUBSTR(Date, 1, 7) as Month
  FROM    expenses
  GROUP   BY Month,Client
  ORDER BY Month,Client
) c 
  ON a.Client = c.Client
  and a.month = c.month
WHERE   b.Client IS NOT NULL OR
        c.Client IS NOT NULL
order by a.month, a.client

SQL Fiddle with Demo

结果是:

| CLIENT |   MONTH | COUNTWORK | WORKTOTAL | COUNTEXPENSE | EXPENSETOTAL |
--------------------------------------------------------------------------
|      A | 2013-01 |         1 |        10 |            1 |           10 |
|      B | 2013-01 |         1 |        20 |            0 |            0 |
|      A | 2013-02 |         1 |        15 |            0 |            0 |
|      B | 2013-02 |         0 |         0 |            1 |           20 |
|      C | 2013-02 |         1 |        10 |            0 |            0 |

【讨论】:

  • 不,您没有遗漏任何东西。您的查询完全返回我想要的。非常感谢!
  • @Nick 很乐意提供帮助,我只是对原始查询进行了轻微编辑,但它仍然返回相同的结果。 :)
  • 您的编辑结果似乎不太一样。它不是按月份排序的
  • @Nick 您只需在最终查询中添加一个ORDER BY 即可获得正确的排序——请参阅我的编辑和此演示 --sqlfiddle.com/#!2/fd61b/62
【解决方案2】:

如果您在子查询中进行排序,则没关系,因为外部查询可能(并且可能需要)重新排序结果。您想在外部查询中添加 order by。

您的问题是您尝试按 B 表的月份和客户进行排序,并且还尝试按 C 表的月份和客户进行排序。需要定义B.month、B.client、C.month的顺序,并放入order by进行外层查询。

顺便说一句,如果你只在 C 表的子查询中按月分组,那么客户端是没有意义的。某些数据库(如 DB2)不允许您将未聚合的字段放在 select 中,如果它不在 group by 中。

【讨论】:

  • 谢谢,work_times 和费用行都按小提琴中的Month,Client 分组。我已经修改了我的问题中的查询以反映这一点。不确定是否可以通过向外部查询添加 ORDER BY 来获得我正在寻找的结果。我尝试了一些组合,但它不起作用。部分问题是客户 B 的费用和工作时间合并到同一行,即使它们位于不同的月份。
猜你喜欢
  • 1970-01-01
  • 2023-04-02
  • 2014-08-31
  • 2019-01-21
  • 2021-11-17
  • 2021-04-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多