mysql - 我如何 "tell"MySQL 在按类别分组的单个查询中返回具有多个聚合函数的关联值?

标签 mysql aggregate-functions

I have these two tables:我的数据库较大,但查询只涉及这两个,它们通过主键“Id_TipoMed”和外键“TipoMedicamento_FK”相关

我正在尝试执行一个查询,显示每个类别的总收入(通过数量(“cantidad”)和价格(“precio”)相乘),在本例中为药物类型(“Tipo Medicamento”)是所说的类别,我还想显示每个类别中收入最高的类别,然后显示它占每个类别总数的百分比,我执行了以下代码:

SELECT 
    T.Id_TipoMed, 
    M.Nombre, 
    MAX(Cantidad*Precio) Ganancia_Medicamento, 
    SUM(Cantidad*Precio)Ganancia_Neta, 
    CONCAT(MAX(Cantidad*Precio)*100/SUM(Cantidad*Precio),'%') Porcentaje_Respecto_Tipo
FROM Medicamento M 
    INNER JOIN TipoMedicamento T ON M.TipoMedicamento_FK=T.Id_TipoMed
GROUP BY TipoMedicamento_FK
HAVING 
    SUM(Cantidad*Precio) = (
        SELECT 
            SUM(Cantidad*Precio) 
        FROM Medicamento M2 
        WHERE M2.TipoMedicamento_FK=T.Id_TipoMed)
    AND 
    MAX(Cantidad*Precio) = (
        SELECT 
            MAX(Cantidad*Precio) 
        FROM Medicamento M2
        WHERE M2.TipoMedicamento_FK=T.Id_TipoMed);  

查询的唯一问题是它显示了错误的名称,它与与最大收入相关联的名称不对应,我做错了什么?因为通过执行过去的子查询方式当执行不涉及多个聚合函数的不太复杂的查询时。

谢谢!

最佳答案

GROUP BY 查询的 SELECT 列表中的非聚合表达式(也不会出现在 GROUP BY 子句中)是不确定的。不保证为非聚合返回的值将是与聚合函数(例如 MAX())返回的行值关联的值。

MySQL 以外的数据库将返回表达式 M.Nombre 的错误在 SELECT 列表中,因为该表达式也不会出现在 GROUP BY 中.

由于 MySQL 特定的 GROUP BY 行为扩展,MySQL 不会返回该语句的错误。这是记录在案的行为,在 MySQL 引用手册中进行了解释。 (对于不熟悉 MySQL 怪癖的 SQL 作者来说,这种非标准行为是一个“陷阱”。)

(通过在sql_mode中包含ONLY_FULL_GROUP_BY,可以让MySQL更好地符合SQL标准,并返回该查询的错误消息。)

<小时/>

尚不清楚为什么该语句包含与“Tipo_Medicamento”表的联接。看来没有必要。

鉴于“id_tipomed”是表的主键,最多会有一行具有特定值,因此连接不会重复任何行。我们可以看出,对“cantidad”和“precio”的非限定引用是对“Medicamento”表中列的引用。 (如果这些已经被限定,读者就不会被迫查看表定义来找出它们位于哪个表中。)

我们确实知道连接谓词中的相等比较将排除“tipomedicamento_fk”列中具有 NULL 值的任何行。但我们可以在没有连接操作的情况下更有效地做到这一点。

(从提供的信息中)我们无法判断数据库是否强制执行外键约束,即我们是否期望“tipomedicamento_fk”中的值不引用表中的行。如果存在,则连接操作将排除它们。

除此之外,似乎没有任何理由进行连接操作。

HAVING 子句似乎比排除 MAX() 和 SUM() 聚合返回的 NULL 值的行所需的复杂得多。如果返回的值为MAX(foo)聚合不为空,我们知道 SUM(foo) 的值保证不为空。

此查询的返回结果与原始查询的返回结果相同:

SELECT m.tipomedicamento_fk
     , m.nombre
     , MAX(m.cantidad*m.precio)  AS `Ganancia_Medicamento`
     , SUM(m.cantidad*m.precio)  AS `Ganancia_Neta`
     , CONCAT(MAX(m.cantidad*m.precio)*100/SUM(m.cantidad*m.precio),'%')
         AS `Porcentaje_Respecto_Tipo`
  FROM Medicamento m
 WHERE m.tipomedicamento_fk IS NOT NULL
 GROUP
    BY m.tipomedicamento_fk
HAVING `Ganancia_Medicamento` IS NOT NULL   
<小时/>

m.nombre 返回的值表达式是不确定的。我们保证它将是组中某一行的值,但不能保证它是任何特定的行。

如果我们想返回`m.cantidad*m.precio`表达式中最大值的行的值,我们需要以不同的方式编写SQL。

我们可以使用几种模式。

由于我们返回单个列,因此我们可以在 SELECT 列表中使用相关子查询。我们可以使用 ORDER BY 首先获取“cantidad*precio”的最高值,并指定 LIMIT 1,这样我们只返回一行。

SELECT m.tipomedicamento_fk
     , ( SELECT o.nombre
           FROM Medicamento o
          WHERE o.tipomedicamento_fk = m.tipomedicamento_fk
          ORDER
             BY (o.cantidad*o.precio) DESC
              , o.nombre DESC
          LIMIT 1
       )  AS `nombre`
     , MAX(m.cantidad*m.precio)  AS `Ganancia_Medicamento`
     , SUM(m.cantidad*m.precio)  AS `Ganancia_Neta`
     , CONCAT(MAX(m.cantidad*m.precio)*100/SUM(m.cantidad*m.precio),'%')
         AS `Porcentaje_Respecto_Tipo`
  FROM Medicamento m
 WHERE m.tipomedicamento_fk IS NOT NULL
 GROUP
    BY m.tipomedicamento_fk
HAVING `Ganancia_Medicamento` IS NOT NULL   

这不是唯一的方法。还有其他查询模式会返回等效结果。

关于mysql - 我如何 "tell"MySQL 在按类别分组的单个查询中返回具有多个聚合函数的关联值?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36562407/

相关文章:

mysql - Migrations 在指定列和索引列之后添加列

sql - 按另一个表中的计数过滤

MySQL组有问题

java - 如何在不构建字符串的情况下使用 JDBC 进行扩展插入?

mysql - 如何编写有效的 "SELECT a FROM t WHERE b IN() ORDER BY a"查询?

mysql - Codeigniter mysql 查询组

mysql - 加载文件 infile 期间生成随机字段

MYSQL通过选择要显示的元素来加入排序和分组

aggregate - KDB/Q 中的滑动时间窗口

sql - SparkSQL : conditional sum using two columns