MySQL группа с использованием значения записи диапазона - PullRequest
3 голосов
/ 17 апреля 2011

В SQL у меня есть таблица t_test ниже:

emp_code | period_month | company_code  
NIK001   | 01           | ALPHA
NIK001   | 02           | ALPHA
NIK001   | 03           | ALPHA
NIK001   | 04           | ALPHA
NIK001   | 05           | ALPHA
NIK001   | 06           | BETA 
NIK001   | 07           | BETA
NIK001   | 08           | BETA
NIK001   | 09           | BETA
NIK001   | 10           | ALPHA
NIK001   | 11           | ALPHA
NIK001   | 12           | ALPHA

Я хочу запросить с результатом ниже:

emp_code | company_code | from_month  | to_month 
--------------------------------------------------
NIK001   | ALPHA        | 01          | 05
NIK001   | BETA         | 06          | 09
NIK001   | ALPHA        | 10          | 12

Ответы [ 3 ]

1 голос
/ 17 апреля 2011

Вы можете использовать переменную MySQL. Например:

SET @ttt = 0;

SELECT
    @ttt := IF(
        `company_code`=(SELECT `company_code` FROM `range` r2 WHERE r2.`period_month`<r1.`period_month` ORDER BY `period_month` DESC LIMIT 1), 
        @ttt,
        @ttt+1) iter,
`period_month`,
`company_code`
FROM `range` r1
WHERE emp_code = 'NIK001'
ORDER BY period_month ASC;

На моих тестовых данных это дает мне:

iter period_month company_code
1    1            Alpha
1    2            Alpha
1    3            Alpha
2    4            Beta
2    5            Beta
2    6            Beta
3    7            Alpha
3    8            Alpha
3    9            Alpha
3    10           Alpha

Идея состоит в том, что в каждой строке вы проверяете, равен ли текущий company_code значению предыдущей записи. Если он другой, увеличьте переменную.

Это шаг вперед. Но как сгруппировать строки? Вы не можете сгруппировать их напрямую, но вам нужно обернуть этот запрос вторым.

SET @ttt = 0;

SELECT `company_code`,MIN(`period_month`),MAX(`period_month`)
FROM
(    SELECT
        @ttt := IF(
            `company_code`=(SELECT `company_code` FROM `range` r2 WHERE r2.`period_month`<r1.`period_month` ORDER BY `period_month` DESC LIMIT 1), 
            @ttt,
            @ttt+1) iter,
    `period_month`,
    `company_code`
    FROM `range` r1
    WHERE emp_code = 'NIK001'
    ORDER BY period_month ASC
    ) subreq
GROUP BY iter
;

Это использует результат внутреннего запроса, во внешнем запросе. Это дает мне, с моими данными испытаний,

company_code MIN(`period_month`) MAX(`period_month`)
Alpha        1                   3
Beta         4                   6
Alpha        7                   10

Если вам нужно ограничить выбор строк, сделайте это в предложении where в запросе inner , иначе вся база данных будет загружаться в память при каждом запросе. Другая проблема заключается в том, что внутренний запрос извлекает предыдущую запись как «запись с ближайшим нижним значением period_month. В результате вы должны не сортировать внутренний запрос по чему-либо, кроме period_month ASC. если хотите, можете сортировать вещи во внешнем запросе.

Наконец, , если по какой-то причине вы используете старый интерфейс mysql_query, вам нужно поместить строку SET в отдельный запрос, поскольку он может обрабатывать только один запрос за раз .

mysql_query("SET @ttt = 0;");

$rs=mysql_query("SELECT `company_code`,MIN(`period_month`),MAX(`period_month`)
FROM
(    SELECT
        @ttt := IF(
            `company_code`=(SELECT `company_code` FROM `range` r2 WHERE r2.`period_month`<r1.`period_month` ORDER BY `period_month` DESC LIMIT 1), 
            @ttt,
            @ttt+1) iter,
    `period_month`,
    `company_code`
FROM `range` r1) subreq
    GROUP BY iter
;");

Не уверен, насколько хорошо этот метод работает при масштабировании до больших баз данных, но он определенно работает. Может быть, есть «хороший» способ. Просто убедитесь, что вы случайно не напутали что-то в запросе, если вы не до конца понимаете это. :)

1 голос
/ 17 апреля 2011

Это заставляет меня дрожать от страха, но выдает приблизительное значение того, что вы ищете:

    select w.emp_code, w.company_code, w.period_month from_month,
(select min(convert(u.period_month,unsigned))-1 from t_test u where u.emp_code=w.emp_Code and convert(u.period_month,signed)>convert(w.period_month,signed) and u.company_code<>w.company_code) to_month
 from 
(
select * from
(
select y.emp_code, y.period_month, y.company_code,
(select x.company_code from t_test x where x.emp_code=y.emp_code and convert(x.period_month,unsigned)<convert(y.period_month,unsigned) order by convert(x.period_month,unsigned) desc limit 1) previous_company_code
 from t_test y
) z
where company_code<>previous_company_code or previous_company_code is null
) w

Однако я попробовал это просто из любопытства.Вы не должны полагаться на что-либо подобное в своей производственной среде.Я не думаю, что простой SQL-запрос хорошо подходит для вашей проблемы.Я бы посмотрел в другом месте.

РЕДАКТИРОВАТЬ: На основе следующей таблицы:

CREATE TABLE `t_test` (
  `emp_code` varchar(50) DEFAULT NULL,
  `period_month` varchar(2) DEFAULT NULL,
  `company_code` varchar(50) DEFAULT NULL
);

Заполнено следующими операторами INSERT:

INSERT INTO `t_test` (`emp_code`,`period_month`,`company_code`)
VALUES
    ('NIK001', '01', 'ALPHA'),
    ('NIK001', '02', 'ALPHA'),
    ('NIK001', '03', 'ALPHA'),
    ('NIK001', '04', 'ALPHA'),
    ('NIK001', '05', 'ALPHA'),
    ('NIK001', '06', 'BETA'),
    ('NIK001', '07', 'BETA'),
    ('NIK001', '08', 'BETA'),
    ('NIK001', '09', 'BETA'),
    ('NIK001', '10', 'ALPHA'),
    ('NIK001', '11', 'ALPHA'),
    ('NIK001', '12', 'ALPHA');
0 голосов
/ 19 апреля 2011

@ магма: большое спасибо за ответ. хорошая работа! спасибо ..

Я только что внес небольшую модификацию, чтобы from_month и to_month никогда не получали значение NULL.

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

SELECT
  w.emp_code, w.company_code, w.period_month from_month,
  LPAD(
    IFNULL(
    IFNULL(
      ( select min(u.period_month)-1
        from t_test u
        where (u.emp_code = w.emp_code)
        and (u.period_month > w.period_month)
        and (u.company_code  w.company_code)
      ),( select max(v.period_month)
          from t_test v
          where
          (v.emp_code=w.emp_code)
          and (v.period_month > w.period_month)
          and (v.company_code = w.company_code)
         )
    ),( select max(z.period_month)
          from t_test z
          where
          (z.emp_code=w.emp_code)
          and (z.period_month = w.period_month)
          and (z.company_code = w.company_code)
         )
    ),2,'0'
  ) AS to_month
FROM
  ( select *
    from
    (
      select
        y.emp_code, y.company_code, y.period_month, 
        ( select x.company_code
          from t_test x
          where
          (x.emp_code = y.emp_code)
          and (x.period_month  previous_company_code)
  or (previous_company_code is null)
  ) w
...