【问题标题】:How would I return the result of SQL math operations?我将如何返回 SQL 数学运算的结果?
【发布时间】:2016-02-25 22:24:52
【问题描述】:

所以我最近参加了一些更高级别的 SQL 问题的测试。我在 SQL 方面只有我认为的“中级”经验,而且我已经为此工作了一天左右。我就是想不通。


问题来了:

您有一个包含 4 列的表格:

EmployeeID     int unique
EmployeeType   int  
EmployeeSalary int  
Created        date

目标:我需要为任何超过 1 个条目的 EmployeeType 检索最近两个 EmployeeSalary 之间的差异。它必须在一个语句中完成(嵌套查询很好)。


示例数据集:http://sqlfiddle.com/#!9/0dfc7

EmployeeID | EmployeeType | EmployeeSalary | Created
-----------|--------------|----------------|--------------------
1          | 53           | 50             | 2015-11-15 00:00:00
2          | 66           | 20             | 2014-11-11 04:20:23
3          | 66           | 30             | 2015-11-03 08:26:21
4          | 66           | 10             | 2013-11-02 11:32:47
5          | 78           | 70             | 2009-11-08 04:47:47
6          | 78           | 45             | 2006-11-01 04:42:55

所以对于这个数据集,正确的返回应该是:

EmployeeType | EmployeeSalary 
-------------|---------------
66           | 10
78           | 25

10 来自于 EmployeeType 为 66 减去最新的两个 EmployeeSalary 值 (30 - 20)。25 来自于 EmployeeType 为 78 减去最新的两个 EmployeeSalary 值 (70-45)。我们完全跳过 EmployeeID 53,因为它只有一个值。

这个一直在破坏我的大脑。有什么线索吗?

谢谢!

【问题讨论】:

  • 用数据创建一个 sqlfiddle
  • sqlfiddle.com/#!9/0dfc7 我想我做得对。感谢您的帮助!
  • 你做到了。谢谢你。现在这是记录它的好方法。我相信你很快就会得到答复。如果没有,我很快就会回来

标签: mysql sql


【解决方案1】:

如何让真正简单的查询变得复杂?

一种有趣的方式(不是最佳性能)是:

SELECT final.EmployeeType, SUM(salary) AS difference
FROM (
  SELECT b.EmployeeType, b.EmployeeSalary AS salary
  FROM tab b
  JOIN (SELECT EmployeeType, GROUP_CONCAT(EmployeeSalary ORDER BY Created DESC) AS c
        FROM tab
        GROUP BY EmployeeType
        HAVING COUNT(*) > 1) AS sub
    ON b.EmployeeType = sub.EmployeeType
    AND FIND_IN_SET(b.EmployeeSalary, sub.c) = 1
  UNION ALL
  SELECT b.EmployeeType, -b.EmployeeSalary AS salary
  FROM tab b
  JOIN (SELECT EmployeeType, GROUP_CONCAT(EmployeeSalary ORDER BY Created DESC) AS c
        FROM tab
        GROUP BY EmployeeType
        HAVING COUNT(*) > 1) AS sub
    ON b.EmployeeType = sub.EmployeeType
    AND FIND_IN_SET(b.EmployeeSalary, sub.c) = 2
) AS final
GROUP BY final.EmployeeType;

SqlFiddleDemo

编辑:

关键是MySQL不支持窗口化功能,所以需要使用等效代码:

例如在 SQL Server 中的解决方案:

SELECT EmployeeType, SUM(CASE rn WHEN 1 THEN EmployeeSalary 
                                 ELSE -EmployeeSalary END) AS difference
FROM (SELECT *,
       ROW_NUMBER() OVER(PARTITION BY EmployeeType ORDER BY Created DESC) AS rn
      FROM #tab
     ) AS sub
WHERE rn IN (1,2)
GROUP BY EmployeeType
HAVING COUNT(EmployeeType) > 1

LiveDemo

MySQL 等效:

SELECT EmployeeType, SUM(CASE rn WHEN 1 THEN EmployeeSalary 
                          ELSE -EmployeeSalary END) AS difference
FROM (
       SELECT t1.EmployeeType, t1.EmployeeSalary,
        count(t2.Created) + 1 as rn
      FROM #tab t1
      LEFT JOIN #tab t2
        ON t1.EmployeeType = t2.EmployeeType
       AND t1.Created < t2.Created
      GROUP BY t1.EmployeeType, t1.EmployeeSalary
     ) AS sub
WHERE rn IN (1,2)
GROUP BY EmployeeType
HAVING COUNT(EmployeeType) > 1;

LiveDemo2

【讨论】:

  • 嘿小伙子2025!我也遇到了小提琴超时,但我在本地测试了代码并且它有效!感谢你的帮助。我现在要梳理一下,试着理解我错过了什么。你是最棒的!
  • @SalsaGuy 这段代码是为了好玩,你不应该在生产中使用它,因为存在更好的方法(幅度更快):)
  • 我从事软件开发已经有大约十年了,直到最近才做很多 SQL。我不知道 SQL 会这么难。当我开始挑战时,我惊讶于它的强大。我将使用您的代码来了解正在发生的事情并尝试对其进行优化。再次感谢您的帮助!
  • 您可能想了解如何在 mysql 中模拟 LEAD/LAG 以优化您的查询。 This 就是一个例子。
  • @JulienBlanchard 是的,这可以通过多种方式进行优化,最快的是模拟ROW NUMBER。我不喜欢 MySQL,因为它缺少窗口函数。 SQL Server Demo 从事仿真工作:)
【解决方案2】:

fiddle 的数据集和上面的例子不一样,比较混乱(更别提有点反常了)。无论如何,有很多方法可以给这只特殊的猫剥皮。这是一个(但不是最快的):

SELECT a.employeetype, ABS(a.employeesalary-b.employeesalary) diff
  FROM 
     ( SELECT x.*
            , COUNT(*) rank 
         FROM employees x 
         JOIN employees y 
           ON y.employeetype = x.employeetype 
          AND y.created >= x.created 
        GROUP
           BY x.employeetype
            , x.created
     ) a
  JOIN
     ( SELECT x.*
            , COUNT(*) rank 
         FROM employees x 
         JOIN employees y 
           ON y.employeetype = x.employeetype 
          AND y.created >= x.created 
        GROUP
           BY x.employeetype
            , x.created
     ) b
    ON b.employeetype = a.employeetype
   AND b.rank = a.rank+1
 WHERE a.rank = 1;

一个非常相似但更快的解决方案看起来像这样(尽管您有时需要在表 a 和 b 之间分配不同的变量 - 原因我仍然不完全理解)...

SELECT a.employeetype
     , ABS(a.employeesalary-b.employeesalary) diff
  FROM 
     ( SELECT x.* 
            , CASE WHEN @prev = x.employeetype THEN @i:=@i+1 ELSE @i:=1 END i
            , @prev := x.employeetype prev
         FROM employees x
            , (SELECT @prev := 0, @i:=1) vars
        ORDER 
           BY x.employeetype
            , x.created DESC
     ) a
  JOIN
     ( SELECT x.* 
            , CASE WHEN @prev = x.employeetype THEN @i:=@i+1 ELSE @i:=1 END i
            , @prev := x.employeetype prev
         FROM employees x
            , (SELECT @prev := 0, @i:=1) vars
        ORDER 
           BY x.employeetype
            , x.created DESC
     ) b
    ON b.employeetype = a.employeetype
   AND b.i = a.i + 1
 WHERE a.i = 1;

【讨论】:

    猜你喜欢
    • 2022-01-22
    • 1970-01-01
    • 1970-01-01
    • 2020-05-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多