LINUX.ORG.RU

Нужна помощь по sqlite (выбор по максимальным значениям)

 


0

1

Нужен совет экспертов. В общем есть две таблицы

t1:
id, call
t2:
id, call_id, call_date, call_record_date

call_date может быть как несколько для одного call_id, так и ни одного.

Пытаюсь выбрать для каждого id из table1 только те записи из table2, где одновременно и call_date, и call_record_date максимальные.

Только для call_date мне все понятно

SELECT t1.id, t2.call_date, t2.call_record_date
FROM t1
LEFT JOIN t2 ON t1.id = t2.call_id
WHERE t2.call_id IS NULL OR t2.call_date = (SELECT MAX(t2.call_date) FROM t2 WHERE t2.call_id = t1.id)

Как сюда оптимально присобачить MAX(t2.call_record_date), чтобы сначала подбирал по максимальной call_date, а потом из полученных еще и выбирал максимальную call_record_date? Если что все даты в формате YYYY-MM-DD.

Такой запрос вроде все правильно возвращает, но меня не покидает ощущение, что как-то оно не правильно

SELECT t1.id, t2.call_date, t2.call_record_date
FROM t1
LEFT JOIN t2 ON t1.id = t2.call_id
WHERE t2.call_id IS NULL
OR (t2.call_date = (SELECT MAX(t2.call_date) FROM t2 WHERE t2.call_id = t1.id)
AND t2.call_record_date = (SELECT MAX(t2.call_record_date) FROM t2 WHERE t2.call_id = t1.id))

Ответ на: комментарий от Toxo2

Ваше решение более короткое и, как я понимаю, требует меньше ресурсов/времени, т. к. все происходит в одном селекте. Можете пояснить, почему в данном случае при JOIN вы берете сопоставление j2 к t1, а не наоборот?

curbar
() автор топика
Последнее исправление: curbar (всего исправлений: 1)
Ответ на: комментарий от curbar

Ваше решение более короткое

Это решение Eshkin_kot'а. Я просто впечатлился и запомнил такую чудесную особенность SQLite.

почему в данном случае при JOIN вы берете сопоставление j2 к t1

По условию вашей же задачи, как я её понял. Вам же нужны записи t1 у которых нет совсем никаких дат?

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

По условию вашей же задачи, как я её понял. Вам же нужны записи t1 у которых нет совсем никаких дат?

Да, все верно. Только я думал, что чтобы выбрать все t1.id, даже если их нет в t2, я должен t1 брать слева, а не справа. Исходил из объяснения отсюда

The expression A.f = B.f is a conditional expression. Besides the equality (=) operator, you can use other comparison operators such as greater than (>), less than (<), etc.

The statement returns a result set that includes:

Rows in table A (left table) that have corresponding rows in table B. Rows in the table A table and the rows in the table B filled with NULL values in case the row from table A does not have any corresponding rows in table B. In other words, all rows in table A are included in the result set whether there are matching rows in table B or not.

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

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

sqlite> select * from t2;
1|1||2025-01-16
2|1|2024-03-11|2025-01-16
3|1|2025-01-16|2025-01-15
4|1|2025-01-16|2025-01-16
5|2|2025-01-17|2025-01-18
6|2|2025-01-18|2025-01-17

sqlite> SELECT
  t1.id, j2.call_date, j2.call_record_date
FROM 
  t1
  LEFT JOIN (
    SELECT *, max(call_date), max(call_record_date) FROM t2 GROUP BY call_id
  ) j2 ON j2.call_id = t1.id
;
1||2025-01-16
2|2025-01-17|2025-01-18

Запрос автора

sqlite> SELECT t1.id, t2.call_date, t2.call_record_date
FROM t1
LEFT JOIN t2 ON t1.id = t2.call_id
WHERE t2.call_id IS NULL
OR (t2.call_date = (SELECT MAX(t2.call_date) FROM t2 WHERE t2.call_id = t1.id)
AND t2.call_record_date = (SELECT MAX(t2.call_record_date) FROM t2 WHERE t2.call_id = t1.id));
1|2025-01-16|2025-01-16
sovety_ot_sonika
()

одновременно и call_date, и call_record_date максимальные.

Бредовое условие. Оно может вернуть как ноль так и Н строк.

Нужно порядок сортировки упорядочивать в условии.

Сначала сортировать по первому полю, а потом по второму, например.

guyvernk
()
Последнее исправление: guyvernk (всего исправлений: 1)
Ответ на: комментарий от Psilocybe

Тут call_date и call_record_date могут принадлежать разным записям

Не, не могут точно. Это точно одна запись.

Но да, с двумя max'ами этот хак как-то не очевидно работает. Надо четко понимать, где первый max, где второй max.

Вот так:

SELECT
	t1.id, j2.call_date, j2.call_record_date
FROM 
	t1
	LEFT JOIN (
		SELECT *, max(call_date) FROM (
			SELECT *, max(call_record_date) AS call_record_date FROM t2 GROUP BY call_id, call_date
		) GROUP BY call_id
	) j2 ON j2.call_id = t1.id
;
вроде более понятно, что прошу сделать у SQLite.

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

У вас что-то не то выходит. Я протестировал свои таблицы на коде, предложенном @Toxo2, и судя по первому впечатлению, все работает на отлично. Выдает все id из таблицы t1, вместе с ними все ассоциированные с ними записи из t2 при условии, что последние должны отвечать одновременно двум условиям: 1) в первую очередь должна быть выбрана запись с максимальной call_date, 2) если их несколько, то среди них выбирается запись с максимальной call_record_date. Если же таких записей в t2 нет, то возвращаются NULL. У меня также работает код из первого поста.

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

этот хак как-то не очевидно работает

к слову - в Sybase примерно такой же хак работает еще более удивительным макаром.

два дня кушать не мог, когда увидел, что Sybase выводит на *, max(some_field).

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

где тут глупость? Пишешь запрос например подзапрос (select id from t2 where t2.call_id=t1.id order by call_date desc,call_record_date desc limit 1) в котором сортируются даты по убыванию сначала по одному столбцу потом по второму, а если дергать max то может оказаться что один max от одной строки, а второй max от другой

doomer
()

Примерно так переделал твой второй запрос

SELECT t1.id, t2.call_date, t2.call_record_date
FROM t1
LEFT JOIN t2 ON t1.id = t2.call_id
WHERE t2.call_id IS NULL
OR (t2.call_date = (SELECT t2.call_date FROM t2 WHERE t2.call_id = t1.id order by call_date desc,call_record_date desc limit 1)
AND t2.call_record_date = (SELECT t2.call_record_date FROM t2 WHERE t2.call_id = t1.id order by call_date desc,call_record_date desc limit 1)

doomer
()