Можно ли добавить условия к вызову MAX () в агрегированном запросе? - PullRequest
2 голосов
/ 16 декабря 2011

Фон

Мой типичный вариант использования:

# Table

id     category    dataUID
---------------------------
0         A        (NULL)
1         B        (NULL)
2         C        text1
3         C        text1
4         D        text2
5         D        text3

# Query

SELECT MAX(`id`) AS `id` FROM `table`
GROUP BY `category`

Это хорошо; он удалит все «дублирующиеся категории» в наборе записей, над которым работает, и даст мне «самый высокий» идентификатор для каждой категории.

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

# Query

SELECT * FROM `table` JOIN (
   SELECT MAX(`id`) AS `id` FROM `table`
   GROUP BY `category`
) _ USING(`id`)

# Result

id     category    dataUID
---------------------------
0         A        (NULL)
1         B        (NULL)
3         C        text1
5         D        text3

Обратите внимание, что это не так же, как:

SELECT MAX(`id`) AS `id`, `category`, `dataUID` FROM `table`
GROUP BY `category`

За документацию :

В стандартном SQL запрос, включающий предложение GROUP BY, не может ссылаться на неагрегированные столбцы в списке выбора, которые не названы в Предложение GROUP BY. Например, этот запрос недопустим в стандартном SQL потому что имя столбца в списке выбора не отображается в GROUP BY:

SELECT o.custid, c.name, MAX(o.payment)   FROM orders AS o, customers
AS c   WHERE o.custid = c.custid   GROUP BY o.custid;

Чтобы запрос был законным, столбец имени должен быть опущен выберите список или имя в предложении GROUP BY.

MySQL расширяет использование GROUP BY, так что список выбора может ссылаться на неагрегированные столбцы, не указанные в предложении GROUP BY. Это означает что предыдущий запрос допустим в MySQL. Вы можете использовать эту функцию чтобы получить лучшую производительность, избегая ненужной сортировки столбцов и группировка. Однако это полезно, прежде всего, когда все значения в каждом Неагрегированные столбцы, не названные в GROUP BY, одинаковы для каждого группа.

[..]

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

Так что я бы получил неопределенное значение для dataUID & mdash; например, text2 или text3 для результата с id 5.

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


Проблема

Однако!

Если какая-либо из строк для данного category имеет NULL dataUID, и хотя бы еще одна строка имеет не-1061 * dataUID, я бы хотел, чтобы MAX игнорировал NULL единицы.

Итак:

id     category    dataUID
---------------------------
4         D        text2
5         D        (NULL)

В настоящее время, поскольку я выбираю строку с максимальным идентификатором, я получаю:

5         D        (NULL)

Но, поскольку dataUID равен NULL, вместо этого я хочу:

4         D        text2

Как я могу получить это? Как добавить условную логику к использованию агрегата MAX?


Я подумал о том, чтобы, возможно, передать MAX кортеж и потом извлечь из него id:

GET_SECOND_PART_SOMEHOW(MAX((IF(`dataUID` NOT NULL, 1, 0), `id`))) AS `id`

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

Ответы [ 6 ]

4 голосов
/ 16 декабря 2011

небольшая настройка @ ответа ypercube .Чтобы получить id s, вы можете использовать

SELECT COALESCE(MAX(CASE
                      WHEN dataUID IS NOT NULL THEN id
                    END), MAX(id)) AS id
FROM   table
GROUP  BY category  

, а затем подключить его к join

1 голос
/ 16 декабря 2011
select * 
from t1
join (
  select max(id) as id,
    max(if(dataGUID is NULL, NULL, id)) as fallbackid,
    category
  from t1 group by category) as ids
on if(ids.id = fallbackid or fallbackid is null, id, fallbackid) = t1.id; 
1 голос
/ 16 декабря 2011

В конце концов, это оказалось проще, чем я думал, потому что оказывается, что MySQL будет принимать произвольное выражение внутри MAX.

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

SUBSTRING(MAX(IF (`dataUID` IS NULL, CONCAT('a',`id`), CONCAT('b',`id`))) FROM 2)

Прохождение:

id     category    dataUID    IF (`dataUID` IS NULL, CONCAT('a',`id`), CONCAT('b',`id`)
--------------------------------------------------------------------------------------
0         A        (NULL)                             a0
1         B        (NULL)                             a1
2         C        text1                              b2
3         C        text1                              b3
4         D        text2                              b4
5         D        (NULL)                             a5

Итак:

SELECT
   `category`, MAX(IF (`dataUID` IS NULL, CONCAT('a',`id`), CONCAT('b',`id`)) AS `max_id_with_hint`
FROM `table`
GROUP BY `category`

category   max_id_with_hint
------------------------------
A          a0
B          a1
C          b3
D          b4

Тогда снова легко отключить подсказку по порядку.

В частности, спасибо @JlStone за то, что я установил через COALESCE путь для встраивания выражений внутри вызова MAX и непосредственно манипулировал им.значения, указанные для MAX.

1 голос
/ 16 декабря 2011
SELECT t.*
FROM table AS t
  JOIN
  ( SELECT DISTINCT category
    FROM table
  ) AS tdc
  ON t.id = 
  COALESCE(
    ( SELECT MAX(id) AS id 
      FROM table 
      WHERE category = tdc.category
        AND dataUID IS NOT NULL
    ) 
  , ( SELECT MAX(id) AS id 
      FROM table 
      WHERE category = tdc.category
        AND dataUID IS NULL
    ) 
  )
0 голосов
/ 27 февраля 2017

вам нужен пункт OVER

SELECT id, category,dataUID 
FROM
 (
    SELECT ROW_NUMBER() OVER (PARTITION BY category ORDER BY id desc, dataUID desc ) rn, 
    id, category,dataUID FROM table 
 ) q
WHERE rn=1

Учтите, что сортировка по desc, наконец, перемещает нулевые значения.

0 голосов
/ 16 декабря 2011

Насколько я помню, вы можете использовать COALESCE внутри группирующих операторов. Например.

SELECT MAX(COALESCE(`id`,1)) ...

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

SELECT * FROM `table` JOIN (
   SELECT MAX(`id`) AS `id` FROM `table`
   WHERE `dataUID` IS NOT NULL
   GROUP BY `category`
) _ USING(`id`)

или, возможно,

SELECT MAX(`id`) AS `id`, 
  COALESCE (`dataUID`, 0) as `dataUID`
FROM `table`
GROUP BY `category`
...