如何在SQL结果中包含分组的子查询?

时间:2019-04-30 04:01:05

标签: mysql

我有一个显示表,以mysql的形式显示每日结果。每日结果不包括今天没有任何销售的产品。我如何从表格中读取信息并确定列出所有产品,即使某个特定产品在该天没有销售但又在另一天出现在桌上。

我研究并了解,在EXIST子句上使用WHERE是可行的。但我试过,但无济于事。我想我只需要在查询中包括一个分组的子查询,但是我应该放在哪里。下面是我的代码示例

SELECT 
transaction.transactionservicetype AS Services,
COUNT(transaction.transactionid) AS Count,
IFNULL (SUM(transaction.transactionamount),'0') AS Amount,
IFNULL (SUM(statement.statementdebit),'0') AS NetCost,
IFNULL((SUM(transaction.transactionamount) - SUM(statement.statementdebit)),'0') as TotalEarning
FROM transaction
RIGHT JOIN statement ON transaction.transactionid = statement.transactionid
WHERE transaction.transactiondate = '2019-04-03' AND transaction.transactionstatus = 'SUCCESS' 
GROUP BY `transaction`.transactionservicetype ASC

而不是显示诸如此类的表:

Services   Count   Amount   Netcost   Total Earning

Chicken   4       5.30       5.14           -

Beef      3       3.30       3.13          -

我希望结果中包含当天未找到的产品,但是确定表中还存在另一种产品,因此显示结果如下:

Services   Count   Amount   Netcost   Total Earning

Chicken        4   5.30        5.14       -

Beef           3   3.30        3.13       -

Venison        0     0           0        -

Fowl           0     0           0        -

更新:

使用反连接和其他结果无法获得正确的结果。通过扫描并使用Ultimater的代码作为示例,这与我想要使用sql查询从结果中获取的结果非常接近:

(
SELECT 
  transaction.transactionservicetype AS Services,
  COUNT(transaction.transactionid) AS Count,
  IFNULL (SUM(transaction.transactionamount),'0') AS Amount,
  IFNULL (SUM(statement.statementdebit),'0') AS NetCost,
  IFNULL((SUM(transaction.transactionamount) - SUM(statement.statementdebit)),'0') as TotalEarning
FROM
  transaction RIGHT JOIN statement
ON
  transaction.transactionid = statement.transactionid
WHERE
  transaction.transactiondate = '2019-04-03' AND transaction.transactionstatus = 'SUCCESS' 
GROUP BY
  `transaction`.transactionservicetype ASC
)
UNION
(
SELECT 
  transactionservicetype AS Services,
    '0' AS Count,
    '0' AS Amount,
  '0' AS NetCost,
  '0' AS TotalEarning
FROM
  transaction
GROUP BY
  transactionservicetype ASC
)

上面的sql查询使我更接近我想要的结果。结果就是这样:

Services   Count   Amount   Netcost   Total Earning

Chicken        4   5.30        5.14       -

Beef           3   3.30        3.13       -

Chicken        0     0           0        -

Beef           0     0           0        -

Venison        0     0           0        -

Fowl           0     0           0        -

我只需要删除重复的行(Chicken, Beef),如何使用sql查询修复它?

1 个答案:

答案 0 :(得分:1)

未经测试但很确定您正在寻找类似这样的东西:
想法是,您将所有当前结果都联合到一个反联接上,以获取其余结果来模拟FULL JOIN

(
SELECT 
  transaction.transactionservicetype AS Services,
  COUNT(transaction.transactionid) AS Count,
  IFNULL (SUM(transaction.transactionamount),'0') AS Amount,
  IFNULL (SUM(statement.statementdebit),'0') AS NetCost,
  IFNULL((SUM(transaction.transactionamount) - SUM(statement.statementdebit)),'0') as TotalEarning
FROM
  transaction RIGHT JOIN statement
ON
  transaction.transactionid = statement.transactionid
WHERE
  transaction.transactiondate = '2019-04-03' AND transaction.transactionstatus = 'SUCCESS' 
GROUP BY
  `transaction`.transactionservicetype ASC
)

UNION ALL

(
SELECT
  transaction.transactionservicetype AS Services,
  COUNT(transaction.transactionid) AS Count,
  IFNULL (SUM(transaction.transactionamount),'0') AS Amount,
  '0' AS NetCost,
  '0' as TotalEarning
FROM
  transaction LEFT JOIN statement
ON
  transaction.transactionid = statement.transactionid
WHERE
  statement.statementid IS NULL AND transaction.transactiondate = '2019-04-03' AND transaction.transactionstatus = 'SUCCESS'
GROUP BY
  `transaction`.transactionservicetype ASC
);

您还可以根据要获取此类“缺失”服务的条件,在反连接上放弃transaction.transactiondate = '2019-04-03' AND transaction.transactionstatus = 'SUCCESS'

以上内容还假设transaction.transactionid是现有列和主键。

第二部分是反联接:

SELECT
  transaction.transactionservicetype AS Services,
  COUNT(transaction.transactionid) AS Count,
  IFNULL (SUM(transaction.transactionamount),'0') AS Amount,
  '0' AS NetCost,
  '0' as TotalEarning
FROM
  transaction LEFT JOIN statement
ON
  transaction.transactionid = statement.transactionid
WHERE
  statement.statementid IS NULL AND transaction.transactiondate = '2019-04-03' AND transaction.transactionstatus = 'SUCCESS'
GROUP BY
  `transaction`.transactionservicetype ASC

您应该能够自己运行它,并看到如下结果:

Services   Count   Amount   Netcost   Total Earning

Venison        0     0           0        0

Fowl           0     0           0        0

我通过将RIGHT JOIN更改为LEFT JOIN并在WHERE子句中指定statement.statementid IS NULL来编写反联接,这样您就不会从上一个查询中获得任何INNER JOIN数据,因此我进行了调整计数代码(例如NetCost和TotalEarnings)通过对它们进行硬编码来进行编码,因为在这种反连接中没有匹配项可以加快其逻辑。

此答案涵盖了在MySQL中使用反联接以模拟完全联接的目的:
https://stackoverflow.com/a/4796911/466314

反连接是我链接到的帖子中使用的术语,但实际上不是官方术语。这里的“ anti”一词意味着需要有一个原始的联接,然后您想要进行另一个联接,但是当被屏蔽为完全联接时,要抓住该联接的相反方向。这不同于仅将LEFT和RIGHT(外部)联接的结果相互组合,因为它们共享相同的INNER JOIN数据,并且您将得到多余的结果。

UNION ALL允许这种冗余。 UNION本身会删除这些重复项。

如果您要依靠UNION删除那些重复项,可以像这样模拟一个更易于理解的FULL JOIN:

(
SELECT 
  transaction.transactionservicetype AS Services,
  COUNT(transaction.transactionid) AS Count,
  IFNULL (SUM(transaction.transactionamount),'0') AS Amount,
  IFNULL (SUM(statement.statementdebit),'0') AS NetCost,
  IFNULL((SUM(transaction.transactionamount) - SUM(statement.statementdebit)),'0') as TotalEarning
FROM
  transaction RIGHT JOIN statement
ON
  transaction.transactionid = statement.transactionid
WHERE
  transaction.transactiondate = '2019-04-03' AND transaction.transactionstatus = 'SUCCESS' 
GROUP BY
  `transaction`.transactionservicetype ASC
)

UNION

(
SELECT 
  transaction.transactionservicetype AS Services,
  COUNT(transaction.transactionid) AS Count,
  IFNULL (SUM(transaction.transactionamount),'0') AS Amount,
  IFNULL (SUM(statement.statementdebit),'0') AS NetCost,
  IFNULL((SUM(transaction.transactionamount) - SUM(statement.statementdebit)),'0') as TotalEarning
FROM
  transaction LEFT JOIN statement
ON
  transaction.transactionid = statement.transactionid
WHERE
  transaction.transactiondate = '2019-04-03' AND transaction.transactionstatus = 'SUCCESS' 
GROUP BY
  `transaction`.transactionservicetype ASC
);

我所做的只是再次写了相同的原始查询,但是将RIGHT(权利)改为LEFT(左),并将它们放在UNION之间以在将结果连接在一起时删除重复项。

编辑:

这是最新的建议: 使用原始查询,但是从RIGHT JOIN更改为LEFT JOIN,然后将整个WHERE子句移到ON子句。

SELECT 
  transaction.transactionservicetype AS Services,
  COUNT(transaction.transactionid) AS Count,
  IFNULL (SUM(transaction.transactionamount),'0') AS Amount,
  IFNULL (SUM(statement.statementdebit),'0') AS NetCost,
  IFNULL((SUM(transaction.transactionamount) - SUM(statement.statementdebit)),'0') as TotalEarning
FROM
  transaction LEFT JOIN statement
ON
  transaction.transactionid = statement.transactionid AND
  transaction.transactiondate = '2019-04-03' AND
  transaction.transactionstatus = 'SUCCESS' 
GROUP BY
  `transaction`.transactionservicetype ASC

根据提供给我的结果,我们可以修改几乎尝试并仅对结果进行子查询,然后对结果进行分组以得到所需的输出:

SELECT r.Services as 'Services',SUM(r.Count) as 'Count', SUM(r.Amount) as 'Amount',SUM(r.NetCost) as 'NestCost', SUM(r.TotalEarnings) AS 'TotalEarnings'
FROM
(
    SELECT 
      transaction.transactionservicetype AS Services,
      COUNT(transaction.transactionid) AS Count,
      IFNULL (SUM(transaction.transactionamount),'0') AS Amount,
      IFNULL (SUM(statement.statementdebit),'0') AS NetCost,
      IFNULL((SUM(transaction.transactionamount) - SUM(statement.statementdebit)),'0') as TotalEarning
    FROM
      transaction RIGHT JOIN statement
    ON
      transaction.transactionid = statement.transactionid
    WHERE
      transaction.transactiondate = '2019-04-03' AND transaction.transactionstatus = 'SUCCESS' 
    GROUP BY
      `transaction`.transactionservicetype ASC

    UNION

    SELECT 
      transactionservicetype AS Services,
        '0' AS Count,
        '0' AS Amount,
      '0' AS NetCost,
      '0' AS TotalEarning
    FROM
      transaction
    GROUP BY
      transactionservicetype ASC
) r
GROUP BY r.Services;