How can I return the result of math SQL operations?

So, I recently took a test with some higher level SQL problems. I only have what I consider to be an “intermediate” experience in SQL, and I have been working on this for a whole day. I just can't understand.


Here's the problem:

You have a table with 4 columns as such:

EmployeeID     int unique
EmployeeType   int  
EmployeeSalary int  
Created        date

Purpose: I need to get the difference between the last two EmployeeSalary for any EmployeeType with more than 1 record. This should be done in one statement (subqueries are ok).


Example dataset: 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

So, for this dataset, the correct return will be as follows:

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

10 EmployeeSalary (30 - 20) EmployeeType 66. 25 EmployeeSalary (70-45) EmployeeType 78. EmployeeID 53, .

. ?

!

+4
2

?

( ):

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

EDIT:

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

+3

, ( ). , . ( ):

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;
+3

Source: https://habr.com/ru/post/1617112/


All Articles