Помогите с запросом...

Модераторы: kdv, CyberMax

Ответить
avenger
Сообщения: 141
Зарегистрирован: 25 окт 2005, 11:53

Помогите с запросом...

Сообщение avenger » 25 мар 2008, 23:11

Добрый вечер!

Пусть имеется данные

Код: Выделить всё

NUM USER_FK 
1      1
2      1
3      2
4      1
Необходимо найти, было ли, если вести обход в порядке возрастания номера, пересечение по пользователю. Т.е. одновременно с первым пользователем работал ли второй. Например: для пользователя #1 min(num) < num=3 < max(num) => c первым пользователем одновременно работал 2-й. В это же время для пользователя #2 есть пересечение с пользователем #1, т.к. документ с номером 3-и лежит между документами с номерами 2 и 4, которые принадлежат 1-му пользователю.

Другой пример:

Код: Выделить всё

NUM USER_FK 
1      1
2      1
3      1
4      2
5      2
6      2
7      3
Здесь у пользователей #1, #2 и #3 нет пересечений между друг другом.

Помогите мне написать процедуру, которая бы по ID пользователя возвращала бы true/false в зависимости от того, есть пересечение или нет. Можно просто идею.

За ранее спасибо. С уважением, Иван.

Tonal
Сообщения: 104
Зарегистрирован: 30 сен 2007, 13:42

Сообщение Tonal » 26 мар 2008, 08:25

Т.е. тебе нужно выяснить, встречаются ли для заданного числа такие, которые встречаются в последовательности и до его появления и после?
Вот код на python-е:

Код: Выделить всё

def cross_test(test_id, seq):
  before = set() #Множество встреченных перед предыдущим появлением test_id чисел
  before2 = set() #Множество встреченных чисел
  for it in seq:
    if it != test_id:
      if it in before: #it появлялся перед предыдущим появлением test_id
        return True
      before2.add(it)
    else:
      before.update(before2)
  return False
Ну а на PSQL думаю сам переведёшь, меня - ломает :-)

avenger
Сообщения: 141
Зарегистрирован: 25 окт 2005, 11:53

Сообщение avenger » 26 мар 2008, 08:37

Tonal писал(а):Т.е. тебе нужно выяснить
Мне надо вычислить - пересекаются диапазоны или нет. Т.е. для первого примера имеем 2-а диапазона:

Код: Выделить всё

UserFK    Min(num)    Max(num)
1            1                4
2            3                3
Видно, что диапазон для 1-го пользователя пересекается с диапазоном 2-го и наоборот.

Для 2-го примера

Код: Выделить всё

UserFK    Min(num)    Max(num)
1            1                3
2            4                6
3            7                7
Пересечений диапазонов нет.

Вот, навоял запрос. Помогите упростить:

Код: Выделить всё

SELECT T2.USERFK
FROM TABLE1 T1
JOIN TABLE1 T2 ON T2.USERFK <> T1.USERFK
WHERE T1.USERFK = :ID
GROUP BY 1, T2.NUM
HAVING (T2.NUM BETWEEN MIN(T1.NUM) AND MAX(T1.NUM))

UNION ALL

SELECT T2.USERFK
FROM TABLE1 T1
JOIN TABLE1 T2 ON T2.USERFK <> T1.USERFK
WHERE T1.USERFK = :ID
GROUP BY 1, T1.NUM
HAVING (T1.NUM BETWEEN MIN(T2.NUM) AND MAX(T2.NUM))

Кузнецов Евгений
Сообщения: 144
Зарегистрирован: 16 фев 2006, 22:36

Сообщение Кузнецов Евгений » 26 мар 2008, 11:24

Доброго времени суток!
avenger писал(а):Мне надо вычислить - пересекаются диапазоны или нет.
Условие пересечения будет таким:
(T1.Min_Num <= T2.Max_num ) and (T2.Min_num <= T1.Max_num)

avenger
Сообщения: 141
Зарегистрирован: 25 окт 2005, 11:53

Сообщение avenger » 26 мар 2008, 11:42

Кузнецов Евгений писал(а):(T1.Min_Num <= T2.Max_num ) and (T2.Min_num <= T1.Max_num)
То, что нужно.

Код: Выделить всё

select t2.userfk
from table1 t1
join table1 t2 on t2.userfk <> t1.userfk
where t1.userfk = :id
group by 1
having ( (min(t1.num) <= max(t2.num)) and (min(t2.num) <= max(t1.num)) )
А другой способ есть решить эту задачу?

Tonal
Сообщения: 104
Зарегистрирован: 30 сен 2007, 13:42

Сообщение Tonal » 26 мар 2008, 11:49

Если для 2х указанных пользователей, нужно узнать пересекаются их "диапазоны" или нет - смотри ответ Кузнецов Евгений.
Если нужно для одного узнать пересекался ли он с кем-нибудь, то алгоритм я привёл. :-)
А если переводить на SQL - то надо выбрать множество всех пользователей в выборке, и для каждого из них проверить пересекается ли он с заданным.
Код вроде довольно простой, если использовать derived tables и подзапросы. :-)

Хотя тут и having-ом вместо derived tables обойтись можно. :-)

avenger
Сообщения: 141
Зарегистрирован: 25 окт 2005, 11:53

Сообщение avenger » 26 мар 2008, 12:05

Tonal писал(а):Если нужно для одного узнать пересекался ли он с кем-нибудь, то алгоритм я привёл. :-)
Запрос, приведенный мной постом выше, с учетом поправки Кузнецова Евгения, возвращает для заданного пользователя, с кем он пересекается. А не для двух заданных пользователей проверяет условие.

Tonal
Сообщения: 104
Зарегистрирован: 30 сен 2007, 13:42

Сообщение Tonal » 26 мар 2008, 12:47

Вот мой способ:

Код: Выделить всё

Макросы:
#GET_MIN(USER) -> (select MIN(NUM) from TBL where USER_FK = USER)
#GET_MAX(USER) -> (select MAX(NUM) from TBL where USER_FK = USER)
Запрос:
select exists(
  select T1.USER_FK from TBL T1
  where T1.USER_FK != :USER
  group by T1.USER_FK
  having
    #GET_MIN(T1.USER_FK) < #GET_MAX(:USER) and #GET_MAX(T1.USER_FK) > #GET_MIN(:USER)
) from rdb$database
Здесь нужно в запросе заменить соответсвующие макросы на их определение.
Вроде должно быть побыстрее твоего? :-)

Tonal
Сообщения: 104
Зарегистрирован: 30 сен 2007, 13:42

Сообщение Tonal » 26 мар 2008, 12:48

Да, если нужно получить всех с кем пересекается - то откусываешь внешний exists. :-)

WildSery
Заслуженный разработчик
Сообщения: 1738
Зарегистрирован: 05 июн 2006, 16:19

Сообщение WildSery » 26 мар 2008, 13:56

Tonal писал(а): - то откусываешь внешний exists. :-)
... и добавляешь distinct. Или группировку. Хотя что это я? Ведь получится опять having :roll:

Ответить