【问题标题】:INNER JOIN query using three tables to return Count and Sum of rows that match criteriaINNER JOIN 查询使用三个表返回匹配条件的行的计数和总和
【发布时间】:2013-02-24 15:25:40
【问题描述】:

我正在尝试连接三个 SQL 表中的数据。

表格格式如下:

客户

╔════════╗
║ CLIENT ║
╠════════╣
║ A      ║
║ B      ║
║ C      ║
║ D      ║
╚════════╝

工作时间

╔════════╦══════════╦════════╦════════════╗
║ Client ║   Work   ║ Amount ║    Date    ║
╠════════╬══════════╬════════╬════════════╣
║ A      ║ Web Work ║     10 ║ 2013-01-12 ║
║ B      ║ Research ║     20 ║ 2013-01-20 ║
║ A      ║ Web Work ║     15 ║ 2013-01-21 ║
║ C      ║ Research ║     10 ║ 2013-01-28 ║
╚════════╩══════════╩════════╩════════════╝

费用

╔════════╦══════════╦════════╦════════════╗
║ Client ║   Item   ║ Amount ║    Date    ║
╠════════╬══════════╬════════╬════════════╣
║ A      ║ Software ║     10 ║ 2013-01-12 ║
║ B      ║ Software ║     20 ║ 2013-01-20 ║
╚════════╩══════════╩════════╩════════════╝

我想要一个返回每个客户的工作和费用的计数和总和的查询,即:

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

到目前为止,我有以下内容:

SELECT clients.Client,
 COUNT(distinct work_times.id) AS num_work,
 COUNT(expenses.id) AS num_expenses
FROM
 clients
 INNER JOIN work_times ON work_times.Client = clients.Client
   INNER JOIN expenses ON expenses.Client = work_times.Client
GROUP BY
  clients.Client

这似乎是正确的,但它跳过了没有费用的客户,并且似乎将 num_expenses 乘以 num_work。我还想添加一个 WHERE 子句来指定只返回两个日期之间的工作时间和费用。我需要对查询进行哪些更改才能获得所需的输出?

【问题讨论】:

    标签: sql inner-join multiple-tables


    【解决方案1】:

    您需要单独计算子查询中的值。最外层查询上的WHERE 子句的目的是过滤掉在一张表上至少有记录的记录。所以在这种情况下,Client D 将不会显示在结果列表中。

    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
    

    更新

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

    【讨论】:

    • 我必须对您的查询进行轻微更改,以按月分组,然后按客户分组,然后按月排序。我试了一下,你可以看到here,但是输出是由客户而不是月份订购的。你有什么建议吗?
    • 小提琴上的输出是否正确,唯一的问题是排序?
    • 输出几乎是正确的。由于我对日期所做的更改,客户 B 的工作时间为 2013 年 1 月,客户 B 的费用为 2013 年 2 月,但它们却在同一行返回。除此之外,订购是一个问题。有些月份,客户可能需要支付费用,但没有工作时间,反之亦然。
    • 好的,我现在分析问题。我可以给你的一个提示是为此打开另一个问题,这样很多其他人都可以看到问题:D
    • 我又问了一个问题here
    【解决方案2】:

    您可以将group by 移动到子查询中,因此您不必为每个expense 重复每个work_time。一旦有了子查询,就可以轻松地为两者添加日期过滤器:

    SELECT  clients.Client
    ,       work_times.cnt AS num_work
    ,       work_times.total AS total_work
    ,       expenses.cnt AS num_expenses
    ,       expenses.total AS total_expenses
    FROM    clients
    LEFT JOIN
            (
            SELECT  Client
            ,       COUNT(DISTINCT id) as cnt
            ,       SUM(Amount) as total
            FROM    work_times
            WHERE   Date between '2013-01-01' and '2013-02-01'
            GROUP BY
                    Client
            ) work_times
    ON      work_times.Client = clients.Client
    LEFT JOIN
            (
            SELECT  Client
            ,       COUNT(DISTINCT id) as cnt
            ,       SUM(Amount) as total
            FROM    expenses
            WHERE   Date between '2013-01-01' and '2013-02-01'
            GROUP BY
                    Client
            ) expenses
    ON      expenses.Client = clients.Client
    

    【讨论】:

      【解决方案3】:

      我没有合适的实例来测试,但我可能会先这样,然后检查是否可以进一步改进查询...

      select 
        T1.client, 
        ce AS 'Count Work', 
        am AS 'Work Total', 
        ci AS 'Count Expense', 
        am2 AS 'Expense Total' 
      from (
        select 
          client, 
          count (work) as ce, 
          sum(amount) as am 
        FROM 
          clients 
            left join work_times 
            on fk_client=client 
        group by 
          fk_client
      ) T1 
      left join (
        select 
          client, 
          count(item) as ci, 
          sum(amount) as am2 
        from 
          clients 
            left join expenses 
            on fk_client=client 
        group by fk_client
      ) T2 
      where T1.client=T2.client;
      

      也许这看起来很复杂,但它可以确保每个客户只有一排。也许以后会更易读……

      【讨论】:

        猜你喜欢
        • 2012-04-04
        • 2016-08-27
        • 2015-02-03
        • 1970-01-01
        • 1970-01-01
        • 2019-04-30
        • 1970-01-01
        • 1970-01-01
        • 2020-02-26
        相关资源
        最近更新 更多