【问题标题】:TSQL Sum by group for monthly reports每月报告的 TSQL 总和
【发布时间】:2013-05-15 12:13:41
【问题描述】:

我正在尝试针对我们数据库中的一组运输条目生成一份报告(每月、年初至今等),以显示正确的总和并按运输公司对其进行分组。我怎样才能完成获取所有这些信息并生成正确的报告?

需要注意的一点是,该表中的所有条目都是 nvarchar(max),因为文本导入时使用了来自各种来源的奇数字符。

主数据库列表如下所示:

表 - shipping_info

shipping_company | ship_date | shipping_category | shipping_cost

UPS              | 20130301  |  CD'S             |  3.50
UPS              | 20130310  |  Records          |  4.50
UPS              | 20130322  |  CD'S             |  7.50
UPS              | 20130313  |  Tapes            |  12.44
UPS              | 20130324  |  CD'S             |  3.50
UPS              | 20120312  |  Records          |  4.50
UPS              | 20120304  |  CD'S             |  5.50
UPS              | 20120306  |  Tapes            |  3.50
UPS              | 20130309  |  CD'S             |  3.50
USPS             | 20130301  |  Tapes            |  3.50
USPS             | 20130301  |  CD'S             |  5.50
USPS             | 20130301  |  CD'S             |  4.50
USPS             | 20130201  |  Tapes            |  3.50
USPS             | 20130201  |  CD'S             |  3.50
USPS             | 20130201  |  Records          |  7.50
USPS             | 20130201  |  Tapes            |  9.50
USPS             | 20130201  |  CD'S             |  12.50
USPS             | 20120301  |  CD'S             |  14.50
USPS             | 20120301  |  Records          |  3.50
USPS             | 20130301  |  CD'S             |  23.50
USPS             | 20120301  |  Tapes            |  15.50
USPS             | 20120301  |  CD'S             |  34.50
DHL              | 20120301  |  Tapes            |  35.50
DHL              | 20120301  |  CD'S             |  3.50
DHL              | 20130301  |  Tapes            |  3.50
DHL              | 20130401  |  CD'S             |  3.50
DHL              | 20130401  |  Records          |  4.50
DHL              | 20130501  |  CD'S             |  4.50
DHL              | 20120201  |  Tapes            |  5.50
DHL              | 20120101  |  CD'S             |  6.50
DHL              | 20120501  |  CD'S             |  3.50
DHL              | 20120301  |  Tapes            |  7.50

我的目标是获得一个最终列表,该列表将按 shipping_company 打印每月总运费的过滤列表。下线我想得到一份年初至今的清单和类似的东西。无论如何,我希望看到的月度报告是这样的:

UPS Shipping Cost Report for March 2013

CD's - $23.50
Records - $9.00
Tapes - $15.94

我尝试的是第一个

SELECT shipping_company, ship_date, shipping_category, shipping_cost FROM shipping_info WHERE (shipping_company = 'UPS' and shipping_cost IS NOT NULL and shipping_cost != '')  GROUP BY shipping_company, ship_date, shipping_category, shipping_cost ORDER BY shipping_company, ship_date, shipping_category, shipping_cost 

这产生了一个很棒的过滤表,但我仍然需要计算总和并按类别将总数组合在一起。

然后我尝试了这个....

SELECT shipping_company, ship_date, shipping_category, SUM(shipping_cost) FROM shipping_info WHERE (shipping_company = 'UPS' and shipping_cost IS NOT NULL and shipping_cost != '')  GROUP BY shipping_company, ship_date, shipping_category, shipping_cost ORDER BY shipping_company, ship_date, shipping_category, shipping_cost 

由于这是一个 NVARCHAR(MAX) 表,t-sql 报错,告诉我需要将 shipping_cost 转换为 int。

所以....我接下来尝试了这个。

SELECT shipping_company, ship_date, shipping_category, convert(decimal(10, 2), shipping_cost) FROM shipping_info WHERE (shipping_company = 'UPS' and shipping_cost IS NOT NULL and shipping_cost != '')  GROUP BY shipping_company, ship_date, shipping_category, shipping_cost ORDER BY shipping_company, ship_date, shipping_category, shipping_cost 

哪种有效,哪种无效。

现在,在将此列表分解为可报告的部分时,我不知道从第一个示例中的哪个方向继续进行。我是否走在正确的道路上,可以将我的信息转换为我需要的月度报告格式?

::编辑/更新::

这很棒,而且很有效。我花时间浏览了这段代码的每一步,现在我理解了这里发生的事情背后的理论。我在这里纠结的最后一件事。由于此表是 NVARCHAR(MAX),因此我在尝试将 ship_date 转换为数字时遇到了挑战,但没有成功。

我采用了下面的原始代码 - 示例 1:

select  
CASE WHEN (GROUPING(shipping_company) = 1) THEN 'TOTAL'
            ELSE shipping_company END AS shipping_company,
CASE WHEN (GROUPING(SUBSTRING(ship_date, 1, 6)) = 1) THEN 'TOTAL'
            ELSE SUBSTRING(ship_date, 1, 6) END AS Date,
CASE WHEN (GROUPING(shipping_category) = 1) THEN 'Yearly TOTAL'
            ELSE shipping_category END AS shipping_category,
sum(convert(decimal(10,2),shipping_cost))
from tbl
group by shipping_company, SUBSTRING(ship_date, 1, 6), 
shipping_category WITH rollup

然后添加如下所示的数字转换命令 - 示例 2:

select  
CASE WHEN (GROUPING(shipping_company) = 1) THEN 'TOTAL'
            ELSE shipping_company END AS shipping_company,
CASE WHEN (GROUPING(CONVERT(numeric, (SUBSTRING(ship_date, 1, 6))) = 1) THEN 'TOTAL'
            ELSE (CONVERT(numeric, SUBSTRING(ship_date, 1, 6))) END AS Date,
CASE WHEN (GROUPING(shipping_category) = 1) THEN 'Yearly TOTAL'
            ELSE shipping_category END AS shipping_category,
sum(convert(decimal(10,2),shipping_cost))
from tbl
group by shipping_company, (CONVERT(numeric, SUBSTRING(ship_date, 1, 6))), 
shipping_category WITH rollup

但仍然得到将 nvarchar 转换为数字的错误。然后我像这样移动了一些东西 - 示例 3:

select  
CASE WHEN (GROUPING(shipping_company) = 1) THEN 'TOTAL'
            ELSE shipping_company END AS shipping_company,
CASE WHEN (CONVERT(numeric, GROUPING(SUBSTRING(ship_date, 1, 6))) = 1) THEN 'TOTAL'
            ELSE (CONVERT(numeric, SUBSTRING(ship_date, 1, 6))) END AS Date,
CASE WHEN (GROUPING(shipping_category) = 1) THEN 'Yearly TOTAL'
            ELSE shipping_category END AS shipping_category,
sum(convert(decimal(10,2),shipping_cost))
from tbl
group by shipping_company, (CONVERT(numeric, SUBSTRING(ship_date, 1, 6))), 
shipping_category WITH rollup

并得到“分组函数的参数 1 与 GROUP BY 子句中的任何表达式都不匹配”,这是有道理的。我真的很想了解背后的理论,不仅是为什么第二个示例不起作用,而且是什么正确的行动方案才能使转换起作用。

【问题讨论】:

  • 我不知道为什么你的第二个例子没有工作...... :P 因为它在这里工作sqlfiddle.com/#!6/197c9/14你在第二个查询中收到什么错误??
  • @Meherzad - 当我执行“示例 2”的查询时,我得到“将数据类型 nvarchar 转换为数字时出错”。当我执行“示例 3”时,我得到“分组函数的参数 1 与 GROUP BY 子句中的任何表达式都不匹配。”

标签: sql sql-server tsql sql-server-2012


【解决方案1】:

试试这个查询

我已经从日期中提取了年份和月份部分,因此对特定年份的每个月进行分组。

select 
   shipping_company, 
   SUBSTRING(ship_date, 1, 6), 
   shipping_category, 
   sum(convert(decimal(10,2),shipping_cost))
from 
   tbl
group by 
   shipping_company, 
   SUBSTRING(ship_date, 1, 6), 
   shipping_category

SQL FIDDLE

| SHIPPING_COMPANY | COLUMN_1 | SHIPPING_CATEGORY | COLUMN_3 |
--------------------------------------------------------------
|              DHL |   201201 |              CD'S |      6.5 |
|              DHL |   201202 |              CD'S |      5.5 |
|              DHL |   201203 |              CD'S |     46.5 |
|              DHL |   201205 |              CD'S |      3.5 |
|              DHL |   201303 |              CD'S |      3.5 |
|              DHL |   201304 |              CD'S |        8 |
|              DHL |   201305 |              CD'S |      4.5 |
|              UPS |   201203 |              CD'S |      5.5 |
|              UPS |   201203 |           Records |      4.5 |
|              UPS |   201203 |             Tapes |      3.5 |
|              UPS |   201303 |              CD'S |       18 |
|              UPS |   201303 |           Records |      4.5 |
|              UPS |   201303 |             Tapes |    12.44 |
|             USPS |   201203 |              CD'S |       68 |
|             USPS |   201302 |              CD'S |     36.5 |
|             USPS |   201303 |              CD'S |       37 |

如果您想要年度总数,则可以使用WITH ROLLUP

注意将年度总计视为每月总计...

查询 1

select  
CASE WHEN (GROUPING(shipping_company) = 1) THEN 'TOTAL'
            ELSE shipping_company END AS shipping_company,
CASE WHEN (GROUPING(SUBSTRING(ship_date, 1, 6)) = 1) THEN 'TOTAL'
            ELSE SUBSTRING(ship_date, 1, 6) END AS Date,
CASE WHEN (GROUPING(shipping_category) = 1) THEN 'Yearly TOTAL'
            ELSE shipping_category END AS shipping_category,
sum(convert(decimal(10,2),shipping_cost))
from tbl
group by shipping_company, SUBSTRING(ship_date, 1, 6), 
shipping_category WITH rollup

SQL FIDDLE

| SHIPPING_COMPANY |   DATE | SHIPPING_CATEGORY | COLUMN_3 |
------------------------------------------------------------
|              DHL | 201201 |              CD'S |      6.5 |
|              DHL | 201201 |      Yearly TOTAL |      6.5 |
|              DHL | 201202 |              CD'S |      5.5 |
|              DHL | 201202 |      Yearly TOTAL |      5.5 |
|              DHL | 201203 |              CD'S |     46.5 |
|              DHL | 201203 |      Yearly TOTAL |     46.5 |
|              DHL | 201205 |              CD'S |      3.5 |
|              DHL | 201205 |      Yearly TOTAL |      3.5 |
|              DHL | 201303 |              CD'S |      3.5 |
|              DHL | 201303 |      Yearly TOTAL |      3.5 |
|              DHL | 201304 |              CD'S |        8 |
|              DHL | 201304 |      Yearly TOTAL |        8 |
|              DHL | 201305 |              CD'S |      4.5 |
|              DHL | 201305 |      Yearly TOTAL |      4.5 |
|              DHL |  TOTAL |      Yearly TOTAL |       78 |
|              UPS | 201203 |              CD'S |      5.5 |
|              UPS | 201203 |           Records |      4.5 |
|              UPS | 201203 |             Tapes |      3.5 |
|              UPS | 201203 |      Yearly TOTAL |     13.5 |
|              UPS | 201303 |              CD'S |       18 |
|              UPS | 201303 |           Records |      4.5 |
|              UPS | 201303 |             Tapes |    12.44 |
|              UPS | 201303 |      Yearly TOTAL |    34.94 |
|              UPS |  TOTAL |      Yearly TOTAL |    48.44 |
|             USPS | 201203 |              CD'S |       68 |
|             USPS | 201203 |      Yearly TOTAL |       68 |
|             USPS | 201302 |              CD'S |     36.5 |
|             USPS | 201302 |      Yearly TOTAL |     36.5 |
|             USPS | 201303 |              CD'S |       37 |
|             USPS | 201303 |      Yearly TOTAL |       37 |
|             USPS |  TOTAL |      Yearly TOTAL |    141.5 |
|            TOTAL |  TOTAL |      Yearly TOTAL |   267.94 |

编辑

好的,您在更新后的查询中遇到了问题。 If 语句的两种情况都应返回相同的结果数据类型,因此当您尝试将日期值转换回数字时,您的真实情况返回 varchar 数据类型字段,即 TOTAL,else 字段返回 numeric 类型字段所以这会导致错误。

要解决它,您需要删除 case 语句,然后它会根据您的需要正常工作。

select  
CASE WHEN (GROUPING(shipping_company) = 1) THEN 'TOTAL'
            ELSE shipping_company END AS shipping_company,
CONVERT(numeric, (SUBSTRING(ship_date, 1, 6))) AS Date,
CASE WHEN (GROUPING(shipping_category) = 1) THEN 'Yearly TOTAL'
            ELSE shipping_category END AS shipping_category,
sum(convert(decimal(10,2),shipping_cost))
from tbl
group by shipping_company, (CONVERT(numeric, SUBSTRING(ship_date, 1, 6))), 
shipping_category WITH rollup

SQL FIDDLE

| SHIPPING_COMPANY |   DATE | SHIPPING_CATEGORY | COLUMN_3 |
------------------------------------------------------------
|              DHL | 201201 |              CD'S |      6.5 |
|              DHL | 201201 |      Yearly TOTAL |      6.5 |
|              DHL | 201202 |              CD'S |      5.5 |
|              DHL | 201202 |      Yearly TOTAL |      5.5 |
|              DHL | 201203 |              CD'S |     46.5 |
|              DHL | 201203 |      Yearly TOTAL |     46.5 |
|              DHL | 201205 |              CD'S |      3.5 |
|              DHL | 201205 |      Yearly TOTAL |      3.5 |
|              DHL | 201303 |              CD'S |      3.5 |
|              DHL | 201303 |      Yearly TOTAL |      3.5 |
|              DHL | 201304 |              CD'S |        8 |
|              DHL | 201304 |      Yearly TOTAL |        8 |
|              DHL | 201305 |              CD'S |      4.5 |
|              DHL | 201305 |      Yearly TOTAL |      4.5 |
|              DHL | (null) |      Yearly TOTAL |       78 |
|              UPS | 201203 |              CD'S |      5.5 |
|              UPS | 201203 |           Records |      4.5 |
|              UPS | 201203 |             Tapes |      3.5 |
|              UPS | 201203 |      Yearly TOTAL |     13.5 |
|              UPS | 201303 |              CD'S |       18 |
|              UPS | 201303 |           Records |      4.5 |
|              UPS | 201303 |             Tapes |    12.44 |
|              UPS | 201303 |      Yearly TOTAL |    34.94 |
|              UPS | (null) |      Yearly TOTAL |    48.44 |
|             USPS | 201203 |              CD'S |       68 |
|             USPS | 201203 |      Yearly TOTAL |       68 |
|             USPS | 201302 |              CD'S |     36.5 |
|             USPS | 201302 |      Yearly TOTAL |     36.5 |
|             USPS | 201303 |              CD'S |       37 |
|             USPS | 201303 |      Yearly TOTAL |       37 |
|             USPS | (null) |      Yearly TOTAL |    141.5 |
|            TOTAL | (null) |      Yearly TOTAL |   267.94 |

所以现在您必须忍受日期列中的空值。希望这能解答您的疑问。

希望这会有所帮助......

【讨论】:

  • 这很好用,除了我坚持将这个谜题的 nvarchar 部分转换为数字。我一般来说转换是如何工作的,但在您提供的代码中并不复杂。我在此线程的第一篇文章中发布了我所经历的示例。我现在很难过。
  • 好的,您在更新的查询中遇到了问题。 If 语句的两种情况都应返回相同的结果数据类型,因此当您尝试将日期值转换回数字时,您的真实情况返回 varchar 数据类型字段,即 TOTAL,else 字段返回 numeric类型字段,因此这会导致错误。要解决它,您需要删除 case 语句,然后它会根据您的需要正常工作。在这里检查小提琴sqlfiddle.com/#!6/197c9/31。所以现在你必须忍受日期列中的空值。希望这能解答你的疑惑..
  • 效果很好!从非必要或不可转换的数据中清除表后,它就像一个魅力。我还能够将脚本分解成更小的部分,以便更好地了解事情是如何全面运作的。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2020-06-29
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多