【问题标题】:SQL - How to average by group for a single column?SQL - 如何按组平均单个列?
【发布时间】:2021-03-22 22:10:04
【问题描述】:

我试图让我的查询在 WO 分组中平均一列,这样我就可以摆脱影响另一列计算的重复项。一点背景 - 在这个数据集中,一个工作订单可以有一个或多个签入/签出组合。旅行/准备时间记录在 WO 级别,但是我需要我的数据在更精细的个人入住/退房组合级别。

我遇到的问题是,工作订单的每个签入/签出组合都会重复旅行时间,这夸大了实际时间。我希望有人可以帮助我弄清楚a)如何平均整个组的旅行时间或b)将重复的实例更改为Null或零,以便我可以获得准确的时间。在所附链接的示例中,您可以看到工作订单“PGE083300001”重复计算了这两种不同入住/退房组合的旅行时间。 7.5 hrs 是工单的总时间,但该列应显示两个条目的时间为 3.75。

SQL 语句如下:

SELECT
a.WorkOrderCustomID, 
b.[Work Order #],
b.[checkIn Date],
b.[checkOut Date],
CONVERT(decimal (16,2),[Windshield/Travel/Prep Time (Hours)]) AS TravelPrepAvg
    FROM clientssdv.vw_Corrigo_WorkOrder_Custom a
     INNER JOIN ssdv.vw_Corrigo_vbiWorkOrderCheckInOuts b on a.WorkOrderCustomID = b.[Work Order ID]
     INNER JOIN ssdv.vw_Corrigo_vbiWorkOrders c ON b.[Work Order ID] = c.ID
        WHERE Status <> 'Cancelled'
          ORDER BY [Work Order ID]

[

【问题讨论】:

  • 这里的大多数人希望样本表数据和预期结果为格式化文本,而不是图像(也不是图像链接)。如果可能的话,请简化 - minimal reproducible example。跨度>
  • 哇,为什么 7.5 应该平均分配在两个条目上?您需要向我们展示 WindshieldTravelPrep Time (hours) 的值,而我们可以在这里推测,将原始输入输入到您的等式中会有所帮助
  • 下次我会记住这一点。我能够通过使用滞后功能并将重复项变为空来解决此问题。感谢您的回复。

标签: sql group-by grouping average


【解决方案1】:

我怀疑有一种更简洁的方法,但如果没有更多关于您的架构的详细信息,我们只能进行理论化。因此,即使我认为这不是解决您的问题的最佳解决方案,当数据不是我们在 MS SQL Server 中所需的形式(自 2012 年以来)时,您可以在窗口查询上使用大多数标准聚合。这种技术通常用于计算运行总计或滚动平均值。

从您的屏幕截图中获取一组虚构的数据:

WO# TimeWorked Windshield
PGE1264 0.1 NULL
PGE1110 0.25 NULL
PGE2458 0.4 NULL
PGE2459 0.97 4
PGE2459 0.18 2
PGE0833 0.0 7.5
PGE0833 0.02 7.5
PGE0870 0.02 NULL

我们可以通过两种方式查询这个集合,要么为每个 WO 生成一条记录,要么对整个范围内的值进行规范化。

请注意,我已经为 PGE2459 注入了一些其他值来证明这一点

DECLARE @data as Table
(
    [WO] char(12),
    TimeWorked NUMERIC(10,2),
    Windsheild NUMERIC(10,2) NULL
)
INSERT INTO @data ([WO],TimeWorked,Windsheild)
VALUES
('PGE1264',0.1  ,NULL),
('PGE1110',0.25 ,NULL),
('PGE2458',0.4  ,NULL),
('PGE2459',0.97 ,4 ),
('PGE2459',0.18 ,2 ),
('PGE0833',0.0  ,7.5 ),
('PGE0833',0.02 ,7.5 ),
('PGE0870',0.02 ,NULL)

SELECT [WO], AVG(Windsheild)
FROM @data
GROUP BY [WO]

SELECT [WO], AVG(Windsheild) OVER (PARTITION BY [WO]) / COUNT(Windsheild) OVER  (PARTITION BY [WO])
FROM @data

使用简单的GROUP BY,表 1 已被缩减为每个WO 仅包含一条记录,这通常是最容易加入的形式:

WO
PGE0833 7.500000
PGE0870 NULL
PGE1110 NULL
PGE1264 NULL
PGE2458 NULL
PGE2459 3.000000

如果您不能对整个集合进行分组,那么您仍然可以使用Window Functions 来评估内联聚合,即第二个查询的结果:

WO
PGE0833 3.750000
PGE0833 3.750000
PGE0870 NULL
PGE1110 NULL
PGE1264 NULL
PGE2458 NULL
PGE2459 1.500000
PGE2459 1.500000

请注意,在此查询中,我们如何获得所有行的平均值,然后返回均匀分布在受影响行上的值,这样SUM,COUNT,AVG 在结果集上的聚合仍将返回与原始查询相同的值。

【讨论】:

  • 感谢您的帮助!实际上,我在帖子中选择了选项 B,并且能够找到另一个包含一些适用代码的 stackflow 帖子。使用滞后函数解决了这个问题。稍后我会发布我的答案。
【解决方案2】:

代码修复

我能够找到另一篇文章,其中显示了类似的代码,使用 lag 函数在第一次输入后将所有内容变为空。我只会发布我需要更改的字段的相关部分,以防大家好奇。

CASE
            WHEN CONVERT(decimal (16,2),[Windshield/Travel/Prep Time (Hours)]) = LAG(CONVERT(decimal (16,2),[Windshield/Travel/Prep Time (Hours)]),1,NULL)
            OVER 
                (ORDER BY b.[Work Order ID], a.[WO#], b.[CheckIn Date]) THEN NULL ELSE CONVERT(decimal (16,2),[Windshield/Travel/Prep Time (Hours)])
        END AS Windshield_TravelPrepTime_hrs

这是新的、正确的输出:

 WO#           CheckIn Date   CheckOut Date TimeWorked  Windshield_TravelPrepTime_hrs
PGE088000171    18:00.0       30:00.0         2.2          -84.5
PGE088000171    06:00.0       57:00.0         1.85          NULL
PGE088000171    39:00.0       30:00.0         2.85          NULL
PGE088000171    28:00.0       04:00.0         1.6           NULL

这是我的第一篇文章,所以我很高兴有人伸出援手。再次感谢!

【讨论】:

  • 您可能已经找到了一种解决方法,但老实说,我相信您提供了来自 3 个表中的每一个的原始数据(只是一小部分样本,但它们必须在原始查询,包括连接),那么可能有更有效的方法可用,我猜如果你在加入之前在ssdv.vw_Corrigo_vbiWorkOrderCheckInOuts 上使用 s 子查询,你可能会避免重复(你称之为重复)。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2021-07-01
  • 1970-01-01
  • 2013-11-24
  • 2011-10-21
  • 2017-04-18
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多