【问题标题】:How to use grouping by XXX, and using aggregate funtion correctly? WITH MIN VALUES如何使用按 XXX 分组,并正确使用聚合函数?具有最小值
【发布时间】:2019-05-08 23:00:42
【问题描述】:

我正在尝试使用联接从 4 个表中获取记录。这些表格是费率、承运人、仓库和保证金。我能够从这 4 个表中获取所需的记录,但是当我使用 group by 和聚合函数 (MIN) 时,我的 min sell 列是正确的,但是,其他数据,如 carrierID、depotID 和 ratesID 是不同的.

我有 OriginType (OT) 和 DestinatioType (DT),每个都有 2 个案例;仓库和门。因此,当我对它们进行分组时,我得到了四个选项(服务):

  • 仓库到仓库
  • 仓库到门
  • 仓库门口
  • 门到门

我正在尝试为这些服务中的每一项获取最低(费率)作为“销售”并显示它。卖出计算如下:

  1. 作为 A 的 autoRate 表有一个列 Buy、carrier、OT、DT、Origin (Value: Canberra)、Destination (Value: Melbourne) 和 Car (价值:4WD/Van)
  2. 左加入运营商作为 C 和 A.carrier = C.ID
  3. C.Fuellevy 列作为百分比((C.FuelLevy * A.buy) + C.FuelLevy) 作为 EQ1
  4. ((EQ1 * 10%) + EQ1) 作为 EQ2
  5. 每行的 EQ2 价格都有一个保证金表百分比。例如,如果 EQ2 的值为 400,那么它会在 Margin 表中查找范围(低和高分别为 350(低)和 500(高)),其百分比为 25%,因此((EQ2 * 25%)+ EQ2) 给出出售价值。

我不太清楚如何在这里上传我的数据和数据库表,所以我试着在这里解释一下我想要什么。

我构建的查询是:-

Select   Depo.*,  DL.id as DepoID,  DL.carrier as CarNo, DL.depotCity, DL.depoSuburb, min(Depo.Sell)  as sellcost , Depo.OriginType as OT From (
    Select Mar.*,  M.MarginPer, round((eq2 * M.MarginPer) + eq2)as Sell   From (
                        Select GST.* , EQ1 as 'FinalEQ1' , round((EQ1 * .10) + EQ1,2) as eq2 From (
                                Select A.ID as RateID, A.Origin, A.OriginState, A.Destination, A.DestinationState, A.Carrier as RateCarrier, A.Car as CarType, A.Buy as Buy, A.OriginType, A.DestinationType ,
                                C.ID as CarrierID, C.Carrier, C.FuelLevy , round((A.buy * C.FuelLevy) + A.Buy, 2) As EQ1,
                                CONCAT(A.OriginType, ' to ' ,A.DestinationType ) as service,
                                 D.id as DepoID,  D.carrier as CarNo, D.depotCity, D.depoSuburb
                                from carrier C
                                left join autorates A on A.carrier = C.ID
                                left join dList D on D.carrier =  C.ID
                                where A.origin = 'Canberra' and A.destination = 'Melbourne' and A.car = '4WD/Van'  AND  D.carrier = A.carrier AND A.goodsAllowed =  0
                                AND C.Disabled = 0
                                AND D.depotCity  = 'Canberra'
                                 order by  EQ1
                            ) As GST
                        order by  eq2
             ) As Mar
             Left Join margin M on Mar.eq2 >=  M.low and  Mar.eq2 <= M.high
                   order by  Sell
        ) As Depo
            Left  Join dList DL  on DL.Carrier = Depo.RateCarrier
            Where DL.depotCity = 'Melbourne'
             group by OT
             order by  sellcost

Group by 和 MIN() 之前的结果:-

我们在这里看到的 sellcost 值是 412,而 carNo 是 51

在此屏幕截图中,carrierno 已更改,但 MIN 值保持不变。

【问题讨论】:

  • “其他数据不同”是什么意思?你的意思是你没有得到列的最小值的载体?如果您在分组时选择其他列,它会从组中的随机行中获取该列,不一定是带有MIN() 的行。要理解为什么,想象一下如果你有两个聚合函数,例如最小值和最大值。您希望它从哪一行获取其他列?或者,如果它是 AVG 之类的东西,甚至不是来自特定行怎么办?
  • 添加到 Barmar 的评论中,MIN 和 MAX 甚至不一定来自特定行。即使 MySQL 很友好地尝试为您提供伴随您计算的 MIN 的行值(如果您只计算一个 MIN),但在很多情况下您可能会有大量具有该值的行。
  • @Barmar 是的,我没有得到正确的运营商值。当我得到结果时,我得到了销售的 MIN 值,但运营商不同。我得到每个组值的下一行,因为我得到的 MIN 值都是正确的
  • 这就是为什么大多数 SQL 数据库甚至不允许您选择不在 GROUP BY 中的列的原因。 MySQL 有ONLY_FULL_GROUP_BY 模式来控制是否允许。
  • @Barmar 我该如何使用ONLY_FULL_GROUP_BY?

标签: mysql sql


【解决方案1】:

我刚刚管理了 get 查询。这将像我在我的 SQL-WB 上测试过的那样工作

SELECT DepoD.*, DL2.id, DL2.carrier AS a, DL2.depotCity
    , DL2.depoSuburb AS 'PickupSub', MIN(Sell) AS sellcost 
FROM ( 
    SELECT Depo.*, DL.id AS DepoID, DL.carrier AS CarNo, DL.depotCity, DL.depoSuburb AS 'PickupSub'
        , DL.depoSuburb AS 'DestSub', MIN(Sell) AS sellcost, Depo.OriginType AS OT 
    FROM (
        SELECT Mar.*, M.MarginPer, MIN(round((eq2 * M.MarginPer) + eq2)) AS Sell 
        FROM (
            SELECT GST.*, EQ1 AS 'FinalEQ1', MIN(round((EQ1 * .10) + EQ1, 2)) AS eq2 
            FROM (
                SELECT A.ID AS RateID, A.Origin, A.OriginState, A.Destination, A.DestinationState
                    , A.Carrier AS RateCarrier, A.Car AS CarType, A.Buy AS Buy,  A.OriginType
                    , A.DestinationType, C.ID AS CarrierID, C.Carrier, C.FuelLevy
                    , round((A.buy * C.FuelLevy) + A.Buy, 2) AS EQ1
                    , CONCAT(A.OriginType, ' to ', A.DestinationType ) AS service 
                FROM carrier C
                LEFT JOIN autorates A ON A.carrier = C.ID
                WHERE A.origin = 'Melbourne' 
                    AND A.destination = 'Canberra' 
                    AND A.car = '4WD/Van' 
                    AND A.goodsAllowed = 0
                    AND C.Disabled = 0
                ORDER BY EQ1 ASC
            ) AS GST
            GROUP BY service, CarrierID
            ORDER BY eq2
        ) AS Mar
        LEFT JOIN margin M ON Mar.eq2 >= M.low AND Mar.eq2 <= M.high
        GROUP BY service
        ORDER BY Sell
    ) AS Depo
    LEFT JOIN dList AS DL ON DL.Carrier = Depo.RateCarrier
    WHERE DL.depotCity IN('Melbourne', 'Canberra') 
    GROUP BY carrier, service
    ORDER BY sellcost
) AS DepoD
LEFT JOIN dList DL2 ON DL2.Carrier = DepoD.RateCarrier
WHERE DL2.depotCity IN('Melbourne', 'Canberra') 
GROUP BY carrier, service
ORDER BY sellcost

【讨论】:

    【解决方案2】:

    注意: 不是最终答案,而是用于测试并查看这部分是否有效的查询。如果是这样,我将提供其余的查询。


    如果您将问题分解,我认为您需要做的是确定 OriginType 和 DestinationType 的每个组合的最低 Sell。然后加入表以获取匹配的行。

    下面的查询没有得到所有的详细信息,但它应该得到具有最低 Sell 值的行。如果它有效,那么我可以添加到查询中以获取其他匹配的数据。

    这个问题的一个方面是LEFT JOIN 的使用。这可能包括与ON 子句不匹配的行,本质上是添加具有空值的列。这可能会影响性能,并且仅使用 JOIN 可能会更好。如果您对此进行测试并且它的工作原理如下所述,请再次运行它,而不要在每个 JOIN 子句前面加上 LEFT。如果您得到相同的结果,请在 cmets 中告诉我。

    试一试,看看您是否获得了 OriginType 和 DestinationType 的每种组合的四个值。如果是这样,我将使用查询的其余部分更新答案。

    SELECT
        A.OriginType,
        A.DestinationType,
        round(A.buy * (1.0 + C.FuelLevy), 2) AS EQ1,
        round(round(A.buy * (1.0 + C.FuelLevy), 2) * 1.10, 2) AS eq2,
        MIN(round((eq2 * (1.0 + M.MarginPer)))) AS Sell
    FROM carrier C
    LEFT JOIN autorates A
        ON A.carrier = C.ID
    LEFT JOIN dList D
        ON D.carrier = A.carrier
    LEFT JOIN margin M
        ON round(round(A.buy * (1.0 + C.FuelLevy), 2) * 1.10, 2) BETWEEN M.low AND M.high
    WHERE A.origin = 'Canberra'
        AND A.destination = 'Melbourne'
        AND A.car = '4WD/Van'
        AND A.goodsAllowed =  0
        AND C.Disabled = 0
        AND D.depotCity  = 'Canberra'
    GROUP BY LOWER(A.OriginType), LOWER(A.DestinationType)
    ORDER BY Sell
    

    【讨论】:

    • 似乎我们有错误。字段列表中的未知列 eq2。感谢您的帮助
    • MIN(round((eq2...)))中的eq2替换为上一行的公式。您不能在同一个 SELECT 列表中引用别名。
    • 谢谢@Barmar。很好的收获。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-01-07
    • 1970-01-01
    • 2020-07-30
    • 2019-08-19
    • 2019-07-01
    • 1970-01-01
    相关资源
    最近更新 更多