【问题标题】:How to best to select items from other tables when grouping by primary keys按主键分组时如何最好地从其他表中选择项目
【发布时间】:2019-02-08 17:57:32
【问题描述】:

我已经发生了几次以下情况,我的解决方案感觉很粗糙。

一般来说,我的表格可能如下所示:

WorkOrder:
    WorkOrderID (primary key)
    WorkOrderDesc

Process:
   ProcessID (primary key)
   ProcessDesc

Work:
   ProcessID
   WorkOrderID

我想知道一个工单上完成了多少个流程,所以我做了类似的事情

select w.WorkOrderId,w.ProcessID,Count(*)
from Work as w
    inner join WorkOrder as wo on w.WorkOrderID=wo.WorkOrderID
    inner join Process as p on w.ProcessID = p.ProcessID
group by w.WorkOrderID,w.ProcessID

此代码将告诉我每个流程在每个工单上运行了多少次。

我遇到的问题是我真的不希望查询结果成为索引,我希望它成为描述,因为这些将被报告或绘制。因为它是 group by 我知道 WorkOrderIDProcessID 是独一无二的这些条目的最大值(或最小值)。

select w.WorkOrderId,w.ProcessID,Count(*),Max(w.WorkOrderDesc),Max(p.ProcessDesc)
from Work as w
    inner join WorkOrder as wo on w.WorkOrderID=wo.WorkOrderID
    inner join Process as p on w.ProcessID = p.ProcessID
group by w.WorkOrderID,w.ProcessID

这个查询给了我 ID,就像第一个查询一样,但它也填写了描述。

有没有办法在不使用这个 hack 的情况下将这种类型的查询作为单个查询?我知道我可以使用 CTE 将第一个查询的结果第二次加入以从两个表中获取描述,但这似乎最终会再次运行相同的(ish)查询。

查询引擎似乎可以检测到我正在按主键进行分组,并允许我在 select 语句中从该行中选择项目。

注意:我意识到我可以只使用这个玩具示例中的描述来完成查询,在一个真实的示例中,每个查询都会在查询中报告许多项目,这些项目都来自连接的表。

【问题讨论】:

  • 不知道你所说的黑客是什么意思;你写的只是一个聚合查询。但是(如果我理解您要正确说的话)不,T-SQL 不支持隐含的 GROUP BY 语句。您必须明确说明要聚合的值以及聚合的值。
  • 如果我知道它们都是相同的,因为它们都来自我使用主键分组的表,那么 SQL 引擎也应该看到这一点。似乎可以证明,当按主键分组时,可以安全地选择主键表中行中的每个值,而无需使用 Max() 或 Min() 修饰它或运行第二个查询。
  • 如果它们都相同,则包括GROUP BY 中的列。 T-SQL 旨在不允许隐含的GROUP BY 语句,它们必须是显式的。如果您从GROUP BY 中省略一列并且不使用聚合函数将其包围,那么您将收到错误消息。它们是 T-SQL 的设计方式。如果您对该功能不满意,您将不得不使用允许这种语法的不同 RDBMS;比如 MySQL。您所说的行为不太可能(很快)会改变;它肯定不在 SQL Server 2019 Preview 中。
  • 为什么不将 w.WorkOrderDesc 添加到 group by 并将其从聚合中删除?它不会给出相同的结果吗?它应该,如果它也真的很独特的话
  • @Larnu,这并不是“隐含的分组依据”。如果我按唯一键分组,则由该键定义的行的元素是唯一的,并且可以安全地添加到 select 语句中,没有歧义。看来这不是 tsql 的特性。 (你应该写一个你不能用tsql回答这个问题)

标签: sql-server tsql


【解决方案1】:

我会这样写查询:

SELECT wo.WorkOrderDesc, p.ProcessDesc, x.Cnt
FROM (
    SELECT w.WorkOrderId,w.ProcessID,Count(*) AS Cnt
    from Work as w
    group by w.WorkOrderID,w.ProcessID
) x
inner join WorkOrder as wo on x.WorkOrderID=wo.WorkOrderID
inner join Process as p on x.ProcessID = p.ProcessID

这样,分组是使用 ID 完成的,但我们稍后会检索结果中出现的每个 ID 的描述。

【讨论】:

  • 这似乎是最干净的方法。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-07-15
  • 1970-01-01
  • 2018-01-23
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多