2013-12-04 3 views
0

У меня есть две таблицы:подсчета MySQL изменения значения колонные в наборе результатов

1) задачи - представляет задачу. Он имеет только первичный ключ, так как все связанные данные находятся в таблице task_version (задача HAS_MANY task_version).

CREATE TABLE task(
    id int(11) unsigned NOT NULL AUTO_INCREMENT, 
    PRIMARY KEY (id) 
); 

и выборка данных:

INSERT INTO task VALUES ('1'); 
INSERT INTO task VALUES ('2'); 

2) task_version - Любое изменение в любой задаче создает новую строку в этой таблице. task_id должен быть внешним ключом (пропущен для простоты). Это должно быть полное ведение всех изменений в задаче.

CREATE TABLE `task_version` (
id int(10) unsigned NOT NULL AUTO_INCREMENT, 
task_id int(11) DEFAULT NULL, 
name varchar(255) DEFAULT NULL, 
text varchar(255) DEFAULT NULL, 
status int(11) DEFAULT NULL, 
PRIMARY KEY (id) 
); 

Образец данных:

INSERT INTO `task_version` VALUES ('1', '1', 'Name of task', 'Text of task', '1'); 
INSERT INTO `task_version` VALUES ('2', '1', 'Name of task', 'Text of task', '1'); 
INSERT INTO `task_version` VALUES ('3', '1', 'Name of task', 'Text of task', '2'); 
INSERT INTO `task_version` VALUES ('4', '1', 'Name of task', 'Text of task', '1'); 
INSERT INTO `task_version` VALUES ('5', '2', 'Name', 'Text', '1'); 

Что мне нужно, чтобы получить статус изменяется количество на задачу.

Очевидно я не могу просто запросить различные статусы, как это:

SELECT 
(
    SELECT 
    COUNT(DISTINCT status) 
    FROM task_version 
    WHERE task_id = t.id 
) AS distinct_statuses_per_task, 
t.id AS task_id 
FROM task t 
INNER JOIN task_version tv ON t.id = tv.task_id 
GROUP BY t.id 

Поскольку distinct_statuses_per_task всего различные значения не изменяется qunatity. Если кто-то меняет статус от 1 до 2, от 2 до 1 и от 1 до 2 раз, мы получим эти Статусы последовательности:

1 
2 
1 
2 

Таким образом, мы имеем 2 различных состояния (1, 2), но 3 изменений состояния (1> 2, 2> 1, 1> 2), поэтому он не работает.

Я разработал решение с переменными пользователя MySQL. Это подзапрос, что я хочу, чтобы встроить в основной запрос:

SELECT 
CASE WHEN (status != @prev_status AND @prev_status IS NOT NULL) 
THEN @status_changes_quantity := @status_changes_quantity + 1 
END as incrementing_logic, 
@status_changes_quantity AS status_changes_quantity, 
@prev_status := status AS save_prev 
FROM task_version, 
(
    SELECT 
    @prev_status := NULL, 
    @status_changes_quantity := 0 
) as task_version_with_additional_vars 
WHERE task_id = 1 --Hardcoded task_id 
ORDER BY status_changes_quantity DESC 
LIMIT 1 

Это работает как автономный запрос с закодированным TASK_ID. Но мне нужно встроить этот запрос в подзапрос, чтобы получить количество изменений статусов для каждой задачи.

Я не могу заставить его работать. Проблема в том, что когда я устанавливаю переменные в части запроса SELECT, они становятся частью результата запроса. Подзапрос должен возвращать одиночный скаляр, но мой запрос возвращает таблицу (incrementing_logic, status_changes_quantity, save_prev) Я не знаю sintax, как избавиться от этих нежелательных colomns (incrementing_logic, save_prev).

I судимого это:

SELECT 
(
    SELECT 
    CASE WHEN (status != @prev_status AND @prev_status IS NOT NULL) 
    THEN @status_changes_quantity := @status_changes_quantity + 1 
    END as incrementing_logic, 
    @status_changes_quantity AS status_changes_quantity, 
    @prev_status := status AS save_prev 
    FROM task_version, 
    (
     SELECT 
     @prev_status := NULL, 
     @status_changes_quantity := 0 
    ) as task_version_with_additional_vars 
    WHERE task_id = t.id 
    ORDER BY status_changes_quantity DESC 
    LIMIT 1 
) AS status_changes_quantity, 
t.id AS task_id, 
tv.status AS task_status 
FROM task t 
INNER JOIN task_version tv ON t.id = tv.task_id 

Очевидно получили:

[Err] 1241 - Operand should contain 1 column(s) 

А потом я пытался обернуть подзапросы таблицу в другую TMP, чтобы избавиться от переменных полей и значения гер скалярного:

SELECT 
(
    SELECT 
    status_changes_quantity 
    FROM 
    (
     SELECT 

      CASE WHEN (status != @prev_status AND @prev_status IS NOT NULL) 
      THEN @status_changes_quantity := @status_changes_quantity + 1 
      END as incrementing_logic, 

      @status_changes_quantity AS status_changes_quantity, 

      @prev_status := status AS save_prev 

     FROM task_version, 
      (
       SELECT 
        @prev_status := NULL, 
        @status_changes_quantity := 0 
      ) as task_version_with_additional_vars 
     WHERE task_id = t.id 
     ORDER BY status_changes_quantity DESC 
     LIMIT 1 
    ) AS tmp_table 
) AS status_changes_quantity, 
t.id AS task_id, 
tv.status AS task_status 
FROM task t 
INNER JOIN task_version tv ON t.id = tv.task_id 

Я также получил аранжировку, которая теперь невидима в области подзапроса:

[Err] 1054 - Unknown column 't.id' in 'where clause' 

Возможно, кто-то знает, как решить мою проблему. Чтобы исправить мой запрос или предложить совершенно другой алгоритм.

Заранее спасибо.

ответ

0

Я слегка модифицировали свой запрос:

SELECT task_id, max(status_changes_quantity) 
FROM (
    SELECT 
     task_id, id, 
     CASE WHEN @prev_task_id <> task_id 
      THEN @status_changes_quantity := 0 
      WHEN status != @prev_status 
      THEN @status_changes_quantity := @status_changes_quantity + 1 
      ELSE @status_changes_quantity 
     END status_changes_quantity, 
     @prev_task_id := task_id, 
     @prev_status := status 
    FROM task_version, 
    (
     SELECT 
     @prev_status := NULL, 
     @prev_task_id := null, 
     @status_changes_quantity := 0 
) as task_version_with_additional_vars 
    -- WHERE task_id = 1 
    ORDER BY task_id, id 
) q 
GROUP BY task_id 
ORDER BY 2 DESC 

Demo ->http://www.sqlfiddle.com/#!2/c9ecc/14

Этот запрос вычисляет число изменений состояния для всех TASK_ID,
, а также только для одной конкретной задачи - если вы uncomment -- WHERE task_id = 1 статья.

0

@kordirko Большое спасибо. Ваша коррекция сделала трюк. Actualy, accrding to this article http://www.xaprb.com/blog/2006/12/15/advanced-mysql-user-variable-techniques/ Мне удалось удалить назначение переменных из набора результатов, чтобы избежать использования таблицы tmp.

Все, что нужно сделать (если я хорошо понимал), чтобы скрыть назначение переменных внутри функции БОЛЬШОЙ в дополнительном ИНЕК, что всегда будет звольвентные ИСТИНА как:

WHERE task_id = t.id 
AND GREATEST(
    @var1 := if(1 = 1, 'some_value', 'alt_value'),--conditional logic instead of CASE WHEN 
    @var := 123 -- simple assignment 
)-- this should evolute to true 

Так окончательный вариант что-то вроде этого:

SELECT 
(
    SELECT 
max(@status_changes_quantity) AS status_changes_quantity 
    FROM task_version, 
    (
     SELECT 
     @prev_status := NULL, 
     @status_changes_quantity := 0, 
     @prev_task_id :=0 
    ) as task_version_with_additional_vars 
    WHERE GREATEST(
       @status_changes_quantity := if(task_id != @prev_task_id, 0, @status_changes_quantity), 
       @prev_task_id := task_id, 
       @status_changes_quantity := if((status != @prev_status AND @prev_status IS NOT NULL), @status_changes_quantity + 1, @status_changes_quantity), 
       @prev_status := status 
    ) 
    AND task_id = t.id 
) AS status_changes_quantity, 
t.id AS task_id 
FROM task t 
INNER JOIN task_version tv ON t.id = tv.task_id 
GROUP BY t.id 
Смежные вопросы