WedX - журнал о программировании и компьютерных науках

Оператор Select с агрегацией MAX() в предложении Where

У меня есть таблица базы данных, в которой ежегодно хранятся обновления членства. При вставке записи о продлении в столбец «expiryDate» записывается дата (31/8/[следующий год]).

Итак, в качестве примера, скажем, член с memberID = 99 продлевает подписку в 2007, 2008 и 2009 годах, у него будет 3 записи (по одной на каждый год), в каждой из которых записано «expiryDate». Если я сделаю

SELECT MAX(YEAR(expiryDate)) as maxExpiry 
  FROM renewals 
 WHERE memberID = 99

...Я верну 2010 год.

Что я хотел бы сделать, так это вернуть ВСЕ записи, где MAX(YEAR(expiryDate)) - данный год.. например,

SELECT * 
  FROM renewals 
 WHERE MAX(YEAR(expiryDate)) = '2010';

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


Ответы:


1

Предикат, основанный на агрегированном столбце, использует предложение HAVING, а не WHERE.

Если вам нужен только идентификатор участника, это достаточно просто:

SELECT memberID
  FROM renewals
  GROUP BY memberID
    HAVING MAX(YEAR(expiryDate)) = 2010

Вы также можете сделать это как подзапрос, если вам нужно получить другие столбцы из этой таблицы, например:

SELECT * FROM members
  WHERE memberID IN ( <<previous query>> )

ОБНОВЛЕНИЕ

Это правильно, поскольку @OMG Ponies указал, что этого недостаточно, если вам нужно выбрать дополнительные столбцы из этой строки в renewals. Если это необходимо, вы можете использовать:

SELECT * FROM renewals
  WHERE memberID IN ( SELECT memberID FROM renewals
                      GROUP BY memberID HAVING MAX(YEAR(expiryDate)) = 2010 )
    AND YEAR(expiryDate) == 2010
11.11.2011
  • @Jason, В дополнение к ЭТОМУ ответу добавьте предложение WHERE с датой, равной или большей, чем 1 января 2010 г. ... это устранит все старые обновления до того, что вы все равно выбросите ... Не знаю, насколько велик ваш таблица есть, для проблем с производительностью. 11.11.2011
  • Сработало отлично.. спасибо за разнообразие вариантов и вариантов!! На самом деле это часть более крупного динамического запроса, поэтому мы перешли к подзапросу, используя HAVING.. сработало.. Также спасибо за совет, DRapp, отличная идея! Спасибо всем! Джейсон 11.11.2011

  • 2

    Используйте ГРУППИРОВАТЬ ПО

    SELECT memberID, MAX(YEAR(expiryDate))
      FROM renewals 
    GROUP BY memberID
    HAVING MAX(YEAR(expiryDate)) = 2010
    
    11.11.2011

    3

    Для SQL Server 2005+ используйте:

    WITH cte AS (
      SELECT r.*,
             ROW_NUMBER() OVER (PARTITION BY r.memberid
                                    ORDER BY r.expirydate DESC) AS rnk
        FROM RENEWALS r)
    SELECT c.*
      FROM cte c
     WHERE c.rnk = 1
       AND YEAR(c.expirydate) = 2010
    

    CTE не является реальной причиной для того, чтобы быть 2005+ - это использование ROW_NUMBER, потому что его можно переписать, чтобы не использовать CTE.

    Проблема с подзапросом заключается в том, что получения memberid (как вы видите в других ответах) недостаточно для присоединения к копии таблицы RENEWALS. Вы получите все записи для этих участников, и вам все равно нужно отфильтровать то, что вы ищете.

    11.11.2011
  • Действительно, это правда. В своем ответе я предположил, что, поскольку memberID дублируется в таблице renewals, он должен быть FK где-то еще, что я предположил как members. Чтобы присоединиться к renewals, необходимы другие меры. 11.11.2011

  • 4

    Этому вопросу уже несколько месяцев, и на него есть принятый ответ, а также еще два действительных ответа. Тем не менее, я добавляю еще один:

    SELECT *
    FROM   renewals r
    WHERE  expiryDate >= '20100101'  -- unambiguous input format with any locale!
    AND    expiryDate <  '20110101'
    AND    NOT EXISTS (
        SELECT *
        FROM   renewals r0
        WHERE  r0.memberID   = r.memberID
        AND    r0.expiryDate > r.expiryDate
        );
    

    Почему? Все предыдущие ответы будут медленными для больших таблиц, потому что они не могут использовать индекс для expiryDate. Это можно. Аарон Бертран (также активно работающий над SO) написал блог на тему здесь, что в поразительных деталях согласуется с что я продолжаю проповедовать для PostgreSQL.

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

    Кроме того, этот запрос предотвращает создание нескольких строк для одного и того же члена. Он возвращает только последнюю строку за 2010 год для каждого члена, если для года должно быть несколько записей. По описанию не должно происходить, но легко могут быть исключения. Я предполагаю, что это то, что нужно. Ответ @OMG Ponies - пока единственный ответ, в котором учтена эта деталь. По иронии судьбы, до сих пор он был единственным, кто не проголосовал за.

    05.03.2012
    Новые материалы

    Как проанализировать работу вашего классификатора?
    Не всегда просто знать, какие показатели использовать С развитием глубокого обучения все больше и больше людей учатся обучать свой первый классификатор. Но как только вы закончите..

    Работа с цепями Маркова, часть 4 (Машинное обучение)
    Нелинейные цепи Маркова с агрегатором и их приложения (arXiv) Автор : Бар Лайт Аннотация: Изучаются свойства подкласса случайных процессов, называемых дискретными нелинейными цепями Маркова..

    Crazy Laravel Livewire упростил мне создание электронной коммерции (панель администратора и API) [Часть 3]
    Как вы сегодня, ребята? В этой части мы создадим CRUD для данных о продукте. Думаю, в этой части я не буду слишком много делиться теорией, но чаще буду делиться своим кодом. Потому что..

    Использование машинного обучения и Python для классификации 1000 сезонов новичков MLB Hitter
    Чему может научиться машина, глядя на сезоны новичков 1000 игроков MLB? Это то, что исследует это приложение. В этом процессе мы будем использовать неконтролируемое обучение, чтобы..

    Учебные заметки: создание моего первого пакета Node.js
    Это мои обучающие заметки, когда я научился создавать свой самый первый пакет Node.js, распространяемый через npm. Оглавление Глоссарий I. Новый пакет 1.1 советы по инициализации..

    Забудьте о Matplotlib: улучшите визуализацию данных с помощью умопомрачительных функций Seaborn!
    Примечание. Эта запись в блоге предполагает базовое знакомство с Python и концепциями анализа данных. Привет, энтузиасты данных! Добро пожаловать в мой блог, где я расскажу о невероятных..

    ИИ в аэрокосмической отрасли
    Каждый полет – это шаг вперед к великой мечте. Чтобы это происходило в их собственном темпе, необходима команда астронавтов для погони за космосом и команда технического обслуживания..


    Для любых предложений по сайту: [email protected]