我有一张产品表,其中包含价格和折扣列。我想得到按价格分组的产品(所以当2个产品价格相同时,它只返回一个)
这个有效:
select * from `products` group by `price` order by `price` asc
但我的问题是,产品是否可以按计算的最终价格分组,例如(价格 - 价格*折扣/ 100作为price_after_discount )
这一个:
select *, (price - (price * discount / 100)) AS price_after_discount from `products` order by `price_after_discount` asc
返回:
[
0 => [
"id" => 2284
"price" => 10.0
"discount" => 5
"price_after_discount" => 9.5
]
1 => [
"id" => 2281
"price" => 10.0
"discount" => 0
"price_after_discount" => 10.0
]
2 => [
"id" => 2286
"price" => 10.0
"discount" => 0
"price_after_discount" => 10.0
]
]
如您所见,有3种型号,其中2种具有相同的最终价格。
但是当我按声明添加组时:
select *, (price - (price * discount / 100)) AS price_after_discount from `products` group by `price_after_discount` order by `price_after_discount` asc
它只返回:
[
0 => [
"id" => 2281
"price" => 10.0
"discount" => 0
"price_after_discount" => 10.0
]
]
请问,如何修改查询,以便按此顺序返回ID为2284和2281(或2286)的2个模型?
(这只是生成Laravel的SQL命令的转录,所以熟悉Laravel,eloquent或DB facade解决方案的人也会很感激: - )
谢谢
答案 0 :(得分:0)
group by `price_after_discount` order by `price_after_discount`
您需要知道GROUP BY在计算结果集的时间之前(或期间)运行,别名等,但在创建结果集并完成列的别名之后ORDER BY运行。这意味着在数据库执行您对数据进行分组的出价时,price_after_discount
不能用作别名。因此,您必须通过以下方式重新声明计算组中price_after_discount
的公式:
select *, (price - (price * discount / 100)) AS price_after_discount
from `products`
group by (price - (price * discount / 100))
order by `price_after_discount`
基本上看起来并没有像你一样进行两次计算;大多数数据库引擎都足够聪明,看到两个结构完全相同,它们不会运行两次
您也可以使用一些查询嵌套来鼓励数据库引擎先做一些别名结果集* *
select *
from
(
select *, (price - (price * discount / 100)) AS price_after_discount
from `products`
) as subquery1
group by `price_after_discount`
order by `price_after_discount`
这里,price_after_discount在数据传递给外部查询之前成为子查询的一部分,因此在分组时可用
如果你看看数据库在幕后做什么,最后一部分可能最终会成为一个完整的谎言也可能会有所帮助。更好的RDBMS将分开选择您的查询并寻找优化它的方法。如果您要向各种数据库询问他们在这里对这两个查询做了什么的报告,我不会怀疑他们中的一些会产生两个相同的计划,因为优化器很聪明地认识到他们& #39;实际上是完全相同的。如果它真的让你感兴趣,请注意我的建议的最后一部分,否则选择你认为看起来更好,更容易理解的格式,如果你的特定数据库没有其他压倒性的性能问题
进一步警告..我认为这是伪代码,或者你的数据库正在"有帮助"通过为您不分组的所有列插入特定聚合:
在我日常工作的典型数据库中(Oracle,MSSQL)你不能说:
SELECT name, age, social_security_number FROM people GROUP BY name
因为数据库想要知道你想要对你所有的那些事情做些什么。假设您的表中有两个Johns,一个99,另一个14.您希望DB返回哪个年龄? 99或14?您认为数据库对将记录数据保持在一起具有既得利益,但是当使用GROUP BY时,所有这些都会消失在窗口之外
通常我们会使用MAX(age)
,MIN(Age)
,SUM(age)
等指定如何处理未分组的数据。
我们必须向DB说出我们希望它与所有与公共名称相关的多个值做什么。如果你的数据库让你逃避而不是说要应用什么样的聚合,那么它可能是默认选择一些聚合。根本没有办法,因为GROUP BY要求它将具有重复标识符的多个记录压缩成一个;如果您还要求数据库将数据返回给您,则根本无法指定如何处理其他数据。
你已经说过了#(因此当2个产品价格相同时,它只返回一个)" - 你必须告诉它哪一个。在名称示例中,这更容易理解,因为它的设计并没有计算任何东西,这将是这样的:
SELECT people.* FROM
people
INNER JOIN
(SELECT name, max(age) as maxage FROM people GROUP BY name) as finder
ON people.name = finder.name AND people.age = finder.age
Theres一个子查询,它给出了一个唯一名称列表和该名称的最大年龄。通过将其重新连接到人员数据表,我们可以将我们从人员中获取的行限制为仅限名称和最大年龄配对匹配的行。基本上这个查询是"找到具有特定名称的最老的人"。
当有两个99岁的约翰斯时会发生什么?你会得到它们两个......所以你需要一个比#34更好的选择器;我想要每个被指名的人中最老的一个&#34; ..必须引入一些东西来打破平局,并添加到查询中< / p>
在折扣价格的情况下,如果MyQL只是帮助&#34;你通过给你MIN()你没有准确指定的东西,你最终会得到这个:
[
0 => [
"id" => 2281
"price" => 10.0
"discount" => 0
"price_after_discount" => 10.0
]
]
如果您的数据是这样的:
[
0 => [
"id" => 2284
"price" => 10.0
"discount" => 5
"price_after_discount" => 9.5
]
1 => [
"id" => 2281
"price" => 20.0
"discount" => 50
"price_after_discount" => 10.0
]
2 => [
"id" => 2287
"price" => 10.0
"discount" => 0
"price_after_discount" => 10.0
]
]
通过分组10.0折扣价格,但只是让MySQL&#34;帮助&#34;通过选择任何它喜欢的其他值,它被选中给你每个的MIN(),这混合了记录......它给出了2281(是的,在2281和2287之中) ,2281是较低的数字),价格为10(是的,20和10之间,10是较低的数字)和0的折扣(是的,50和0,0更低),但现在一切都是搞砸了:如果数据库在没有显式指令的情况下随机决定做什么,那么数据库甚至可以将行数据保持在一起是没有充分理由的。
然而,它已经正确地遵守了自己的规则,它将所有内容分组为&#34;所有10.0折扣价格&#34;然后决定给你最小的其他数量,它可以找到..您必须决定所需的产品,然后根据该条件运行查询并返回它们:
SELECT * FROM
products
INNER JOIN
(SELECT MAX(id) as id FROM products GROUP BY (price - (price * discount/100)) finder
ON finder.id = products.id
希望您现在知道为什么会这样:您要求mysql为您准备一份特定折扣价的所有最高ID号的列表。你不在乎真的(顺便说一下)回传折扣价,你只想做计算,在组中找到最高的ID,然后将它加回到产品表中以获得其余的该max()ed ID
的详细信息