MySQL Join Улучшение - PullRequest
       2

MySQL Join Улучшение

2 голосов
/ 23 сентября 2011

У меня проблемы с тем, чтобы MySQL правильно возвращал запрос.

Вот мои данные:

id            date            value
2             2011-01-04         55.66
2             2011-03-23         22.33
2             2011-04-21          9.44
5             2010-01-04        104.55
5             2011-02-03         38.82
...              ...              ...

Я пытаюсь получить запрос на возврат:

select t1.id, max(t1.date), t1.value, t2.id, min(t2.date), t2.value
from tab1 as t1, tab1 as t2
where t1.id = t2.id
and t1.date <= '2011-03-31'
and t2.date >= '2011-04-01'
group by t1.id;

Но это занимает вечность (дБ имеет ~ 1 мм линий).Я пробовал разные соединения, но потом, похоже, игнорирует операторы date <и>.По сути, я хочу, чтобы у каждого покупателя была дата и сумма последней покупки до 01.04.2011, а также их первая покупка и дата до или после 04.01.2011.Любые предложения будут великолепны.

Ответы [ 4 ]

1 голос
/ 24 сентября 2011

data - запрос на создание некоторых тестовых данных вместо создания таблицы с тестовыми данными.

before_query - извлекает максимальную дату <= 2011-03-31 для каждого идентификатора клиента </p>

after_query - извлекает минимальную дату> = 2011-04-01 для каждого идентификатора клиента

Кроме моей фиктивной таблицы Oracle dual (которую я использовал для генерации некоторых тестовых данных), я считаю, что я использовал только стандартный синтаксис SQL.

Вам не нужно будет генерировать данные, чтобы можно было пропустить часть их запроса.Везде, где на запрос ссылается data, замените его на table name.

with
    data as (select 2 as id, '2011-01-04' as trans_date, 55.66 as value from dual
                    union all
             select 2 as id, '2011-03-23' as trans_date, 22.33 as value from dual
                    union all
             select 2 as id, '2011-04-21' as trans_date, 9.44 as value from dual
                    union all
             select 5 as id, '2010-01-04' as trans_date, 104.55 as value from dual
                    union all
             select 5 as id, '2011-02-03' as trans_date, 38.82 as value from dual),

    before_qry as (select id, max(trans_date) as max_date from data
                   where trans_date <= '2011-03-31'
                   group by id),

    after_qry as (select id, min(trans_date) as min_date from data
                   where trans_date >= '2011-04-01'
                   group by id)

    select bq.*, bq_d.value, aq.*, aq_d.value
    from before_qry bq inner join after_qry aq on bq.id = aq.id
    inner join  data bq_d on bq.id = bq_d.id and bq.max_date = bq_d.trans_date
    inner join data aq_d on aq.id=aq_d.id and aq.min_date = aq_d.trans_date

. Для тестовых данных, показанных в вашем вопросе, этот запрос дает следующие результаты

        ID MAX_DATE        VALUE         ID MIN_DATE        VALUE
---------- ---------- ---------- ---------- ---------- ----------
         2 2011-03-23      22.33          2 2011-04-21       9.44
1 голос
/ 23 сентября 2011

Ваш запрос некорректен, столбцы t1.value и max (t1.date) не имеют отношения друг к другу.

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

SELECT st1.id, st1.date, st1.total_value, st2.id, st2.date, st2.total_value
FROM (SELECT t1.id, t1.date, sum(t1.value) as total_value
      FROM tab1 t1
      WHERE t1.date <= '2011-03-31'
      GROUP BY t1.id
      HAVING t1.date = MAX(t1.date)
      ) st1
INNER JOIN (SELECT t2.id, t2.date, sum(t2.value) as total_value
           FROM tab1 t2
           WHERE t2.date > '2011-03-31'
           GROUP BY t2.id
           HAVING t2.date = MAX(t2.date)
           ) st2 
  ON (st1.id = st2.id)

Убедитесь, что у вас есть индекс для id и date

Примечания
id обычно понимается как сокращение для первичного ключа.
Наличие поля с именем id, которое не aуникальный индекс, сбивает с толку и широко считается запах кода.

1 голос
/ 23 сентября 2011
SELECT
      td.id
    , ta.`date` AS before_date
    , ta.value AS value_at_before_date
    , tb.`date` AS after_date
    , tb.value AS value_at_after_date
FROM
    ( SELECT DISTINCT id
      FROM tabl
    ) AS td
  LEFT JOIN
    tabl AS ta
      ON ta.tablePK =   
        ( SELECT tablePK
          FROM tabl AS a 
          WHERE `date` < '2011-04-01'
            AND a.id = td.id
          ORDER BY `date` DESC
          LIMIT 1
        ) 
  LEFT JOIN
    tabl AS tb
      ON tb.tablePK =   
        ( SELECT tablePK
          FROM tabl AS b 
          WHERE `date` >= '2011-04-01'
            AND b.id = td.id
          ORDER BY `date` ASC
          LIMIT 1
        ) 

, где tablePK - это PRIMARY KEY таблицы (надеюсь, у вас есть).

Индекс на (id, date, tablePK) был бы полезен для скорости.

1 голос
/ 23 сентября 2011
SELECT t2.* 
FROM tab1 t2
INNER JOIN  
(SELECT t1.id,
MIN(CASE WHEN t1.date>='2011-04-01' THEN t1.date END) as min_date_1,
MAX(CASE WHEN t1.date<='2011-03-31' THEN t1.date END) as max_date_2
SUM(CASE WHEN t1.date>='2011-04-01' THEN t1.value END) sum_1,
SUM(CASE WHEN WHEN t1.date<='2011-03-31' THEN t1.value END) sum_2
FROM tab1 t1
GROUP BY t1.id)a ON 
(a.id = t2.id AND (t2.date = a.min_date_1 OR t2.date = a.max_date_2))

Это должно работать довольно быстро, если у вас есть индекс (id, дата).

ОБНОВЛЕНО Сумма добавлена ​​

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