Расчет разницы между минимальным значением и значением каждой строки - PullRequest
0 голосов
/ 30 июня 2019

У меня есть таблица базы данных, в которой есть идентификатор, имя и время человека (в миллисекундах, хранится как int). Например:

| id  | name   | totalTime |
| --- | ------ | --------- |
| 1   | Bob    | 16280     |
| 2   | Andy   | 17210     |
| 3   | Bill   | 15320     |
| 4   | Matt   | 14440     |
| 5   | Steven | 17570     |
| 6   | Tom    | NULL      |
| 7   | Angus  | 17210     |
| 8   | Will   | NULL      |
| 9   | Jack   | 17410     |
| 10  | Alex   | 16830     |

Не обязательно у всех людей есть время (таким образом, нули).

Мне бы хотелось иметь еще два столбца: один показывает ранг / положение каждого человека, а другой - разницу во времени (в миллисекундах) между лучшим (т.е. минимальным) временем и временем в каждой строке.

Мне удалось написать запрос MySQL 8.x, который оценивает:

SELECT id, name, totalTime, 

(CASE WHEN totalTime IS NOT NULL THEN RANK() OVER ( PARTITION BY (CASE WHEN totalTime IS NOT NULL THEN 1 ELSE 0 END) ORDER BY totalTime ) END) totalRank

FROM results

ORDER BY -totalRank DESC;

... и выводит это:

| id  | name   | totalTime | totalRank |
| --- | ------ | --------- | --------- |
| 4   | Matt   | 14440     | 1         |
| 3   | Bill   | 15320     | 2         |
| 1   | Bob    | 16280     | 3         |
| 10  | Alex   | 16830     | 4         |
| 2   | Andy   | 17210     | 5         |
| 7   | Angus  | 17210     | 5         |
| 9   | Jack   | 17410     | 7         |
| 5   | Steven | 17570     | 8         |
| 6   | Tom    | NULL      | NULL      |
| 8   | Will   | NULL      | NULL      |

... но не удалось определить SQL для добавления еще одного столбца с разницей во времени.

Ниже приведен пример того, что я хотел бы, но не могу понять, как это сделать:

| id  | name   | totalTime | totalRank | difference |
| --- | ------ | --------- | --------- | ---------- |
| 4   | Matt   | 14440     | 1         | 0          |
| 3   | Bill   | 15320     | 2         | 880        |
| 1   | Bob    | 16280     | 3         | 1840       |
| 10  | Alex   | 16830     | 4         | 2390       |
| 2   | Andy   | 17210     | 5         | 2770       |
| 7   | Angus  | 17210     | 5         | 2770       |
| 9   | Jack   | 17410     | 7         | 2970       |
| 5   | Steven | 17570     | 8         | 3130       |
| 6   | Tom    | NULL      | NULL      | NULL       |
| 8   | Will   | NULL      | NULL      | NULL       |

У меня есть это как БД Fiddle: https://www.db -fiddle.com / f / gQvSeij2EKSufYp9VjbDav / 0

Заранее спасибо за любую помощь!

Ответы [ 4 ]

2 голосов
/ 30 июня 2019

Вы можете использовать CTE для получения минимума totalTime и использовать его для вычисления difference:

WITH cte as (SELECT MIN(totalTime) minTotalTime FROM results)
SELECT id, name, totalTime, 
CASE WHEN totalTime IS NOT NULL 
  THEN RANK() OVER (PARTITION BY (
      CASE 
        WHEN totalTime IS NOT NULL THEN 1 
        ELSE 0 
      END
  ) ORDER BY totalTime) 
END totalRank,
totalTime - (SELECT minTotalTime from cte) difference
FROM results
ORDER BY -totalRank DESC;

См. Демонстрационную версию .Результаты:

| id  | name   | totalTime | totalRank | difference |
| --- | ------ | --------- | --------- | ---------- |
| 4   | Matt   | 14440     | 1         | 0          |
| 3   | Bill   | 15320     | 2         | 880        |
| 1   | Bob    | 16280     | 3         | 1840       |
| 10  | Alex   | 16830     | 4         | 2390       |
| 2   | Andy   | 17210     | 5         | 2770       |
| 7   | Angus  | 17210     | 5         | 2770       |
| 9   | Jack   | 17410     | 7         | 2970       |
| 5   | Steven | 17570     | 8         | 3130       |
| 6   | Tom    |           |           |            |
| 8   | Will   |           |           |            |
1 голос
/ 30 июня 2019

Ответ Сергея правильный. Я бы написал так:

SELECT id, name, totalTime, 
        (CASE WHEN totalTime IS NOT NULL
              THEN RANK() OVER (PARTITION BY (totalTime IS NULL) ORDER BY totalTime)
         END) as totalRank,
        totaltime - MIN(totaltime) OVER() as diff
FROM results
ORDER BY (totalTime IS NOT NULL) DESC, totalRank;

Различия:

  1. Упрощение PARTITION BY. Вы используете CASE, но MySQL удобно обрабатывает логические значения как "реальные" значения.
  2. Выражение ORDER BY более интуитивным способом.
1 голос
/ 30 июня 2019

Добавить min () оконную функцию

SELECT id, name, totalTime, 

(CASE WHEN totalTime IS NOT NULL THEN RANK() OVER ( PARTITION BY (CASE WHEN totalTime IS NOT NULL THEN 1 ELSE 0 END) ORDER BY totalTime ) END) totalRank
,totaltime - min(totaltime) over() diff
FROM results
ORDER BY -totalRank DESC;
1 голос
/ 30 июня 2019
            SELECT subtable.id,
                    subtable.NAME,
                    subtable.totalTime,                     
                    subtable.diff,

                    IIF(subtable.totalTime IS NULL,NULL,subtable.rowno) as bisi

                     FROM (
                    select *,
                            ROW_NUMBER() OVER (ORDER BY totalTime desc) as rowno,
                            totalTime - 
                                        (
                                            select min(rst.totalTime) 
                                            from results rst) as diff
                        from results) subtable;

Я бы сделал это в MS-SQL или, альтернативно, в MYSQL

 SELECT subtable.id,
                    subtable.NAME,
                    subtable.totalTime,                     
                    subtable.diff,

                    IF (subtable.totalTime IS NULL,  NULL, subtable.rowno) as bisi

                     FROM (
                    select *,
                            ROW_NUMBER() OVER (ORDER BY totalTime desc) as rowno,
                            totalTime - 
                                        (
                                            select min(rst.totalTime) 
                                            from results rst) as diff
                        from results) subtable;
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...