#mysql #sql #database #frequency
#mysql #sql #База данных #частота
Вопрос:
У меня есть база данных MySQL, где один столбец содержит коды состояния. Столбец имеет тип int, и значения всегда будут равны только 100,200,300,400. Это выглядит как показано ниже; другие столбцы удалены для наглядности.
id | status
----------------
1 300
2 100
3 100
4 200
5 300
6 300
7 100
8 400
9 200
10 300
11 100
12 400
13 400
14 400
15 300
16 300
Поле id генерируется автоматически и всегда будет последовательным. Я хочу, чтобы в третьем столбце отображалась разделенная запятыми строка распределения частот кодов состояния предыдущих 10 строк. Это должно выглядеть следующим образом.
id | status | freq
-----------------------------------
1 300
2 100
3 100
4 200
5 200
6 300
7 100
8 400
9 300
10 300
11 100 300,100,200,400 -- from rows 1-10
12 400 100,300,200,400 -- from rows 2-11
13 400 100,300,200,400 -- from rows 3-12
14 400 300,400,100,200 -- from rows 4-13
15 300 400,300,100,200 -- from rows 5-14
16 300 300,400,100 -- from rows 6-15
Я хочу, чтобы наиболее частый код был указан первым. И там, где два кода состояния имеют одинаковую частоту, для меня не имеет значения, какой из них указан первым, но я перечислил меньший код перед большим в примере. Наконец, если код вообще не отображается в предыдущих десяти строках, он также не должен быть указан в столбце freq.
И чтобы было предельно ясно, номер строки, в которой отображается строка частоты, не учитывает код состояния этой строки; это только предыдущие строки.
Итак, что я сделал? Я довольно хорошо разбираюсь в SQL. Я программист, и я нахожу этот язык SQL немного странным для привыкания. Я выполнил следующую инструкцию select для самостоятельного объединения.
select *, avg(b.status) freq
from sample a
join sample b
on (b.id < a.id) and (b.id > a.id - 11)
where a.id > 10
group by a.id;
Используя агрегатную функцию avg, я могу, по крайней мере, продемонстрировать концепцию. Производная таблица b предоставляет правильные строки для функции avg, но я просто не могу разобраться в многоступенчатом процессе подсчета и группировки строк из b, чтобы получить распределение частот, а затем свернуть строки частот в одно строковое значение.
Также я пытался использовать стандартные хранимые функции и процедуры вместо встроенных агрегатных функций, но, похоже, производная таблица b выходит за рамки или что-то в этом роде. Кажется, я не могу получить к нему доступ. И из того, что я понимаю, написание пользовательской агрегатной функции для меня невозможно, поскольку, похоже, требуется разработка на C, чему я не обучен.
Вот sql для загрузки образца.
create table sample (
id int NOT NULL AUTO_INCREMENT,
PRIMARY KEY(id),
status int
);
insert into sample(status) values(300),(100),(100),(200),(200),(300)
,(100),(400),(300),(300),(100),(400),(400),(400),(300),(300),(300)
,(100),(400),(100),(100),(200),(500),(300),(100),(400),(200),(100)
,(500),(300);
В примере есть 30 строк данных для работы. Я знаю, что это длинный вопрос, но я просто хотел быть настолько подробным, насколько мог. Я работал над этим несколько дней и действительно хотел бы это сделать.
Спасибо за вашу помощь.
Комментарии:
1. Просто примечание:
b.id > a.id - 11
будет работать, только если вы никогда не удаляете строки и ни одна вставка никогда не завершается неудачно. В противном случае вы не можете полагаться на то, что в значениях ID нет пробелов2. Это верно. Однако в этом конкретном приложении я могу гарантировать, что поле id ВСЕГДА будет последовательным. Спасибо.
Ответ №1:
SELECT id, GROUP_CONCAT(status ORDER BY freq desc) FROM
(SELECT a.id as id, b.status, COUNT(*) as freq
FROM
sample a
JOIN
sample b ON (b.id < a.id) AND (b.id > a.id - 11)
WHERE
a.id > 10
GROUP BY a.id, b.status) AS sub
GROUP BY id;
Ответ №2:
Единственный известный мне способ сделать то, о чем вы просите, — использовать BEFORE INSERT
триггер. Это должно быть BEFORE INSERT
потому, что вы хотите обновить значение в вставляемой строке, что может быть сделано только в BEFORE
триггере. К сожалению, это также означает, что ему еще не был присвоен идентификатор, поэтому, надеюсь, можно с уверенностью предположить, что на момент вставки новой записи последние 10 записей в таблице — это те, которые вас интересуют. Вашему триггеру необходимо будет получить значения последних 10 идентификаторов и использовать GROUP_CONCAT
функцию, чтобы объединить их в единую строку, упорядоченную по COUNT
. В основном я использую SQL Server, и на данный момент у меня нет доступа к серверу MySQL, чтобы протестировать это, но, надеюсь, мой синтаксис будет достаточно близким, чтобы, по крайней мере, заставить вас двигаться в правильном направлении:
create trigger sample_trigger BEFORE INSERT ON sample
FOR EACH ROW
BEGIN
DECLARE _freq varchar(50);
SELECT GROUP_CONCAT(tbl.status ORDER BY tbl.Occurrences) INTO _freq
FROM (SELECT status, COUNT(*) AS Occurrences, 1 AS grp FROM sample ORDER BY id DESC LIMIT 10) AS tbl
GROUP BY tbl.grp
SET new.freq = _freq;
END
Комментарии:
1. Когда я впервые увидел это, я был взволнован. Я подумал: «неужели это так просто?, я просто пропустил использование триггера?». Затем реальность поразила меня. После изучения триггеров я ни за что на свете не могу понять, как я могу использовать один из них в этом сценарии. Триггеры, насколько я понимаю, работают с реальными таблицами в базе данных, а не с результирующим набором из запроса. Это сработало бы, если бы я вставлял в таблицу samples, но это не то, что я делаю. Итак, если я чего-то не упускаю, что, конечно, возможно, я не думаю, что это может мне помочь. Тем не менее, спасибо за комментарий.
2. Я предположил, когда вы сказали: «Я хочу, чтобы в третьем столбце отображалась разделенная запятыми строка распределения частот кодов состояния предыдущих 10 строк». это означало, что вы добавляли столбец в свою таблицу. Итак, вы хотите сделать это в запросе?
3. Извините, если мне было непонятно это утверждение. Я предоставил запрос select в качестве примера того, как я пытался получить результирующий набор для отображения третьего столбца. Если вы можете заставить триггер работать с результирующим набором, это было бы здорово. Я все еще работаю над этим, и любая помощь очень ценится. Спасибо, что рассмотрели проблему со мной, Джоэл.