LINUX.ORG.RU

[postgresql] группировка

 


0

0

Что-то я туплю... Как из такой таблички:

id | group_id | value
----+----------+-----
1 | 1 | a
2 | 1 | b
3 | 1 | c
4 | 2 | d
5 | 3 | e
6 | 3 | f
7 | 4 | g

получить такую:

id | group_id | value
----+----------+-----
3 | 1 | c
4 | 2 | d
6 | 3 | f
7 | 4 | g

те выбрать в каждой группе строку с максимальным id.

в mysql это делается так:

select max(id), value from t group by group_id;

а вот в postgresql:

ERROR: колонка "t.value" должна фигурировать в выражении GROUP BY или использоваться в агрегатной функции...


Во-первых, я бы очень настоятельно посоветовал вам прочесть справочник по языку SQL, причем в его НОРМАЛЬНОЙ реинкарнации, а не в той что реализована в MySQL. В соответствии со стандартом, в запросе с группировкой ЛЮБОЙ аргумент в группе SELECT должен быть либо значением агрегатной функции (например MAX/MIN/AVG), либо быть вычислямым от выражения, встречающегося в блоке GROUP BY.

В твоем случае запрос может выглядеть так:

select max(id),max(value) from t group by group_id;
select max(id),min(value) from t group by group_id;
select max(id),value from t group by group_id,value;

Первые два варианта запроса скорей всего то, чего ты хотел

==== А теперь урок баз данных ====

Почему это именно так? Потому, чтj в твоем запросе возникает неоднозначность. Рассмотри пример с group_id=1:

select max(id),value from t group by group_id

При этом в группу попадают три записи:

1,1,a
2,1,b
3,1,c

Первый столбец (id) выбрасывается - его нет ни в select, ни в group by. Но группа из трез строк должна свернуться в одну (см. group by group_id). И какое же из значений value должен вернуть сервер? Значение "a"? Или значени "b"? Или значение "c"? А почему именно его, а не какое-то другое? А ведь SQL-оператор обязан быть однозначно вычисляемым, а в твоем случае это правило нарушено.

==== Нецензурные выражения ====

&^$%, *(&^%^ любители MySQL!!! Вы %#&^% &^%*^% $%$*$ %^(*^ $%&*^%%^ $%(%( !!!! Когда же вы наконец-то начнете писать НОРМАЛЬНЫЙ код и проектировать НОРМАЛЬНЫЕ структуры данных, а не эту &*%*% ^$%&*^&^%$&^ %*%&* &^*(%*&, которую вы почему-то называете "запросами" и "базами"?!?! Прослушайте &*^%&$% $%&^&* (*&^^%&% наконец-то какой-нибудь курс по базам данных!!!

no-dashi ★★★★★
()
Ответ на: комментарий от aydar

> select t2.*
> from (select group_id, max(id) mid

> from t

> group by group_id) t1,

> t t2

> where t1.mid = t2.id


Это другой запрос, возвращающий другое количество строк.

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

no-dashi ★★★★★
()
Ответ на: комментарий от no-dashi

Вдогонку к нецензурным выражениям:

... и когда-нибудь кто-нибудь втретит автора кода mysql_query("delete from table where id = ".$_GET["id_to_del"]), то желаю вам успешно избавить цивилизацию от ДНК этого феерического ПТУшника :-(

no-dashi ★★★★★
()
Ответ на: комментарий от aydar

> Подумал, что запрос будет работать в любой СУБД, которая поддерживает вложенные запросы

Дело не в синтаксисе, к синтаксису претензий нет :-) Просто если ты прогонишь свой запрос, то увидишь что твой запрос возвращает семь строк, а изначальны запрос автора в MySQL вернет 4 строки :-)

no-dashi ★★★★★
()
Ответ на: комментарий от no-dashi

no-dashi, а нормально что max(id),max(value) эквивалентно max(id),min(value)? :)

Я бы предпочёл чтобы было что-то типа select id,value from t group by group_id having maximum(id). Но вот нету такой агрегатной функции которая бы возвращала true на максимальное значение.

Только не кидайте камнями, я с sql не дружу :)

true_admin ★★★★★
()
Ответ на: комментарий от true_admin

>а нормально что max(id),max(value) эквивалентно max(id),min(value)? :)

Где же они эквивалентны? Результаты разные будут.

sdio ★★★★★
()

ха, это первое на что натыкается mysql-щик в postgresql
на sql.ru каждую неделю кто нибудь спрашивает %-)

hizel ★★★★★
()
Ответ на: комментарий от true_admin

sqlite> create table t (id integer primary key,
                        group_id integer,
                        value text);
sqlite> insert into t VALUES (1,1,'a');
sqlite> insert into t VALUES (2,1,'b');
sqlite> insert into t VALUES (3,1,'c');
sqlite> insert into t VALUES (4,2,'d');
sqlite> insert into t VALUES (5,3,'e');
sqlite> insert into t VALUES (6,3,'f');
sqlite> insert into t VALUES (7,4,'g');
sqlite> select max(id),max(value) from t group by group_id;
3|c
4|d
6|f
7|g
sqlite> select max(id),min(value) from t group by group_id;
3|a
4|d
6|e
7|g

sdio ★★★★★
()
Ответ на: комментарий от hizel

>ха, это первое на что натыкается mysql-щик в postgresql
на sql.ru каждую неделю кто нибудь спрашивает %-)

Ссылочку можно ? :)

xtron
() автор топика
Ответ на: комментарий от no-dashi

>В твоем случае запрос может выглядеть так:
>select max(id),max(value) from t group by group_id;

>select max(id),min(value) from t group by group_id;

>select max(id),value from t group by group_id,value;


мне нужен max(id), но не max(value)/min(value).
третий же запрос просто не схлопнет записи так как у строк разные value

>Значение "a"? Или значени "b"? Или значение "c"?

Казалось что значение max(id)

>Прослушайте &*^%&$% $%&^&* (*&^^%&% наконец-то какой-нибудь курс по базам данных!!!

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

xtron
() автор топика
Ответ на: комментарий от sdio

>Где же они эквивалентны? Результаты разные будут.

во-во, потому предложенные no-dashi:
select max(id),max(value) from t group by group_id;
select max(id),min(value) from t group by group_id;
не подходят, а предложенный
select max(id),value from t group by group_id,value;
просто не схлопывает строки в виду того что value у них разный

xtron
() автор топика
Ответ на: комментарий от borisych

Да нет это дает вообще другой результат.

Можно конечно написать вот так:
select id, group_id, value from t where id in
(select max(id) from t group by group_id);

но хочется более стройного решения без подзапросов потому и обратился к присутствующим

xtron
() автор топика
Ответ на: комментарий от borisych

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

xtron
() автор топика
Ответ на: комментарий от xtron

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

SELECT DISTINCT
group_id,
LAST_VALUE (VALUE) OVER (PARTITION BY GROUP_ID),
MAX (id) OVER (PARTITION BY GROUP_ID)
FROM t

borisych ★★★★★
()

вообщем самый человеческий запрос для этой задачи выглядит так:

select distinct on ( group_id ) group_id, id, value from t order by group_id, id desc;

xtron
() автор топика
Ответ на: комментарий от xtron

оптимизатору postgres этот запрос нравится больше всех

xtron
() автор топика

в последней версии постгреса реализовали (наконец-таки!) оконные функции. попробуй их использовать (ну еже ли у тебя последняя стоит). сразу же избавишься от сабсиквела.

Deleted
()
Ответ на: комментарий от borisych

>тот запрос что я привел достаточно классический, если не нравится, то >вот Вам оракловый без подзапроса, правда съест ли постгрес хз:

>SELECT DISTINCT

>group_id,

>LAST_VALUE (VALUE) OVER (PARTITION BY GROUP_ID),

>MAX (id) OVER (PARTITION BY GROUP_ID)

>FROM t


ой, а я тут как раз про оконные функции отписал. не заметил твой камент.

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

Deleted
()
Ответ на: комментарий от borisych

чет я облажался, правильно так:

SELECT DISTINCT
GROUP_ID,
LAST_VALUE(VALUE)
OVER (PARTITION BY GROUP_ID
ORDER BY id
ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING),
MAX (id) OVER (PARTITION BY GROUP_ID)
FROM t

borisych ★★★★★
()
Ответ на: комментарий от no-dashi

> ... и когда-нибудь кто-нибудь втретит автора кода mysql_query("delete from table where id = ".$_GET["id_to_del"]), то желаю вам успешно избавить цивилизацию от ДНК этого феерического ПТУшника :-(

ммм... может быть я конечно туплю, но собственно а что тут не так :-? если я хочу удалить запись с id 123456, то прямо пишу DELETE FROM foo WHERE id = 123456; какой тут криминал? или ты по SQL inject? но тут MySQL как таковой не причём, это скорее к начинающим PHP-шникам.

// wbr

klalafuda ★☆☆
()
Ответ на: комментарий от klalafuda

Вот что постгрес проглотил

SELECT DISTINCT MAX (id) OVER (PARTITION BY GROUP_ID), GROUP_ID, LAST_VALUE(VAL) OVER (PARTITION BY GROUP_ID ORDER BY id ROWS BETWEEN UNBOUNDED PRECEDING AND UNBOUNDED FOLLOWING) FROM (VALUES (1 , 1 , 'a'), (2 , 1 , 'b'), (3 , 1 , 'c'), (4 , 2 , 'd'), (5 , 3 , 'e'), (6 , 3 , 'f'), (7 , 4 , 'g')) AS T(id , group_id , val) ORDER BY GROUP_ID ASC

oktogen
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.