C++Builder
  Начало   Форум   Помощь Войти Регистрация  
Страниц: [1] 2   Вниз
  Печать  
Автор Тема: Помощь с запросом  (Прочитано 7781 раз)
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Помощь с запросом
« : 16 Май 2015, 10:53:20 »

Подскажите, пожалуйста, как исправить SQL запрос, чтобы получить данные указанные на прилагаемой картинке.
Если убрать AND organizations_rooms.enddata IS NULL, то все равно получаю не то, что надо. Нужно как то сделать соединение...
Спасибо.

ссылка на картинку
Записан
volvo877
Глобальный модератор


Сказали спасибо: +736/-1
Offline

Сообщений: 891


Re: Помощь с запросом
« Ответ #1 : 17 Май 2015, 04:25:24 »

Цитировать
чтобы получить данные указанные на прилагаемой картинке
С чего это ты их должна получить? У тебя "Комната видеонаблюдения" есть только в ООО Салют, почему в результате должно выдаваться пустое поле orgname? Я бы еще понял, если бы речь шла про Туалет, он есть для двух организаций, вместо того, чтобы дублировать его дважды - может быть и возможно сделать пустое поле "orgname". Но по какой причине запрос должен вернуть те данные, которые ты привела - непонятно совершенно.
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #2 : 17 Май 2015, 08:40:19 »

Цитировать
С чего это ты их должна получить?
Есть выпадающий список, в котором пользователь выбирает организацию.
Помимо этого пользователь видит список всех помещений.
Напротив помещений пользователь может ставить галочки.
Установка галочки записывает связь в таблицу, что это помещение принадлежит этой организации. Если пользователь снимает галочку, то в поле enddata записывается время окончания эксплуатации этого помещения. Таким образом, это помещение становиться свободным.
Допустим, пользователь выбрал организацию №1.
Изначально есть, к примеру, три помещения
A
B
C
Пользователь поставил галочки у A и B.
Теперь пользователь выбрал организацию №2.
Мне надо чтобы пользователь видел, что помещения A и B заняты организацией №1. Т.е. мне надо вывести напротив A и B название организации №1. И напротив С вывести пусто. Чтобы видна была общая картина.
Помимо всего, надо учесть, что если в помещении B, к примеру, сняли галочку, то оно свободно снова для всех организаций.
Интерфейс с галочками для наглядности показан на картинке
картинка

Вроде как для получения нужного мне результата нужен LEFT JOIN к таблице organizations. Не получается оформить правильно.
Записан
volvo877
Глобальный модератор


Сказали спасибо: +736/-1
Offline

Сообщений: 891


Re: Помощь с запросом
« Ответ #3 : 17 Май 2015, 12:45:10 »

Лена, у тебя же PostgreSQL? Могу вот такой вариант предложить:
Код
SELECT rooms.id, rooms.name AS roomname, rooms.checkbool,
 CASE
   WHEN organizations_rooms.enddata IS NULL
 THEN organizations.name
ELSE
 ''
 END AS orgname
 
FROM rooms INNER JOIN (
 organizations INNER JOIN organizations_rooms
   ON organizations.id = organizations_rooms.id_organizations
) ON rooms.id = organizations_rooms.id_rooms
ORDER BY rooms.name;
, результат будет вот таким, как в аттаче:
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #4 : 17 Май 2015, 14:15:10 »

Огромное спасибо!
Теперь хоть видно пустые значения в колонке orgname. Я никак этого добиться не могла.
Остался такой вопрос, можно ли как-то оптимизировать вопрос, чтобы туалет не дублировался?
Туалет в свое время принадлежал ООО Салют. Это видно из картинки в первом посте. Эта информация пригодиться для архивного отчета в будущем.
На данный момент туалет принадлежит ООО Вымпел и именно эта одна запись нужна в гриде.
Возможно ли такое?
« Последнее редактирование: 17 Май 2015, 14:20:16 от SilverFish » Записан
volvo877
Глобальный модератор


Сказали спасибо: +736/-1
Offline

Сообщений: 891


Re: Помощь с запросом
« Ответ #5 : 17 Май 2015, 15:00:04 »

Ты сама же себе противоречишь. Смотри. Если Туалет раньше принадлежал ООО Салют, теперь освободился - его не надо показывать. А вот "Комнату видеонаблюдений", которая раньше принадлежала тому же ООО Салют, и теперь ТОЧНО ТАК ЖЕ освободилась - надо? По какому принципу отбирать, что надо, а что - нет? Логика какая?
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #6 : 17 Май 2015, 16:23:19 »

Цитировать
Ты сама же себе противоречишь.

Что-то путано объясняю...
Мне надо чтобы показывались все помещения, но чтобы в выборке не было дублей. Ведь туалет один, а пользователь увидет два.
Мне надо чтобы пользователь видел самую свежую текущую информацию.
Если помещение в данный момент занято, то показать организацию которая заняла. Если помещение свободно, вместо имени организации в колонке пишем пусто.
Твой запрос полностью воспроизводит картинку из моего первого поста. Все верно, но в выборку попал туалет с архивной записью это лишний дубль. Сейчас мы видим, что туалет принадлежит ООО Вымпел с 18:14:58 В данный момент это самая актуальная информация по туалету. Только эта запись должна появиться для туалета. т.е. если как-то учесть это и отобразить по туалету его нынешнее состояние, мы получим картинку с первого поста что и требуется.

Посмотрим на таблицу organizations_rooms. Строка с номером 36 , говорит, что туалет не закреплен за организацией и если бы не было записи номер 38, мы бы писали в колонке организаций для туалета пустоту. Однако запись номер 38 говорит, что организация c номером 15 захватила туалет.
И автоматически запись номер 36 превратилась в устаревшую архивную. Запись номер 38 актуальное состояние туалета. Эта запись и есть самой актуальной, и только она должна попасть в выборку. Но если бы не было записи 38, актуальной была бы запись номер 36, говорящая, что у туалета нет организации. Однако появившийся №38 перекрыл №36.
« Последнее редактирование: 17 Май 2015, 16:29:22 от SilverFish » Записан
volvo877
Глобальный модератор


Сказали спасибо: +736/-1
Offline

Сообщений: 891


Re: Помощь с запросом
« Ответ #7 : 17 Май 2015, 20:38:09 »

Ну, смотри, ты сама этого хотела

Код
SELECT rooms.id, rooms.name AS roomname, rooms.checkbool,
 CASE
   WHEN organizations_rooms.enddata IS NULL
 THEN organizations.name
ELSE
 ''
 END AS orgname
 
FROM rooms INNER JOIN (
 organizations INNER JOIN organizations_rooms
   ON organizations.id = organizations_rooms.id_organizations
) ON rooms.id = organizations_rooms.id_rooms
WHERE 1 = (SELECT COUNT(id_rooms) FROM organizations_rooms WHERE id_rooms = rooms.id) OR organizations_rooms.enddata IS NULL
 
ORDER BY rooms.name;

Оно?
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #8 : 18 Май 2015, 10:20:20 »

М-да…
Первую часть запроса поняла. Вторая напоминает секретный код инопланетного корабля, готовящегося к захвату Земли.
Огромное спасибо.

Туалет не даст жить спокойно.
Я вчера заполняла базу новыми тестовыми данными. Экспериментировала. И при текущих новых тестовых данных, туалет снова показал свое истинное нутро. Этот туалет вводит в ступор. Надо же, как случайно я дала верное название строптивому помещению.
Вот, что обнаружилось при новых случайных тестовых данных:
Если в базу добавили помещение, и этому помещению не разу еще не была назначена организация, то соответственно нет связи в таблице organizations_rooms. Тем не мене туалет как помещение существует. Можно ли его как-то добавить в выборку, естественно с пустым именем организации. Тестовые данные с зависшим в воздухе туалетом на картинке.
Поскольку у нас CASE анализирует organizations_rooms.enddata, а для туалета пока нет такой записи то, наверное, то, что я хочу увидеть туалет без организации в выборке не возможно? Т.е. есть ли возможность изменить запрос, чтобы при любых раскладах в выборку попадали все имена помещений? Ведь помещения существуют. Одним уже назначены организации, другим не назначены еще не разу, третьим назначены, а потом сняты (enddata).
Прошу прощения, что сразу не описала все варианты, просто задача настолько для меня сложная, что я заметила проблему только при новых тестовых данных и использованием твоего сложного запроса.

Неугомонный туалет
Записан
volvo877
Глобальный модератор


Сказали спасибо: +736/-1
Offline

Сообщений: 891


Re: Помощь с запросом
« Ответ #9 : 18 Май 2015, 11:17:19 »

Цитата: SilverFish от 18 Май 2015, 10:20:20
М-да…
Первую часть запроса поняла. Вторая напоминает секретный код инопланетного корабля, готовящегося к захвату Земли.
Сейчас ты увидишь пульт управления этого корабля

Цитата: SilverFish от 18 Май 2015, 10:20:20
наверное, то, что я хочу увидеть туалет без организации в выборке не возможно?
Нет такого понятия "невозможно", есть "программист не умеет" UNION-ы еще никто не отменял, и если возможно получить отдельно все то, что уже было получено выше (по таблице organizations_rooms), и отдельно вытащить те rooms, которых нет в organizations_rooms - значит можно их объединить и получить нужное. Готова?

Код
SELECT id, roomname, checkbool, orgname FROM (
SELECT rooms.id AS ID, rooms.name AS roomname, rooms.checkbool AS checkbool,
 CASE
   WHEN organizations_rooms.enddata IS NULL
 THEN organizations.name
ELSE
 ''
 END AS orgname
 
FROM rooms INNER JOIN (
 organizations INNER JOIN organizations_rooms
   ON organizations.id = organizations_rooms.id_organizations
) ON rooms.id = organizations_rooms.id_rooms
WHERE 1 = (SELECT COUNT(id_rooms) FROM organizations_rooms WHERE id_rooms = rooms.id) OR organizations_rooms.enddata IS NULL
 
UNION
 
SELECT rooms.id AS ID, rooms.name AS roomname, rooms.checkbool AS checkbool, '' AS orgname
FROM rooms
LEFT JOIN organizations_rooms ON (organizations_rooms.id_rooms = rooms.id)
WHERE organizations_rooms.id_rooms IS NULL
) AS subselect
 
ORDER BY roomname;
Проверил на таблицах:
Код
INSERT INTO rooms VALUES
(12, 'вестибюль на первом этаже', ' ', ' ', ' ', FALSE),
(13, 'склад на втором этаже', ' ', ' ', ' ', FALSE),
(14, 'подвал', ' ', ' ', ' ', FALSE),
(15, 'комната видео-наблюдения', ' ', ' ', ' ', FALSE),
(16, 'туалет', ' ', ' ', ' ', FALSE),
(17, 'секретная комната 1', ' ', ' ', ' ', FALSE), -- вот этой комнаты нет в organizations_rooms
(18, 'очень секретная комната 2', ' ', ' ', ' ', FALSE); -- этой тоже
 
INSERT INTO organizations_rooms VALUES
(33, 14, 12, '2015-05-12', NULL),
(34, 14, 14, '2015-05-12', NULL),
(35, 15, 13, '2015-05-12', NULL),
(36, 16, 16, '2015-05-12', '2015-05-12'),
(37, 16, 15, '2015-05-12', '2015-05-12'),
(38, 15, 16, '2015-05-12', NULL);
, получил правильный результат: все то, что выдавал предыдущий запрос, и эти 2 секретные комнаты вместе. Упорядочено, как положено... Тестировал на sqlfiddle.com, если что...
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #10 : 18 Май 2015, 12:01:27 »

Круто! Работает как часы! Спасибо.
Сейчас попробую еще каких-то новых тестовых данных, чтобы убедиться, что туалет побежден!  

P.S.
Оставшуюся часть дня посвящу осознанию, что-же значит код этого сложного запроса.

P.P.S.
Если потребовался такой сложный запрос, можно ли сказать, что структура базы не совсем верно придумана?
До этого момента все было удобно, сама писала свои простецкие выборки, вставки и вдруг потребовался сигнал из космоса.
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #11 : 18 Май 2015, 12:58:15 »

А еще такой вопрос...
Я раньше делала такую выборку:
SELECT rooms.id, rooms.name, rooms.checkbool FROM rooms ORDER BY name;

Поле checkbool было удобно просто показывать в ClientDataSet. Если пользователь ставит галочку записываю связь в таблицу organizations_rooms если убирает галочку записываю время в organizations_rooms.enddata. При открытии ClientDataSet отдельным запросом смотрю связи и предварительно в интерфейсе ClientDataSet проставляю галочки  в колонке checkbool на против записей где есть связи.
После замены на новый запрос, не удается работать с этим полем checkbool
Код
UniMainModule()->ClientDataSetOnlyRooms->First();
  while(!UniMainModule()->ClientDataSetOnlyRooms->Eof)
  {
//предварительный сброс флажков
UniMainModule()->ClientDataSetOnlyRooms->Edit();
UniMainModule()->ClientDataSetOnlyRooms->FieldByName(L"checkbool")->AsBoolean = false;//ошибка
UniMainModule()->ClientDataSetOnlyRooms->Post();
UniMainModule()->ClientDataSetOnlyRooms->Next();
  }
//***
 

raised exception class EDatabaseError with message 'Field 'checkbool' cannot be modified'.

С чем это может быть связано?
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #12 : 18 Май 2015, 13:52:17 »

Вроде получилось убрать ошибку...
У компонента FDQuery (FireDAC) снять галочку UpdateOption->CheckReadOnly=false и у этого поля в ClientDataSet тоже снять галку ReadOnly...
Почему-то раньше этого не требовалось. Странно.
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #13 : 18 Май 2015, 14:34:43 »

Экспериментировала с туалетом и он снова пропал.

Свободный туалет исчез

« Последнее редактирование: 18 Май 2015, 15:12:20 от SilverFish » Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #14 : 18 Май 2015, 15:22:02 »

Чего то в запросе не хватает. Тут пропали два свободных помещения:
Комната видео наблюдения
Туалет (куда же без него)
картинка
Записан
volvo877
Глобальный модератор


Сказали спасибо: +736/-1
Offline

Сообщений: 891


Re: Помощь с запросом
« Ответ #15 : 18 Май 2015, 17:04:58 »

Тут как раз все понятно, я рассчитывал, что если одинаковых кодов комнат в таблице organizations_rooms будет больше одного, то хотя бы у одного будет дата = NULL, это исправляется, не очень сложно, надо просто вот в этой части:
Код
WHERE 1 = (SELECT COUNT(id_rooms) FROM organizations_rooms WHERE id_rooms = rooms.id) OR organizations_rooms.enddata IS NULL
чуть-чуть поменять запрос. Логика такая: надо проверить два условия:
1) если количество id_rooms, у которых enddata is NULL равно 1, значит, комната занята
2) если первое условие не выполнилось - то проверяем второе: количество id_rooms, у которых enddata is NOT null должно быть больше 0, то есть, это будет комната, которая в данный момент свободна (потому что занятые комнаты проверяются первым условием).

Итак:

Код
SELECT id, roomname, checkbool, orgname FROM (
SELECT rooms.id AS ID, rooms.name AS roomname, rooms.checkbool AS checkbool,
 CASE
   WHEN organizations_rooms.enddata IS NULL
 THEN organizations.name
ELSE
 ''
 END AS orgname
 
FROM rooms INNER JOIN (
 organizations INNER JOIN organizations_rooms
   ON organizations.id = organizations_rooms.id_organizations
) ON rooms.id = organizations_rooms.id_rooms
WHERE
 -- первое условие - занятая комната
 1 = (SELECT COUNT(id_rooms) FROM organizations_rooms WHERE id_rooms = rooms.id AND organizations_rooms.enddata IS NULL) OR
 -- если не сработало - проверяем второе условие, свободная комната
 0 (SELECT COUNT(id_rooms) FROM organizations_rooms WHERE id_rooms = rooms.id AND organizations_rooms.enddata IS NOT NULL)
 
UNION -- -- и добавляем те комнаты, которых вообще еще нет в этой таблице
 
SELECT rooms.id AS ID, rooms.name AS roomname, rooms.checkbool AS checkbool, '' AS orgname
FROM rooms
LEFT JOIN organizations_rooms ON (organizations_rooms.id_rooms = rooms.id)
WHERE organizations_rooms.id_rooms IS NULL
) AS subselect
 
ORDER BY roomname;

Для тестов

Что не так - говори
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #16 : 18 Май 2015, 17:14:53 »

Цитировать
Что не так - говори

Теперь вестибюль перехватил эстафету у туалета.
Он почему-то два раза отобразился в итоговой выборке.

вестибюль

P.S.
Большое спасибо за комментарии в коде запроса. 
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #17 : 18 Май 2015, 17:27:00 »

Вот картинка интерфейса с неправильным вестибюлем.
Сереньким цветом показываю, что это занято другой организацией. Это визуально получается удобно.
Пользователь может ставить/снимать галочки там где серого цвета нет.
Если он пытается поставить галочку где серый цвет, я выдаю сообщения, что он должен снять эту галочку предварительно выбрав ту организацию к которой это помещение было закреплено ранее.
Такая цветовая визуализация, как мне кажется, удобна. Только вот вестибюль теперь вылез два раза...

А таблица связи такая:
CREATE TABLE organizations_rooms
(
  id serial NOT NULL,
  id_organizations integer,
  id_rooms integer,
  "start" timestamp without time zone,
  enddata timestamp without time zone,
  CONSTRAINT pk_id_org_rooms PRIMARY KEY (id),
  CONSTRAINT fk_id_organizations FOREIGN KEY (id_organizations)
      REFERENCES organizations (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE SET NULL,
  CONSTRAINT fk_id_rooms_organizations FOREIGN KEY (id_rooms)
      REFERENCES rooms (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE SET NULL
)
« Последнее редактирование: 18 Май 2015, 17:41:59 от SilverFish » Записан
volvo877
Глобальный модератор


Сказали спасибо: +736/-1
Offline

Сообщений: 891


Re: Помощь с запросом
« Ответ #18 : 18 Май 2015, 22:27:42 »

Лена, смотри. Я чуть-чуть переделал запрос, и прокомментировал его:

Код
SELECT id, roomname, checkbool, orgname FROM (
 
 SELECT -- работаем с комнатами, присутствующими в organizations_rooms
   rooms.id AS ID,
rooms.name AS roomname,
rooms.checkbool AS checkbool,
-- хитрый шаг: в качестве orgname берем объединение всех
-- organizations.name, то есть, если хоть у одной из комнат
-- есть незакрытая дата - то уже будет ясно, кто ей владеет
string_agg(
 CASE
   -- если поле enddata не заполнено - то есть владелец
   WHEN organizations_rooms.enddata IS NULL THEN organizations.name
-- иначе (enddata заполнено) - пустая строка, комната свободна
ELSE ''
 END, '') AS orgname
 
 -- и теперь просто связываем все id с названиями через inner join
 FROM rooms INNER JOIN (
   organizations INNER JOIN organizations_rooms
 ON organizations.id = organizations_rooms.id_organizations
)
ON rooms.id = organizations_rooms.id_rooms
 GROUP BY roomname, rooms.ID, checkbool
 
 UNION -- добавляем те комнаты, которых вообще еще нет в таблице organizations_rooms
 
 SELECT
   rooms.id AS ID,
rooms.name AS roomname,
rooms.checkbool AS checkbool,
'' AS orgname
 FROM rooms
 LEFT JOIN organizations_rooms
   ON (organizations_rooms.id_rooms = rooms.id)
 WHERE organizations_rooms.id_rooms IS NULL
 
) AS subselect
ORDER BY roomname; -- и упорядочиваем по названию комнаты
, проверил вот на такой таблице:
Код
INSERT INTO organizations_rooms VALUES
(33, 14, 12, '2015-05-12', '2015-05-15'),
(34, 14, 14, '2015-05-12', NULL),
(35, 15, 13, '2015-05-12', NULL),
(36, 16, 16, '2015-05-12', '2015-05-12'),
(37, 16, 15, '2015-05-12', '2015-05-12'),
(38, 15, 16, '2015-05-12', '2015-05-12'),
(39, 14, 16, '2015-05-12', '2015-05-12'),
(40, 14, 12, '2015-05-12', NULL);
, как видишь, тут есть все возможные комбинации: одна запись с комнатой organizations_rooms, и enddata is NULL, одна запись и enddata is NOT null, несколько записей, и у всех enddata id NOT null, несколько записей, и НЕ у всех enddata is NOT null... Для всех выдает правильный ответ. Не знаю, как ты находишь эти несоответствия... Насколько я понимаю, нескольких комнат с enddata IS NULL в этой таблице быть не может.

Мне кажется, теперь запрос стал попроще, и его будет несложно подпилить, если что опять не так (хотя я надеюсь, что все и так нормально)
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #19 : 19 Май 2015, 09:41:10 »

Большое спасибо!
Как я могу правильно заменить string_agg? У меня PostgreSQL 8.3 пишет что нет такой функции. Видимо она появилась в новых версиях.
Я попробовала заменить 
Код
string_agg(
 CASE
   -- если поле enddata не заполнено - то есть владелец
   WHEN organizations_rooms.enddata IS NULL THEN organizations.name
-- иначе (enddata заполнено) - пустая строка, комната свободна
ELSE ''
 END, '') AS orgname

на
Код
  CASE 
   WHEN organizations_rooms.enddata IS NULL
 THEN organizations.name
ELSE
 ''
 END AS orgname
 
 

ERROR:  column "organizations_rooms.enddata" must appear in the GROUP BY clause or be used in an aggregate function
ERROR:  column "organizations.name" must appear in the GROUP BY clause or be used in an aggregate function

Чтобы избавиться от ERRORS
Добавила organizations_rooms.enddata, organizations.name в GROUP BY

GROUP BY roomname, rooms.ID, checkbool, organizations_rooms.enddata, organizations.name

Это не правильно так как получила два вестибюля.

Картинка

Спасибо за комментарии, очень полезно для меня. В pgAdmin я их убрала т.к. он не переваривает большое кол-во комментариев.
Вот как я пытаюсь без string_agg но тогда теряю твой хитрый шаг:
Код
SELECT id, roomname, checkbool, orgname FROM (
 
 SELECT
   rooms.id AS ID,
rooms.name AS roomname,
rooms.checkbool AS checkbool,
 
 
 CASE
   WHEN organizations_rooms.enddata IS NULL
 THEN organizations.name
ELSE
 ''
 END AS orgname
 
 
 FROM rooms INNER JOIN (
   organizations INNER JOIN organizations_rooms
 ON organizations.id = organizations_rooms.id_organizations
)
ON rooms.id = organizations_rooms.id_rooms
 GROUP BY roomname, rooms.ID, checkbool, organizations_rooms.enddata, organizations.name
 
 UNION
 
 SELECT
   rooms.id AS ID,
rooms.name AS roomname,
rooms.checkbool AS checkbool,
'' AS orgname
 FROM rooms
 LEFT JOIN organizations_rooms
   ON (organizations_rooms.id_rooms = rooms.id)
 WHERE organizations_rooms.id_rooms IS NULL
 
) AS subselect
ORDER BY roomname;
 
Записан
volvo877
Глобальный модератор


Сказали спасибо: +736/-1
Offline

Сообщений: 891


Re: Помощь с запросом
« Ответ #20 : 19 Май 2015, 09:55:27 »

Цитировать
У меня PostgreSQL 8.3
Чего таким старьем пользуешься? Да, string_agg - новая функция, начиная с PostgreSQL 9.0. На 8.4 и до 9.0 можно было вместо нее использовать array_to_string(array_agg(тут тот самый CASE), ''), но в 8.3 и этого нет. Там вообще ничего нет, тебе придется писать свою агрегатную функцию через CREATE AGGREGATE. Видишь, насколько все усложняется только из-за того, что ты пользуешься устаревшим ПО?

И я не уверен, что с самописной функцией оно отработает так, как задумывалось, и так, как работает в 9.3 с использованием string_agg... Так что тут я ничем не могу помочь, проверять под 8.3 мне негде, на том сайте - только 9.3, себе ставить - неинтересно, тем более устаревшую версию.
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #21 : 19 Май 2015, 10:03:33 »

Цитировать
Чего таким старьем пользуешься?

Он стоял тут на компьютере. Там уже было до меня пару таблиц создано. Сказали доделать в этой версии.
Буду думать что придумать.
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #22 : 19 Май 2015, 10:42:08 »

Цитировать
тебе придется писать свою агрегатную функцию

Ход мысли правильный?
Код
CREATE FUNCTION stringcheck(organizations_rooms_enddata text, organizations_name text) RETURNS text AS $$   
BEGIN     IF organizations_rooms_enddata IS NULL RETURN organizations_name;
   ELSE       RETURN '';
   END IF;
 END;
$$ LANGUAGE plpgsql;
 
CREATE AGGREGATE mytest(text, text)
(
SFUNC=stringcheck,
STYPE=text
);
 
 
--Потом как-то так:
SELECT id, roomname, checkbool, orgname FROM (
 
 SELECT
   rooms.id AS ID,
rooms.name AS roomname,
rooms.checkbool AS checkbool,
 
       mytest(organizations_rooms.enddata, organizations.name) AS orgname
 
Записан
volvo877
Глобальный модератор


Сказали спасибо: +736/-1
Offline

Сообщений: 891


Re: Помощь с запросом
« Ответ #23 : 19 Май 2015, 12:13:51 »

Код
  CREATE AGGREGATE textcat_all(
     basetype    = text,
     sfunc       = textcat,
     stype       = text,
     initcond    = ''
 );
 
SELECT id, roomname, checkbool, orgname FROM (
 
 SELECT -- работаем с комнатами, присутствующими в organizations_rooms
   rooms.id AS ID,
rooms.name AS roomname,
rooms.checkbool AS checkbool,
-- хитрый шаг: в качестве orgname берем объединение всех
-- organizations.name, то есть, если хоть у одной из комнат
-- есть незакрытая дата - то уже будет ясно, кто ей владеет
textcat_all(
 CASE
   -- если поле enddata не заполнено - то есть владелец
   WHEN organizations_rooms.enddata IS NULL THEN organizations.name
-- иначе (enddata заполнено) - пустая строка, комната свободна
ELSE ''
 END || '') AS orgname
 
 -- и теперь просто связываем все id с названиями через inner join
 FROM rooms INNER JOIN (
   organizations INNER JOIN organizations_rooms
 ON organizations.id = organizations_rooms.id_organizations
)
ON rooms.id = organizations_rooms.id_rooms
 GROUP BY roomname, rooms.ID, checkbool
 
 UNION -- добавляем те комнаты, которых вообще еще нет в таблице organizations_rooms
 
 SELECT
   rooms.id AS ID,
rooms.name AS roomname,
rooms.checkbool AS checkbool,
'' AS orgname
 FROM rooms
 LEFT JOIN organizations_rooms
   ON (organizations_rooms.id_rooms = rooms.id)
 WHERE organizations_rooms.id_rooms IS NULL
 
) AS subselect
 
ORDER BY roomname;
На сайте отработало. Но опять же, фактически-то там 9.3, будет ли работать с 8.3 - неизвестно.
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #24 : 19 Май 2015, 14:34:59 »

Запрос вернул полностью правильные данные! Второй вестибюль пропал!
Большое спасибо!
Продолжу эксперименты с новыми тестовыми данными...

P.S.
Никак не доходило, а где объявлена функция textcat и почему работает. Потом дошло что это внутренняя функция PostgreSQL.
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #25 : 19 Май 2015, 17:06:24 »

Не удалось при новых тестовых данных получить дубликаты.  
Все отлично работает!  

Еще пару вопросиков:
1. А куда попало объявление CREATE AGGREGATE textcat_all? В pgAdmine в дереве объектов есть раздел функции и там пусто. Или pgAdmin не отображает пользовательские агрегатные функции?

2. Второй вопрос связан с колонкой orgname. В ClientDataSet не работает сортировка по этой колонке. По другим колонкам roomname и checkbool все работает как надо.
При сортировке по orgname выскакивает ошибка:
First chance exception at $765DC42D. Exception class EDBClient with message 'Invalid field type.'. Process ProjectUni2.exe (1412)

Я думаю, что из-за того что колонка orgname формируется сложным образом ClientDataSet не в состоянии ее отсортировать.

Вся разработка идет с использованием компонентов uniGUI.
Сортировка осуществляется кодом из demo примеров компонентов uniGUI.
Переделано с паскаля:
Код
//В модуле данных h:
 
public:
void SortColumn(const String FieldName, bool Dir);
 
//cpp
void TUniMainModule::SortColumn(const String FieldName, bool Dir)
{
 if(Dir)
ClientDataSetOnlyRooms->IndexName = FieldName+"_index_asc";
 else
ClientDataSetOnlyRooms->IndexName = FieldName+"_index_des";
}
 
//---------------------------------------------------------------------
 
void __fastcall TUniMainModule::UniGUIMainModuleCreate(TObject *Sender)
{
 
 int i;
 String IndexnameAsc;
 String IndexnameDes;
 
 for(i = 0; i ClientDataSetOnlyRooms->FieldCount; i++)
 {
  IndexnameAsc = ClientDataSetOnlyRooms->Fields->Fields[i]->FieldName + "_index_asc";
  IndexnameDes = ClientDataSetOnlyRooms->Fields->Fields[i]->FieldName + "_index_des";
  TIndexOptions opts, opts1;
  opts1 ixDescending;
  ClientDataSetOnlyRooms->IndexDefs->Add(IndexnameAsc,
  ClientDataSetOnlyRooms->Fields->Fields[i]->FieldName, opts);
  ClientDataSetOnlyRooms->IndexDefs->Add(IndexnameDes,
  ClientDataSetOnlyRooms->Fields->Fields[i]->FieldName, opts1);
 }
 
}
 
//***
//Событие сортировки для грида
void __fastcall TUniFormRoomsToOrg::UniDBGridOnlyRoomsColumnSort(TUniDBGridColumn *Column,
 bool Direction)
{
 
 UniMainModule()->SortColumn(Column->FieldName, Direction);
 
}
 

Есть ли смысл что-то попробовать чтобы починить сортировку по этой колонке orgname?
Все колонки присутствую в ClientDataSet т.к. по колонке checkbool я использую событие OnChange.


« Последнее редактирование: 20 Май 2015, 19:20:50 от SilverFish » Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #26 : 19 Май 2015, 18:55:56 »

Вот еще такая просьба:
Помоги, пожалуйста модифицировать UNION чтобы при любых раскладах все комнаты попадали в выборку.
Выяснилась при тестировании такая вот проблема. Таблица organizations_rooms объявлена так:
CREATE TABLE organizations_rooms
(
  id serial NOT NULL,
  id_organizations integer,
  id_rooms integer,
  "start" timestamp without time zone,
  enddata timestamp without time zone,
  CONSTRAINT pk_id_org_rooms PRIMARY KEY (id),
  CONSTRAINT fk_id_organizations FOREIGN KEY (id_organizations)
      REFERENCES organizations (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE SET NULL,
  CONSTRAINT fk_id_rooms_organizations FOREIGN KEY (id_rooms)
      REFERENCES rooms (id) MATCH SIMPLE
      ON UPDATE NO ACTION ON DELETE SET NULL
)
Следовательно при удалении комнаты или организации в эту таблицу связи в колонки записывается NULL.
Таким образом в колонках id_organizations и id_rooms таблицы organizations_rooms могут быть NULL значения. Я удалила несколько организаций и потеряла подвал.
Суть происходящего на картинке.
null значения в id_organizations и id_rooms
Записан
volvo877
Глобальный модератор


Сказали спасибо: +736/-1
Offline

Сообщений: 891


Re: Помощь с запросом
« Ответ #27 : 20 Май 2015, 10:36:42 »

Лена, самый простой способ  - добавить еще один UNION:

Код
CREATE AGGREGATE textcat_all(
     basetype    = text,
     sfunc       = textcat,
     stype       = text,
     initcond    = ''
 );
 
SELECT id, roomname, checkbool, orgname FROM (
 
 SELECT -- работаем с комнатами, присутствующими в organizations_rooms
   rooms.id AS ID,
rooms.name AS roomname,
rooms.checkbool AS checkbool,
-- хитрый шаг: в качестве orgname берем объединение всех
-- organizations.name, то есть, если хоть у одной из комнат
-- есть незакрытая дата - то уже будет ясно, кто ей владеет
textcat_all(
 CASE
   -- если поле enddata не заполнено - то есть владелец
   WHEN organizations_rooms.enddata IS NULL
     THEN organizations.name
-- иначе (enddata заполнено) - пустая строка, комната свободна
ELSE ''
 END || '') AS orgname
 
 -- и теперь просто связываем все id с названиями через inner join
 FROM rooms INNER JOIN (
   organizations INNER JOIN organizations_rooms
 ON organizations.id = organizations_rooms.id_organizations
)
ON rooms.id = organizations_rooms.id_rooms
 GROUP BY roomname, rooms.ID, checkbool
 
 UNION -- добавляем те комнаты, которых вообще еще нет в таблице organizations_rooms
 
 SELECT
   rooms.id AS ID,
rooms.name AS roomname,
rooms.checkbool AS checkbool,
'' AS orgname
 FROM rooms
 LEFT JOIN organizations_rooms
   ON (organizations_rooms.id_rooms = rooms.id)
 WHERE organizations_rooms.id_rooms IS NULL
 
 UNION -- добавляем те комнаты, у которых в organizations_rooms поле id_organizations is NULL
 
 SELECT
   organizations_rooms.id_rooms AS ID,
   rooms.name AS roomname,
   rooms.checkbool AS checkbool,
   '' AS orgname
 FROM rooms
 INNER JOIN organizations_rooms
   ON rooms.id = organizations_rooms.id_rooms
 GROUP BY organizations_rooms.id_rooms, rooms.name, rooms.checkbool, organizations_rooms.id_organizations, organizations_rooms.enddata
 HAVING (((organizations_rooms.id_organizations) IS NULL) AND ((organizations_rooms.enddata) IS NULL))
 
) AS subselect
 
ORDER BY roomname;
Возможно, что-то получилось бы, если пошаманить с уже существующими двумя подзапросами, но в них опять что-нибудь сломается, опять переделывать придется. А так - они уже протестированы, работают, просто добавляем то, что они не покрывают...
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #28 : 20 Май 2015, 11:26:14 »

Огромное спасибо!
То, что id_rooms в таблице organizations_rooms может быть также NULL, я так понимаю, на правильность этого запроса не влияет?

P.S.
Просьба проверить почтовый ящик форума.
Записан
SilverFish
Участник


Сказали спасибо: +14/-6
Offline

Сообщений: 786


Re: Помощь с запросом
« Ответ #29 : 20 Май 2015, 12:10:41 »

Добавила организацию. Назначила ей помещения. Появились дубликаты. 
Какие-то упрямые таблицы.
Вот на картинке...
дубликаты
Записан
Страниц: [1] 2   Вверх
  Печать  
 
Перейти в:  




....

Powered by SMF 1.1.21 | SMF © 2025, Simple Machines
Страница сгенерирована за 0.136 секунд. Запросов: 21.