metaclass: (Default)
metaclass ([personal profile] metaclass) wrote2009-07-09 08:34 am

SQL извраты

Не хватает в SQL аггрегирующей операции типа MIN или MAX(поле1, поле2, поле3), т.е. минимум или максимум тупла из полей. Операция сравнения задана обычным образом - сначала сравниваем первое поле, если больше-меньше, то возвращаем результат, если равны - переходим к следующему, итд.
Тогда можно было бы простым образом решать классическую задачу "при группировке выбрать значения некоторых полей в тех записях, где заданное поле имеет минимальное или максимальное значение". Типа например "состояние объекта на начало и конец дня" - группируем по дням и объектам, а аггрегацию делаем в виде MIN(DT,STATUS) и MAX(DT,STATUS).

[identity profile] nolar.livejournal.com 2009-07-09 05:55 am (UTC)(link)
least()? greatest()?
min(least()), max(greatest())?

[identity profile] metaclass.livejournal.com 2009-07-09 06:00 am (UTC)(link)
Это какой сервер такое поддерживает?

[identity profile] nolar.livejournal.com 2009-07-09 06:04 am (UTC)(link)
http://www.postgresql.org/docs/8.4/interactive/functions-conditional.html#AEN15291
http://dev.mysql.com/doc/refman/5.0/en/comparison-operators.html#function_least
Oracle тоже.

Мне вообще почему-то казалось что это ANSI SQL.

[identity profile] metaclass.livejournal.com 2009-07-09 06:13 am (UTC)(link)
Не, нестандартные. И по ходу, они как бы и реляционной модели противоречат. Мы можем сравнивать только одинаковые по смыслу величины, а если такие величины расположены не в отдельных записях, а в ряд полей внутри одной(где эту функцию и можно применить тогда), то это тонкое издевательство на нормальными формами :)

[identity profile] metaclass.livejournal.com 2009-07-09 06:02 am (UTC)(link)
Не-не, это не функция сравнения списка полей.
Я говорю про аггрегирующую функцию для туплов из полей. Т.е. сравниваются не поля из списка, а туплы составленные из полей в разных записях при аггрегации.

[identity profile] nolar.livejournal.com 2009-07-09 06:06 am (UTC)(link)
А, понял. В постгре такую фуункцию написать можно самому.
Вообще это типа лексографического сравнения. Хотя вот насчёт нескольких полей не уверен что умеют они так. Разве что конкатинировать.

[identity profile] metaclass.livejournal.com 2009-07-09 06:10 am (UTC)(link)
Кстати, лексикографическое сравнение, как обходной маневр - вполне себе идея, только нужно поля выровнять, чтобы в результатирующей строке всегда были на одинаковых позициях.

[identity profile] zamotivator.livejournal.com 2009-07-09 08:40 am (UTC)(link)
select top 1 A, B, C from TABLE order by A,B,C;

[identity profile] metaclass.livejournal.com 2009-07-09 09:01 am (UTC)(link)
А GROUP BY сюда как прикрутить? Можно конечно сначала сгруппировать, а потом к каждой полученной записи сделать такой подзапрос с условием, но если по полям из условия нету индекса - это будет очевидный капец.

[identity profile] zamotivator.livejournal.com 2009-07-09 09:59 am (UTC)(link)
С group by да, не получиться.

[identity profile] gds.livejournal.com 2009-07-09 10:53 am (UTC)(link)
оракловские аналитические функции это умеют, как и многое другое.
нестандарт, разумеется.
склеивать в строку -- тоже вариант, но некоторые значения слишком велики могут быть (строки, например).

[identity profile] metaclass.livejournal.com 2009-07-09 11:55 am (UTC)(link)
Ага, ими, родимыми, и навеяно. Но думаю, там реализация более нечеловеческая, т.к. более универсальная.

[identity profile] ennor.livejournal.com 2009-07-09 05:55 pm (UTC)(link)
"состояние объекта на начало и конец дня", гы-гы (MSSQL 2005, для простоты - на начало и конец смены, т.к. неохота возиться с округлением дат до суток):
declare @log table (
	Id int identity(1,1) primary key,
	ShiftId int not null,
	State1 int not null,
	State2 int null
);

-- Completely random data, just as a sample
insert into @log (ShiftId, State1, State2)
select top 300 low as [ShiftId],
	cast(substring(cast(newid() as binary(16)), 1, 3) as int) as [State1], case
		when (number % 5) % 3 = 0 then null
		else cast(substring(cast(newid() as binary(16)), 1, 3) as int)
	end as [State2]
from master.dbo.spt_values where type = 'P'
order by newid();

-- Now get start and end conditions for each shift
select Id, ShiftId, State1, State2, case RN when 1 then 'Start' else 'End' end as [StateTime]
from (
	select *,
		row_number() over(partition by shiftid order by id) as [rn],
		count(*) over(partition by Shiftid) as [ShiftSize]
	from @log
	) sq
where rn in (1, ShiftSize)
order by ShiftId, rn;

:)))

Если кроме шуток, то возможно почти все, но в каждом случае будет очень сильно зависеть от конкретной СУБД.