【问题标题】:SQL Composite key grouping issueSQL 复合键分组问题
【发布时间】:2012-02-27 02:08:58
【问题描述】:

我遇到了一个非常令人沮丧的 SQL 问题,我一生都无法通过派生查询返回复合键但还在该表中的另一个字段上执行 MIN() 聚合函数来解决这个问题。如果我在其中一个复合键上执行 MIN() 会很容易,但是由于我需要返回两个键并对外部查询执行 MIN() 函数,所以我无法弄清楚如何执行此操作.整个查询如下所示:

SELECT
    p.name as productname
   ,tmp.packageid
   ,tmp.price
   ,ppk2.packageoptionid
   ,ppk2.selcomproductid
FROM ( 
        SELECT ppk.productid, ppk.packageid, MIN(ppk.price) as price
        FROM  product_package ppk
                 INNER JOIN package pk ON ppk.packageid = pk.id
                 INNER JOIN [plan] pl ON pk.planid = pl.id
        WHERE pk.networkid = 1
        GROUP BY ppk.productid, ppk.packageid
) tmp
INNER JOIN product_package ppk2 ON ( 
        ppk2.productid = tmp.productid 
    AND ppk2.packageid = tmp.packageid
)    
INNER JOIN product p ON (p.id = ppk2.productid)  
WHERE p.isenabled = 1;

当前结果:

--------------------------------------
productid   |   packageid   |   price
1               500             0
1               501             19.95
1               502             29.95
2               501             0
3               500             15    
3               504             39.95 

期望的结果:

--------------------------------------
productid   |   packageid   |   price
1               500             0
2               501             0
3               500             15  

派生查询“tmp”是我的问题所在,因为在加入外部表之前,我需要为每个产品/包装组合以最低价格返回唯一的行。

任何帮助将不胜感激!

【问题讨论】:

  • 好问题。现在:您能否注册pastebin account,这样我们就不必查看stackoverflow 在您的代码周围放置的那些丑陋的滑块? :)
  • @Droogans 或者你可以格式化代码...
  • @nineside: 好样的,先生。
  • @Droogans:我不了解你,但我跳过任何涉及 pastebin 帐户的问题。
  • 感谢 Nineside 的编辑,抱歉,我不应该考虑较低的分辨率。

标签: sql group-by composite-key unique-constraint


【解决方案1】:

每当我需要一个子查询以及最小的东西时,我都会使用这个技巧。这个想法是将值和键与最高有效位中的值组合在一起并取其最小值。然后在外层选择中拆开。

组合到值的最佳方式取决于您使用的 RDBMS。你没有提到你使用的是哪一个,所以我只是提供伪代码:

select ..., (tmp.c >> 32) price
from
(select productid, min((price << 32) | packageid) c
  from product_package
  where networkid=1
  group by productid) tmp
inner join product_package ppk on ppk.productid=tmp.productid
  and ppk.packageid=(tmp.c & 0xFFFFFFFF)
inner join product p on p.id=ppk.productid
where p.isenabled=1

&lt;&lt; 32 表示将值向左移动 32 位,| 是按位“或”。所以这是假设 packageid 被定义为一个 32 位整数(或数字(4))。 &amp; 0xFFFFFFFF 是按位“与”和 32 位的十六进制值,用于屏蔽并仅返回 packageid。

根据您的 RDBMS,您可能需要找到这些东西的特定语法,或者如果它们不受支持,您可以使用普通数学 - &lt;&lt; 32 相当于乘以 4294967296 和 &amp; 0xFFFFFFFF 等于除以 4294967296 . 如果您使用的是 MSSQL,您可以使用convert(binary,price)+convert(binary,packageid) 将它们组合起来,并使用substring(..) 将它们分开。

【讨论】:

  • 感谢 Gordy,这是另一个有趣的答案,我正在尝试类似的方法,将价格和 packageid 一起投射到 MIN() 中,但它实在是太丑陋了。你的方法更优雅,我没想过在这种情况下使用数学。
  • @PhilRasmussen:我想这取决于您如何衡量查询的丑陋程度?我建议将这种方法的执行计划与您接受的答案中的执行计划进行比较。如果你真的想美化它,你可以为 Min 编写一个用户定义的聚合,它需要两个参数。
【解决方案2】:

好吧,我不知道您的表中实际拥有的数据。我只有您的查询返回的数据。你没有回答我的评论,要求提供你的表和你正在使用的 DBMS 的数据样本。

但是,假设您的表的当前数据是来自您的查询的数据,以下查询将为您提供您指定的“期望结果”:

select t1.* from t t1
left join t t2
on t1.productid = t2.productid and t1.details > t2.details
where t2.details is null

用表格的话,查询变成这样:

+-----------+-----------+---------+
| PRODUCTID | PACKAGEID | DETAILS |
+-----------+-----------+---------+
|         1 |       500 |       0 |
|         1 |       501 |      20 |
|         1 |       502 |      30 |
|         2 |       501 |       0 |
|         3 |       500 |      15 |
|         3 |       504 |      40 |
+-----------+-----------+---------+

进入这个:

+-----------+-----------+---------+
| PRODUCTID | PACKAGEID | DETAILS |
+-----------+-----------+---------+
|         1 |       500 |       0 |
|         2 |       501 |       0 |
|         3 |       500 |      15 |
+-----------+-----------+---------+

让我知道是否清楚。

【讨论】:

  • 感谢 Mosty,这是实现 MIN() 函数的一种有趣方式,而且它似乎确实有效,尽管它显然要冗长得多,但它确实给了我任何特定领域中最低的。我猜当需要返回复合键时,SQL Server 中没有能够返回超过 1 个值以及 MIN() 聚合的解决方案。感谢您的帮助!
【解决方案3】:

简单(阅读:昂贵)方式:构建两个视图:一个仅获得每个 productid WHERE pk.networkid = 1 中的最小值 ppk.price,然后按 productid 分组。称它为Product_MinPrice_VIEW 或其他任何名称。

构建第二个视图Product_VIEW,它通过INNER JOIN 在您刚刚创建的Product_MinPrice_VIEW 的结果上替换您试图逃避的所有子SELECT INNER JOIN 工作。

我发誓,与 sub-SELECTSHAVINGSGROUP-BYs 争吵是乏味且容易出错的。我有时受不了。希望这能让你走得更远,开发出一个可以在以后优化并变得更正确的解决方案。

最终答案

I have an extremely similar problem 使用我正在开发的应用程序,同时(虽然我点击这个网站以获得更好的答案),我只是推卸责任,并编写了一些应用程序级代码来处理任何重复,并让程序的逻辑在遇到时找到真正的最小值。不漂亮,但话说回来,我没有一整天的时间来尝试弄清楚!

很抱歉,我的回答对您没有帮助。祝你好运!

【讨论】:

  • 嗨 Droogan 感谢您的回复。好的,问题不在于最低 packageid,而是需要影响行的最低价格。因此,如果一个产品有多个包裹,则只退回价格最低的包裹。但问题是我需要返回 productid/packageid 以便唯一加入该表以获取更多数据。我同意 group bys 和 sub-selects 很痛苦,尤其是当需要在树上返回多个列并且涉及多个聚合函数时:S
  • 啊,我会更新我的答案,但我可以看到你的麻烦在哪里。我的解决方案涉及完全删除packageid,这可能会混淆真实答案的数据。我得再研究一下。
  • 是的,这正是我们遇到问题的地方 Droggans,我们可以删除 packageid,并愉快地取回 productid 和最低价格,但随后我们失去了链接回该表的能力访问更多数据。我最初会带回所有数据,但这不可能是 MIN(price) 和 GROUP BY,我什至尝试了 3 级派生查询但没有成功。我也从来没有真正找到解决这类问题的好方法,因为我记得过去曾遇到过类似的问题。
  • 好的,下一个问题;当productid 1packageid 500 的价格为0.0,而productid 1packageid 501 的价格为0.0 时会发生什么?你就随便说点packageid 500吗?
  • 如果有帮助,Product_Package 表如下所示: PRODUCT_PACKAGE [id,productid,packageid,packageoptionid,price,selcomproductid,listorder] id - IDENTITY 字段和主键(CMS 必需) productid|packageid -唯一性约束
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2015-08-22
  • 2021-10-03
  • 1970-01-01
  • 2017-07-24
  • 1970-01-01
相关资源
最近更新 更多