过滤 SQL 查询返回的结果

Posted

技术标签:

【中文标题】过滤 SQL 查询返回的结果【英文标题】:Filtering of results returned by SQL query 【发布时间】:2012-01-08 03:56:55 【问题描述】:

我整个下午都在努力解决这个问题 - 看起来很简单,但我一定错过了什么!

我有一个返回一些数据的查询,它返回的两列是“PackageWeight”和“PackageGroup”。本质上,我想过滤这些数据,只为每个“PackageGroup”显示一行 - 这应该是“PackageWeight”列中值最高的行。

这看起来很简单,但我无法使用 TOP 1 和 GROUP BY 的组合在 SQL Server 中工作。我一定是错过了什么!

    SELECT VendorID, PackageID, PackageWeight, PackageGroup
  FROM (SELECT VendorID, COUNT(*) AS qty
          FROM VendorServices
         GROUP BY VendorID
       ) cs
  JOIN (SELECT PackageServices.PackageID, lookupPackages.PackageWeight, lookupPackages.PackageGroup, COUNT(*) AS qty
          FROM PackageServices
          JOIN lookupPackages ON PackageServices.PackageID = lookupPackages.PackageID
          GROUP BY PackageServices.PackageID, lookupPackages.PackageWeight, lookupPackages.PackageGroup
       ) ps ON cs.qty >= ps.qty
  WHERE (SELECT COUNT(*)
          FROM VendorServices cs2
          JOIN PackageServices ps2 ON cs2.ServiceTypeID = ps2.ServiceID
         WHERE cs2.VendorID = cs.VendorID
           AND ps2.PackageID = ps.PackageID
       ) = ps.qty

此查询返回我需要过滤的完整数据集。但是到目前为止我的尝试都失败了:(

非常感谢任何帮助!

编辑 - 感谢下面的贡献者,到目前为止我有以下查询:

with result_cte as
(
SELECT VendorID, PackageID, PackageWeight, PackageGroup,
    RANK() over (partition by PackageGroup order by PackageWeight desc) as [rank]
FROM (SELECT VendorID, COUNT(*) AS qty
    FROM VendorServices
    GROUP BY VendorID
    ) cs
JOIN (SELECT PackageServices.PackageID, lookupPackages.PackageWeight, lookupPackages.PackageGroup, COUNT(*) AS qty
    FROM PackageServices
    JOIN lookupPackages ON PackageServices.PackageID = lookupPackages.PackageID
    GROUP BY PackageServices.PackageID, lookupPackages.PackageWeight, lookupPackages.PackageGroup
    ) ps ON cs.qty >= ps.qty
WHERE (SELECT COUNT(*)
    FROM VendorServices cs2
    JOIN PackageServices ps2 ON cs2.ServiceTypeID = ps2.ServiceID
    WHERE cs2.VendorID = cs.VendorID
    AND ps2.PackageID = ps.PackageID
    ) = ps.qty
)

select *
from result_cte
WHERE [rank] = 1
ORDER BY VendorID

到目前为止,一切都很好。我仍然会看一下@gbn 建议的 APPLY 运算符,因为这对我来说是新的 - 我仍然需要做一些测试以确保这个查询在 100% 的时间内工作。不过初步迹象很好!

感谢迄今为止做出贡献的所有人。

编辑 2 - 遗憾的是,在使用更多示例数据填充数据库后,此查询无法正常工作。好像漏掉了一些条目。

也许我需要多解释一下这里发生了什么。我的原始查询返回的数据列出了系统中的每个客户,以及派生的 PackageID(由该查询计算)以及在查找表中分配给该包裹的权重和组。

我需要过滤原始结果表,以便我从每个组中为每个客户获得不超过一个包(每个客户可能从一个或多个组中获得一个包,但可能不会从每个组中获得一个包)

明天我会重新审视这个问题,因为我想我可能会处于“只见树木不见森林”的境地!

谢谢大家。

【问题讨论】:

SQL Server - SELECT TOP 5 rows for each FK 或 ***.com/q/1164483/27535 的可能副本。还有几十个:***.com/questions/tagged/greatest-n-per-group 您使用的是什么版本的 SQL Server? @JimmE:在这种情况下,我提供的所有链接都是有效的。 @gbn - 感谢您的指点。这看起来很简单,但由于某种原因,我很难让它像我期望的那样工作。我之前没有在您的一个示例中遇到过 CROSS APPLY 操作 - 所以我会去看看。非常感谢。 @gbn - 是的,你应该看看其中一个链接。在早期版本中,这可能会变得更加棘手。 【参考方案1】:

你可以试试这个吗?如果您在同一组中有多个具有相同权重的记录,则它不是防弹的。还有其他处理方法。

with result_cte as
(
SELECT VendorID, PackageID, PackageWeight, PackageGroup
FROM (SELECT VendorID, COUNT(*) AS qty
    FROM VendorServices
    GROUP BY VendorID
    ) cs
JOIN (SELECT PackageServices.PackageID, lookupPackages.PackageWeight, lookupPackages.PackageGroup, COUNT(*) AS qty
    FROM PackageServices
    JOIN lookupPackages ON PackageServices.PackageID = lookupPackages.PackageID
    GROUP BY PackageServices.PackageID, lookupPackages.PackageWeight, lookupPackages.PackageGroup
    ) ps ON cs.qty >= ps.qty
WHERE (SELECT COUNT(*)
    FROM VendorServices cs2
    JOIN PackageServices ps2 ON cs2.ServiceTypeID = ps2.ServiceID
    WHERE cs2.VendorID = cs.VendorID
    AND ps2.PackageID = ps.PackageID
    ) = ps.qty
)

select *
from result_cte
where result_cte.PackageWeight = (select top 1 highestweight.PackageWeight from result_cte highestweight
                                where highestweight.PackageGroup = result_cte.PackageGroup
                                order by highestweight.PackageWeight desc)

或者你可以这样做:

with result_cte as
(
SELECT VendorID, PackageID, PackageWeight, PackageGroup,
    ROW_NUMBER() over (partition by PackageGroup order by PackageWeight desc) as [row]
FROM (SELECT VendorID, COUNT(*) AS qty
    FROM VendorServices
    GROUP BY VendorID
    ) cs
JOIN (SELECT PackageServices.PackageID, lookupPackages.PackageWeight, lookupPackages.PackageGroup, COUNT(*) AS qty
    FROM PackageServices
    JOIN lookupPackages ON PackageServices.PackageID = lookupPackages.PackageID
    GROUP BY PackageServices.PackageID, lookupPackages.PackageWeight, lookupPackages.PackageGroup
    ) ps ON cs.qty >= ps.qty
WHERE (SELECT COUNT(*)
    FROM VendorServices cs2
    JOIN PackageServices ps2 ON cs2.ServiceTypeID = ps2.ServiceID
    WHERE cs2.VendorID = cs.VendorID
    AND ps2.PackageID = ps.PackageID
    ) = ps.qty
)

select *
from result_cte
where [row] = 1

【讨论】:

与基于 ROW_NUMBER 或 APPLY 的查询相比,即使有效,这也是非常混乱的 我不是要解决作者的问题。我只是想展示不同的技术来获得所需的结果。我提供的查询需要验证是否正确。 @Eric.K.Yung - 非常感谢 - 我稍作修改以使用 RANK() 而不是 ROW_NUMBER() 因为 ROW_NUMBER() 似乎没有返回客户没有的行每个组的一个包。我会将更新后的查询作为编辑粘贴到 OP。 @JimmE - 我很高兴你可以使用它。我没有表格和数据来验证查询。【参考方案2】:

你可以使用 MAX 函数:

SELECT * FROM #one
lbs groups
5   0
4   0
1   0
9   1
2   1     

SELECT groups,MAX(lbs)
FROM #one
GROUP BY groups

groups  (No column name)
0   5
1   9

【讨论】:

【参考方案3】:

如果多个包裹在一组中具有相同的最大重量,您是否愿意接受单个任意供应商和PackageID?如果可以,只需在它们上加上PackageWeight

SELECT max(VendorID), max(PackageID), max(PackageWeight), PackageGroup
...
GROUP BY PackageGroup

否则,您将需要像 E.Y.建议并执行嵌套查询,首先找到每个组的最大权重,然后自己处理重复项(如果有)。

【讨论】:

谢天谢地,应用程序的性质使得每个包在组中都有唯一的权重。这实际上是首先存在加权/分组的原因 - 强制执行围绕将包分配给客户的业务规则。【参考方案4】:

感谢 Eric.K.Yung 的帖子 - 我终于使用他的查询解决了这个问题,但将 VendorID(实际上是 CustomerID)添加到查询的“分区依据”部分。这样可以确保为所有客户退回包裹。

感谢所有做出贡献的人。最后的查询是:

with result_cte as
(
SELECT VendorID, PackageID, PackageWeight, PackageGroup,
    ROW_NUMBER() over (partition by PackageGroup, VendorID order by PackageWeight desc) as [row]
FROM (SELECT VendorID, COUNT(*) AS qty
    FROM VendorServices
    GROUP BY VendorID
    ) cs
JOIN (SELECT PackageServices.PackageID, lookupPackages.PackageWeight, lookupPackages.PackageGroup, COUNT(*) AS qty
    FROM PackageServices
    JOIN lookupPackages ON PackageServices.PackageID = lookupPackages.PackageID
    GROUP BY PackageServices.PackageID, lookupPackages.PackageWeight, lookupPackages.PackageGroup
    ) ps ON cs.qty >= ps.qty
WHERE (SELECT COUNT(*)
    FROM VendorServices cs2
    JOIN PackageServices ps2 ON cs2.ServiceTypeID = ps2.ServiceID
    WHERE cs2.VendorID = cs.VendorID
    AND ps2.PackageID = ps.PackageID
    ) = ps.qty
)

select *
from result_cte
where [row] = 1

【讨论】:

根据您的最终查询与其所基于的答案或实际上与此处的其他答案有多大不同,您可能会考虑将其添加到您的答案中。我的意思是,有人可能会发现它与此线程中发布的任何其他正确解决方案一样有用。 @AndriyM - 当然 - 非常感谢。最终查询已添加到我的答案中。

以上是关于过滤 SQL 查询返回的结果的主要内容,如果未能解决你的问题,请参考以下文章

从 SQL 结果中过滤大数组

使用 PL/SQL 游标为报告工具返回结果集

SQL中Where与Having的区别

sql查询语句并不是最先执行SELECT

DB2 SQL 通过评估具有两种类型条目的 ID 过滤查询结果

SQL 日期过滤器:当开始日期 = 结束日期时返回结果