Выберите в MySQL, где все строки соответствуют условию - PullRequest
5 голосов
/ 11 февраля 2009

В MySQL, как вы можете выбрать данные, где каждая строка соответствует определенному условию? Например, допустим, у меня есть таблица, показывающая, когда сотрудники прибыли на работу, она имеет три поля:

CREATE TABLE ArrivalTimes
(UserID INT
,Day DATE 
,ArrivalTime TIME
);

Я хочу выбрать все идентификаторы пользователей, которые никогда не опаздывали (прибыли в 9 утра или раньше), как лучше всего это сделать?

Ответы [ 5 ]

17 голосов
/ 11 февраля 2009

Ответы @jjclarkson и @ davethegr8 близки, но вы не можете поместить агрегатные функции в предложение WHERE. Предложение WHERE оценивается для каждой строки.

Вам необходимо оценить выражение MAX() для каждой группы, поэтому вам нужно использовать предложение HAVING.

Попробуйте это:

SELECT UserID 
FROM ArrivalTimes
GROUP BY UserID
HAVING MAX(ArrivalTime) <= '09:00:00';

@ MBCook комментирует, что HAVING может быть медленным. Вы правы, это может быть не самый быстрый способ добиться желаемого результата. Но решение HAVING является наиболее ясным . В некоторых ситуациях производительность имеет более низкий приоритет, чем четкость и удобство обслуживания.

Я посмотрел на вывод EXPLAIN (на MySQL 5.1.30) для решения HAVING: никакие индексы не использовались, и в дополнительных примечаниях говорилось "Using temporary; Using filesort", что обычно означает, что производительность будет низкой.

Рассмотрим следующий запрос:

SELECT DISTINCT a1.UserID
FROM ArrivalTimes a1
  LEFT OUTER JOIN ArrivalTimes a2 
  ON (a1.UserID = a2.UserID AND a2.ArrivalTime > '09:00:00')
WHERE a2.UserID IS NULL;

Создает план оптимизации, который использует индекс для UserID и говорит:

  • a1: "Using index; Using temporary"
  • a2: "Using where; Distinct"

Наконец, следующий запрос генерирует план оптимизации, который, по-видимому, использует индексы наиболее эффективно, без временных таблиц или сортировки файлов.

SELECT DISTINCT a1.UserID
FROM ArrivalTimes a1
WHERE NOT EXISTS (SELECT * FROM ArrivalTimes a2 
                  WHERE a1.UserID = a2.UserID 
                    AND a2.ArrivalTime > '09:00:00'); 
  • a1: "Using where; Using index"
  • a2: "Using where"

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

1 голос
/ 04 февраля 2014

вы можете получить результат более 3 способов для этого запроса 1. Используя Групповую Функцию 2. использование подзапроса 3. использование соединений ...... и т. Д.

с использованием группы - по

ВЫБЕРИТЕ userID, MAX (ArrivalTime) как последний FROM ArrivalTimes ГДЕ последний <= '9: 00: 00' </p>

GROUP BY userID

с использованием подзапроса с

выбрать * у пользователя где '09: 00: 00 '

= все (выберите ArrivalTime из ArrivalTime b, где b.UserID = a.ID);


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

1 голос
/ 28 апреля 2009

Билл Карвин предлагает:

Попробуйте это:

SELECT UserID 
FROM ArrivalTimes
GROUP BY UserID
HAVING MAX(ArrivalTime) <= '09:00:00';

Я посмотрел на вывод EXPLAIN (на MySQL 5.1.30) для решения HAVING: индексы не использовались, и в дополнительных примечаниях говорилось: «Использование временного; Использование сортировки файлов», что обычно означает, что производительность будет плохой.

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

 select * from user a 
 where '09:00:00' 
  >= all( select ArrivalTime from ArrivalTime b where b.UserID = a.ID);

Это выбирает любого пользователя, который когда-либо запаздывал:

 select * from user a 
 where '09:00:00' 
  < any( select ArrivalTime from ArrivalTime b where b.UserID = a.ID);

Это более понятно, поскольку оно еще более близко соответствует нашей спецификации английского / естественного языка.

И это позволяет избежать неэффективности group by; под MySql 5.0.51 для не требуется временная или файловая сортировка, как это делает Билл.

(Обратите внимание, что требуется, чтобы значение постоянного времени было заполнено нулями, таким образом: '09:00:00'; '9:00:00' терпит неудачу.)

1 голос
/ 11 февраля 2009
SELECT userID, MAX(ArrivalTime) as latest
FROM ArrivalTimes 
WHERE latest <= '9:00:00'
GROUP BY userID
1 голос
/ 11 февраля 2009

Это была хорошая мысль, но она не работает.

SELECT UserID FROM ArrivalTimes WHERE MAX(ArrivalTime) <= '09:00:00' GROUP BY UserID

В этом запросе вы получите сообщение об ошибке: «Неправильное использование групповой функции»

Агрегатные функции, такие как COUNT, MAX, MIN, AVG, SUM и другие по определению, выполняют свои функции над набором (или группой записей), поэтому MAX (ArrivalTime) должен иметь вид

GROUP BY UserID HAVING MAX(ArrivalTime) <= '09:00:00'

См. Ответ от @Bill Karwin выше.

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