Получение количества отдельных столбцов с оконными функциями в MySQL 8 - PullRequest
0 голосов
/ 14 апреля 2020

У меня есть скрипта MVP DB: https://www.db-fiddle.com/f/cUn1Lo2xhbTAUwwV5q9wKV/2

Я пытаюсь получить количество уникальных shift_id s в таблице на любую дату, используя оконные функции.

Я пытался использовать COUNT(DISTINCT(shift_id)), но это не поддерживается на MySQL 8 с оконными функциями на данный момент.

На всякий случай, если скрипка выключится. Вот схема теста:

CREATE TABLE `scores` (
  `id` bigint unsigned NOT NULL AUTO_INCREMENT,
  `shift_id` int unsigned NOT NULL,
  `employee_name` varchar(255) COLLATE utf8mb4_unicode_ci NOT NULL,
  `score` double(8,2) unsigned NOT NULL,
  `created_at` timestamp NOT NULL,
  PRIMARY KEY (`id`)
);

INSERT INTO scores(shift_id, employee_name, score, created_at) VALUES
(1, "John", 6.72, "2020-04-01 00:00:00"),
(1, "Bob", 15.71, "2020-04-01 00:00:00"),
(1, "Bob", 54.02, "2020-04-01 00:00:00"),
(1, "John", 23.55, "2020-04-01 00:00:00"),

(2, "John", 9.13, "2020-04-02 00:00:00"),
(2, "Bob", 44.76, "2020-04-02 00:00:00"),
(2, "Bob", 33.40, "2020-04-02 00:00:00"),
(2, "James", 20, "2020-04-02 00:00:00"),

(3, "John", 20, "2020-04-02 00:00:00"),
(3, "Bob", 20, "2020-04-02 08:00:00"),
(3, "Bob", 30, "2020-04-02 08:00:00"),
(3, "James", 10, "2020-04-02 08:00:00")

И мой запрос, в котором используются два метода с использованием того, что я видел в этом посте: Количество различных функций в окнах

SELECT
    ANY_VALUE(employee_name) AS `employee_name`,
    DATE(created_at) AS `shift_date`,
    COUNT(*) OVER (PARTITION BY ANY_VALUE(created_at), ANY_VALUE(shift_id)) AS `shifts_on_day_1`,

    (
        dense_rank() over (partition by ANY_VALUE(created_at) order by ANY_VALUE(shift_id) asc) +
        dense_rank() over (partition by ANY_VALUE(created_at) order by ANY_VALUE(shift_id) desc) - 1
    ) as `shifts_on_day_2`

FROM scores
    GROUP BY employee_name, DATE(created_at);

Ожидаемым результатом будет любая строка с датой 2020-04-01, которая будет иметь shifts_on_day 1, а строки с датой 2 апреля будут иметь shifts_on_day в 2.

У меня есть рассмотрено использование коррелированного подзапроса, но это кошмар производительности с миллионами строк в таблице и тысячами возвращаемых в запросе.

Обновление: я думаю, что необходимость в оконных функциях состоит в том, что уже существует группа в запрос. Все данные необходимы в одном запросе с конечной целью - получить среднее значение по каждому сотруднику в определенный c день. Чтобы получить этот общий балл для каждого сотрудника, я могу просто COUNT(*). Но затем мне нужно разделить это на общее количество смен в день, чтобы получить среднее значение.

Обновление

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

Следовательно, ожидаемый результат:

name  | shift_date | avrg
------+------------+-----
Bob   | 2020-04-01 | 2     2 / 1 = 2 ; two rows for Bob, one shift_id (1) that day
Bob   | 2020-04-02 | 2     4 / 2 = 2 ; four rows for Bob, two shift_ids (2,3) that day
James | 2020-04-02 | 1     2 / 2 = 1 ; two rows for James, two shift_ids (2,3) that day
John  | 2020-04-01 | 2     2 / 1 = 2 ; two rows for John, one shift_id (1) that day
John  | 2020-04-02 | 1     2 / 2 = 1 ; two rows for John, two shift_ids (2,3) that day

1 Ответ

1 голос
/ 14 апреля 2020

«Все строки на дату и сотрудника» и «различное количество идентификаторов на дату» - это две полные разные совокупности; Вы не можете выполнить одну агрегацию и каким-то образом извлечь другую агрегацию из агрегированных в противном случае строк. Это окно правил правил выводит результат агрегации.

Вместо этого вам нужно две отдельные агрегации. Например:

with empdays as
(
  select employee_name, date(created_at) as shift_date, count(*) as total
  from scores
  group by employee_name, date(created_at)
)
, days as 
(
  select date(created_at) as shift_date, count(distinct shift_id) as total
  from scores
  group by date(created_at)
)
select ed.employee_name, shift_date, ed.total / d.total as average
from empdays ed
join days d using (shift_date)
order by ed.employee_name, shift_date;

Демо: https://www.db-fiddle.com/f/qjqbibriXtos6Hsi5qcwi6/0

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...