powered by simpleCommunicator - 2.0.61     © 2026 Programmizd 02
Целевая тема:
Создать новую тему:
Автор:
Закрыть
Цитировать
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Как правильно объединить запросы?
17 сообщений из 17, страница 1 из 1
Как правильно объединить запросы?
    #39144581
Фотография INOKENTiY
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Привет.
Есть три таблицы, user, conversation, message.
С таблицей user - все понятно, поля id, login, last_login_time (время последнего входа)

conversation, поля:
id,
user1_id,
user2_id,
message_last_read_id_user1 - id последнего прочитанного сообщения пользователем user1_id
message_last_write_id_user1 - id последнего отправленного сообщения пользователем user1_id
message_last_read_id_user2 - id последнего прочитанного сообщения пользователем user2_id
message_last_write_id_user2 - id последнего отправленного сообщения пользователем user2_id
...

message, поля:
id,
conversation_id,
poster_id, - id пользователя, который отправил данное сообщение
msg,
...

Цель выбрать пользователей, кто входил в течении последнего часа и выбрать для каждого из них количество непрочитанных сообщений:
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
 SELECT u.id, u.login, u.last_login_time, 
( 
  SELECT count ( * ) FROM conversation c, message m 
  WHERE c.user1_id = u.id AND m.conversation_id = c.id AND
        COALESCE(c.message_last_read_id_user1, 0) < COALESCE(message_last_write_id_user2, 0) AND 
        m.id > COALESCE(message_last_read_id_user1, 0) AND m.id <= COALESCE(message_last_write_id_user2, 0) AND 
        m.poster_id = c.user2_id
) AS unread_user1, 
( 
  SELECT count ( * ) FROM conversation c, message m 
  WHERE user2_id = u.id AND m.conversation_id = c.id AND 
        COALESCE(message_last_read_id_user2, 0) < COALESCE(message_last_write_id_user1, 0) AND 
	c.user1_id > 0 AND m.id > COALESCE(message_last_read_id_user2, 0) AND m.id <= COALESCE(message_last_write_id_user1, 0) AND 
        m.poster_id != c.user2_id 
) AS unread_user2
FROM user u WHERE u.last_login_time >= 1452504418;


Одно из unread_user1 или unread_user2 всегда будет 0.
План выполнения запроса здесь http://explain.depesz.com/s/VT6

Может у кого-то будут идеи как объединить эти два под-запроса в один?
...
Рейтинг: 0 / 0
Как правильно объединить запросы?
    #39144755
ilejn
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
INOKENTiY,
думайте в сторону
WHERE (c.user1_id = u.id OR c.user2_id = u.id) AND
...
Рейтинг: 0 / 0
Как правильно объединить запросы?
    #39144815
Фотография INOKENTiY
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
ilejn,

Пробовал так, но еще медленей получается.
Может индекс дополнительный сделать по пробовал по message_last_read_id_user1 и message_last_write_id_user2 создать и по message_last_read_id_user2 и message_last_write_id_user1, но результатов не дало
...
Рейтинг: 0 / 0
Как правильно объединить запросы?
    #39144894
big-trot
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Гость
Необходимо избавиться от подзапросов в выражении между SELECT и FROM (это всегда тормоза).
Рекомендуется попробовать использовать выражение SELECT c DISTINCT ON.
...
Рейтинг: 0 / 0
Как правильно объединить запросы?
    #39144922
Фотография INOKENTiY
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
big-trotНеобходимо избавиться от подзапросов в выражении между SELECT и FROM (это всегда тормоза).
Рекомендуется попробовать использовать выражение SELECT c DISTINCT ON.

пробовал вот так
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
SELECT u.id, u.login, u.last_login_time
     , COUNT(CASE WHEN COALESCE(c.message_last_read_id_user1, 0) 
                     < COALESCE(message_last_write_id_user2, 0) 
                   AND m.id > COALESCE(message_last_read_id_user1, 0) 
                   AND m.id <= COALESCE(message_last_write_id_user2, 0) 
                   AND m.poster_id = c.user2_id
                  THEN 1
             END) as unread_user1
     , COUNT(CASE WHEN COALESCE(message_last_read_id_user2, 0) 
                     < COALESCE(message_last_write_id_user1, 0) 
                   AND c.user1_id > 0 
                   AND m.id > COALESCE(message_last_read_id_user2, 0) 
                   AND m.id <= COALESCE(message_last_write_id_user1, 0) 
                   AND m.poster_id <> c.user2_id
                  THEN 1
             END) as unread_user2   
FROM user u 
JOIN conversation c
    ON c.user1_id = u.id
JOIN message m
    ON m.conversation_id = c.id
WHERE u.last_login_time >= 1452504418
GROUP BY u.id, u.login, u.last_login_time;


но так намного медленей
...
Рейтинг: 0 / 0
Как правильно объединить запросы?
    #39144923
Alexius
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
INOKENTiY,

explain analyze запросов приведите.
...
Рейтинг: 0 / 0
Как правильно объединить запросы?
    #39145207
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
INOKENTiYпробовал вот так
....

т.е. вот так :
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
SELECT u.id, u.login, u.last_login_time
	,unread_user1
	,unread_user2
FROM user u
/*JOIN*/ ,LATERAL
(SELECT 
	/*c.user1_id AS id 
	,*/COUNT(CASE WHEN COALESCE(c.message_last_read_id_user1, 0) 
					< COALESCE(message_last_write_id_user2, 0) 
				AND m.id > COALESCE(message_last_read_id_user1, 0) 
				AND m.id <= COALESCE(message_last_write_id_user2, 0) 
				AND m.poster_id = c.user2_id
				THEN 1
			END) as unread_user1
	,COUNT(CASE WHEN COALESCE(message_last_read_id_user2, 0) 
					< COALESCE(message_last_write_id_user1, 0) 
				AND c.user1_id > 0 
				AND m.id > COALESCE(message_last_read_id_user2, 0) 
				AND m.id <= COALESCE(message_last_write_id_user1, 0) 
				AND m.poster_id <> c.user2_id
				THEN 1
			END) as unread_user2   
FROM conversation c	
JOIN message m
	ON m.conversation_id = c.id
WHERE c.user1_id = u.id
GROUP BY c.user1_id
) FOO /*USING (id)*/
WHERE u.last_login_time >= 1452504418
;



начиная с 9.3
...
Рейтинг: 0 / 0
Как правильно объединить запросы?
    #39145454
Фотография INOKENTiY
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
qwwq,

вот блин у меня 9.1
...
Рейтинг: 0 / 0
Как правильно объединить запросы?
    #39145495
Фотография INOKENTiY
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
Alexiusexplain analyze
вот explain analyze первого запроса http://explain.depesz.com/s/rpBT
...
Рейтинг: 0 / 0
Как правильно объединить запросы?
    #39145496
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
INOKENTiYqwwq,

вот блин у меня 9.1

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

примерно вот туда:

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
SELECT id, login, last_login_time
	,unread_user12[1] AS unread_user1
	,unread_user12[2] AS unread_user2
FROM 
	(SELECT u.id, u.login, u.last_login_time
		,(SELECT ARRAY[
			COUNT(CASE WHEN COALESCE(c.message_last_read_id_user1, 0) 
						< COALESCE(message_last_write_id_user2, 0) 
					AND m.id > COALESCE(message_last_read_id_user1, 0) 
					AND m.id <= COALESCE(message_last_write_id_user2, 0) 
					AND m.poster_id = c.user2_id
					THEN 1
				END)::bigint
			,COUNT(CASE WHEN COALESCE(message_last_read_id_user2, 0) 
						< COALESCE(message_last_write_id_user1, 0) 
					AND c.user1_id > 0 
					AND m.id > COALESCE(message_last_read_id_user2, 0) 
					AND m.id <= COALESCE(message_last_write_id_user1, 0) 
					AND m.poster_id <> c.user2_id
					THEN 1
				END)::bigint]				
		FROM conversation c	
		JOIN message m
			ON m.conversation_id = c.id
		WHERE c.user1_id = u.id
		GROUP BY c.user1_id
		) as unread_user12
	FROM user u
	WHERE u.last_login_time >= 1452504418
	)
AS FOO
;


...
Рейтинг: 0 / 0
Как правильно объединить запросы?
    #39145809
Фотография INOKENTiY
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
qwwq,

Проблему при join'е таблицы message, она большая в ней 20млн записей.
Думаю если понять как этот запрос ускорить, то можно воспользоваться вашим решением
Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
SELECT u.id, tmp.* 
FROM user u,
(  SELECT c.user1_id, count(m.id)
   FROM conversation c 
   JOIN message m ON m.conversation_id = c.id
   GROUP BY c.user1_id
) tmp
WHERE 
 tmp.user1_id = u.id AND
 u.last_login_time >= 1452610663 - 3600;


Вот его план выполнения http://explain.depesz.com/s/2B0
Больше всего занимает выборка сообщений по ключу conversation_pkey id = m.conversation_id, наверное это уже никак не ускорить?
...
Рейтинг: 0 / 0
Как правильно объединить запросы?
    #39145832
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
INOKENTiY,


а скажите, любезный, сколько от числа всех user_id попадает в

Код: sql
1.
SELECT COUNT(1) FROM user u WHERE u.last_login_time >= 1452504418;



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

если от этого никуда не деться -- то делайте как вам нравится - кто ж вам запретит.

но и тогда мне кажется вам ы сначала проанализироовать ваши таблички -- там 10 кратное отличие прогноза от результата (11лямов лупов индекс сика даже по пк -- радуют).
...
Рейтинг: 0 / 0
Как правильно объединить запросы?
    #39145852
Фотография INOKENTiY
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
qwwq,

886 пользователей в среднем.

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


а можно это более популярно растолковать?

спасибо
...
Рейтинг: 0 / 0
Как правильно объединить запросы?
    #39145875
Фотография INOKENTiY
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
qwwq,

Код: sql
1.
2.
3.
4.
5.
6.
7.
8.
9.
explain analyze SELECT u.id, tmp.* 
FROM co_user u,
(SELECT c.user_id, count(m.record_id)
FROM co_consultation c 
JOIN co_record m
ON m.record_consultation_id = c.id
GROUP BY c.user_id
) tmp
WHERE tmp.user_id = u.id and u.last_login_time >= 1452617409 - 3600;



пробую так, но этот запрос вообще не желает выполнятся за обозримое время, прошло 3 минуты я остановил
...
Рейтинг: 0 / 0
Как правильно объединить запросы?
    #39145904
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
qwwqINOKENTiY,
а скажите, любезный, сколько от числа всех user_id попадает в

INOKENTiYqwwq,

886 пользователей в среднем.

и какая это часть "от числа всех" пользователей ?

INOKENTiYа можно это более популярно растолковать?
спасибо
-- что именно ?

я просто пытаюсь следить за вашей мыслью -- вы зачем то, в последнем вашем запросе, берёте полную свертку от произведения сообщений на conversation

я, абстрагируясь от вопроса, зачем оно вам надо, хочу понять, нельзя ли эффективно проредить вашу выборку-для-свёртки ещё до свёртки. (перейдя к корелятам)

но, видимо я зря абстрагируюсь -- вы просто гребёте стейтменты в кучку, и надо самому разбираться в постановке задачи, а не следовать за вашими попытками. Пока, скажу честно, мне лень разбираться с вашей структурой, и выяснять "кто на ком стоял " в вашем conversation.
...
Рейтинг: 0 / 0
Как правильно объединить запросы?
    #39145998
Фотография INOKENTiY
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
qwwq,

886 пользователей из 1200000.
Сообщений 20млн, а в conversation 6млн записей.

Не совсем понял ваши термины "свертка"

Цель подсчитать количество не прочитаных сообщений для пользователей, которые заходили не позже чем чам назад.

Для этого в conversation есть поля
message_last_read_id_user1 - id последнего прочитанного сообщения пользователем user1_id
message_last_write_id_user1 - id последнего отправленного сообщения пользователем user1_id
message_last_read_id_user2 - id последнего прочитанного сообщения пользователем user2_id
message_last_write_id_user2 - id последнего отправленного сообщения пользователем user2_id

Вот ними и можно проредить выборку произведения сообщений на conversation.

Вся проблема в том, что я не могу знать в каком поле user1_id или user2_id пользователь, который выбирается из таблицы user по условию last_login_time >= :timestamp_now - 3600. Потому в исходном запросе и есть два подзапроса.
К примеру, у меня есть id пользователя, я узнаю что его айди соответсвует user2_id, значит, мне нужно проверить или message_last_read_id_user2 < message_last_write_id_user1, или id прочитаного мной сообщения меньше id отправленного сообщения моим собеседником user1. Что бы посчитать эти непрочитаные сообщения нужно выбрать сообщени по условию message_last_read_id_user2 < id <= message_last_write_id_user1 и которые отправленны user_id
...
Рейтинг: 0 / 0
Как правильно объединить запросы?
    #39146066
qwwq
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Участник
INOKENTiYqwwq,

886 пользователей из 1200000.
Сообщений 20млн, а в conversation 6млн записей.

и зачем тогда в теме появились полные свертки всех сообщений по всем пользователям ?

вот кто вот это 18668735 и это 18669143 писал ?
Вася Пупкин ?

какого хера я должен про вот это вот 18668735 и это 18669143 вообще думать ?

если речь -- о 886 из 1200000 ?
о 886 из 1200000, Карл !

что вы можете, а что нет -- мне не интересно.
напишите правильный фомально запрос, мы начнем его формально преобразовывать. И всё.
человек усекающий множества через {coalesce(,) operator coalesce(,)} должен страдать.
мне за него думать лень.

и вообще тогда не понял , план какого именно запроса приведен в 18668735 .
если этого 18667107 -- то что там делает фулл-скан по message

а если вашего, простите, высера из 18668735 -- то зачем он мне ? зачем мне про него вообще думать ?
...
Рейтинг: 0 / 0
17 сообщений из 17, страница 1 из 1
Форумы / PostgreSQL [игнор отключен] [закрыт для гостей] / Как правильно объединить запросы?
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]