Выберите строки на основе последней даты с несколькими объединениями

8

У меня есть этот запрос ( SQLFiddle ):

SELECT
c.name,
a.user_id,
a.status_id,
a.title,
a.rtime,
u.user_name,
s.status_name

FROM company c

LEFT JOIN action a ON a.company_id=c.id
LEFT JOIN user u ON u.id=a.user_id
LEFT JOIN status s ON s.id=a.status_id



WHERE u.user_name='Morgan'

-- WHERE c.name='Fiddle'

GROUP BY c.id

HAVING a.rtime IS NULL OR a.rtime = (
 SELECT max(rtime)
 FROM action a2
 WHERE deleted IS NULL
 AND a2.company_id = c.id
 )

Проблема 1

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

Проблема 2

Мне также нужно иметь возможность искать пользователя по имени, тем самым выбирая все компании, в которых этот пользователь выполнял последнее действие. Запрос сделан из формы, поэтому я могу вводить переменные.


Я не могу изменить базу данных SCHEMA на данный момент, но советы для будущей миграции очень ценятся.

Я пытался связать это вместе с, INNER JOIN ( SELECT.. ) t ONно я не могу обдумать это.

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

Версия MySQL: 5.5.16. Таблица компании имеет около 1 млн рядов, а таблица действий на 70 тыс. Растет. Производительность важна для меня здесь.

Как это можно решить?

stiq
источник
2
Посмотрите этот вопрос: MySQL оптимизированный запрос Вместо MAX(Marks)per TaskID, вы хотите MAX(ActivityDate)per Company.) Нет большой разницы, если у вас есть одна таблица или объединение.
ypercubeᵀᴹ

Ответы:

7

Ваш запрос может быть упрощен до:

SELECT
    c.id,
    c.name,
    a.rtime,
    s.status_name,
    u.user_name
FROM company c
    LEFT JOIN
      ( SELECT 
            company_id,
            MAX(rtime) AS maxdate
        FROM action
        WHERE deleted IS NULL
        GROUP BY company_id
      ) AS x ON x.company_id = c.id
    LEFT JOIN action a ON  a.deleted IS NULL
                       AND a.company_id = x.company_id 
                       AND a.rtime = x.maxdate 
    LEFT JOIN user u ON u.id = a.user_id
    LEFT JOIN status s ON s.id = a.status_id
WHERE 
    c.name LIKE 'Company%' ;

Индекс на (deleted, company_id, rtime)сделает подзапрос производной таблицы эффективным. Я полагаю, у вас уже есть индексы для столбцов, используемых для объединений и для Company (name).

SQL-Fiddle

ypercubeᵀᴹ
источник
1
Это проще и кажется примерно на 0,3 секунды быстрее на 100 строк, чем мой ответ.
Стик
2

После того, как я попробовал разные ответы, я заработал, но в моей настройке он оказался медленным.

ypercube указал мне на ответ, который указал на другие подобные вопросы о том, как MySQL группирует результаты. Это привело меня к этому ресурсу, где я наконец понял, что происходит.

В результате получается такой запрос:

SELECT
c.id,
c.name,

a.rtime,

s.status_name,

u.user_name


FROM company c

LEFT JOIN (
    SELECT 
        a.company_id,
        a.rtime,
        a.user_id,
        a.status_id
        FROM
            (
            SELECT company_id,
 MAX(rtime) as maxdate
            FROM action
            WHERE deleted IS NULL
            GROUP BY company_id
            ) as x

        LEFT JOIN action a ON a.rtime=x.maxdate AND a.company_id=x.company_id

) as a ON a.company_id=c.id

LEFT JOIN user u ON u.id=a.user_id
LEFT JOIN status s ON s.id=a.status_id

WHERE (1)

-- Search by user
-- AND u.user_name LIKE 'Johnny'

-- Seach by company name
AND c.name LIKE 'Company%'

Вот скрипка с рабочим запросом

stiq
источник
1

Вам необходимо переместить таблицы пользователя, действия и состояния в подзапрос, например так:

SELECT
c.name,
a.user_id,
a.status_id,
a.title,
a.max_rtime,
a.user_name,
a.status_name

FROM company c

LEFT JOIN
(select company_id, user_id, 
 max(rtime) max_rtime, 
 title, status_id, s.status_name,
 u.user_name
 from action INNER JOIN
  status s ON s.id=action.status_id
  INNER JOIN user u ON u.id=action.user_id
 where action.deleted IS NULL
 group by company_id, user_id, status_id, s.status_name, title, u.user_name
) a ON a.company_id=c.id AND a.user_name='Morgan'


GROUP BY c.id

SQL Fiddle

ча
источник