У меня есть следующая таблица базы данных с информацией о людях, болезнях и наркотиках:
PERSON_T DISEASE_T DRUG_T
========= ========== ========
PERSON_ID DISEASE_ID DRUG_ID
GENDER PERSON_ID PERSON_ID
NAME DISEASE_START_DATE DRUG_START_DATE
DISEASE_END_DATE DRUG_END_DATE
От этих таблиц я выполняю некоторую статистику, о которой люди взяли, какие наркотики и имел который болезни. От этого я могу выяснить, какие шаблоны интересны, чтобы я копался далее в. Например, ниже упрощенный пример булева шаблона, который я мог бы найти для болезни 52:
( (Drug 234 = false AND Drug 474 = true AND Drug 26 = false) OR
(Drug 395 = false AND Drug 791 = false AND Drug 371 = true) )
Править: Вот другой пример:
( (Drug 234 = true AND Drug 474 = true AND Drug 26 = false) OR
(Drug 395 = false AND Drug 791 = false AND Drug 371 = true) )
Теперь я хочу преобразовать этот шаблон в запрос SQL и найти всех людей, которые соответствуют этому шаблону.
Например, я хочу найти всех людей в PERSON_T, у которых была болезнь, и ((кто не принимал наркотика 234 и 26 прежде, чем показать признаки, но действительно принимал наркотик 474 прежде, чем показать признаки), или (кто принял наркотик 371 прежде, чем показать признаки, но не препарат 791 и 395 прежде, чем показать признаки)),
Как я пошел бы о переводе этого шаблона назад в исходный запрос?
Вот моя первая попытка, но я застреваю на первом сроке:
SELECT * FROM PERSON_T, DRUG_T, DISEASE_T
WHERE DISEASE_ID = 52 AND
PERSON_T.PERSON_ID = DISEASE_T.PERSON_ID AND
PERSON_T.PERSON_ID = DRUG_T.PERSON_ID AND
(DRUG_T.DRUG_ID=234 AND (DRUG_T.DRUG_START_DATE>DISEASE_T.END_DATE || ???)
Мне нужно это для работы в PostgreSql, но я предполагаю, что любой данный ответ может быть переведен от данной базы данных до PostgreSql.
Ответ на комментарии
Редактирование я добавил свой собственный ответ. Кто-либо может придумать более простой ответ?
На мой взгляд, прямолинейное (хотя и уродливое) решение - использовать предложения EXISTS и NOT EXISTS:
SELECT *
FROM PERSON_T INNER JOIN DISEASE_T
USING (PERSON_ID)
WHERE DISEASE_ID = 52
AND EXISTS (SELECT 1 FROM DRUG_T
WHERE DRUG_T.PERSON_ID = PERSON_T.PERSON_ID
AND DRUG_ID = 474
AND [time condition])
AND NOT EXISTS (SELECT 1 FROM DRUG_T
WHERE DRUG_T.PERSON_ID = PERSON_T.PERSON_ID
AND DRUG_ID = 234
AND [time condition])
... и так далее. В примере мы спрашиваем людей, которые принимали препарат 474, но не 234. Очевидно, что вы можете группировать предложения с помощью AND и OR в зависимости от того, что вам нужно.
Примечание: я считаю, что все прописные буквы трудно читать. Я обычно использую верхний регистр для ключевых слов SQL и нижний регистр для имен таблиц и столбцов.
Простите за ошибки, но я думаю, что будет работать что-то вроде этого (на T-SQL):
SELECT col1, col2, col3...
FROM PERSON_T AS P, DRUG_T AS DR, DISEASE_T AS DI
WHERE disease_id = 52
AND P.person_id = DI.person_id
AND P.person_id = DR.person_id
AND drug_id NOT IN(234, 26)
AND drug_id = 474
AND disease_start_date < drug_start_date
UNION
SELECT col1, col2, col3...
FROM PERSON_T AS P, DRUG_T AS DR, DISEASE_T AS DI
WHERE disease_id = 52
AND P.person_id = DI.person_id
AND P.person_id = DR.person_id
AND drug_id NOT IN(791, 395)
AND drug_id = 371
AND disease_start_date < drug_start_date
Теперь это не обязательно должно быть сделано с помощью UNION, но для читабельности я подумал, что это самое простое, учитывая ваши условия. Возможно, это направит вас в правильном направлении.
SELECT per.person_id, per.name, per.gender
FROM person_t per
INNER JOIN disease_t dis
USING (person_id)
INNER JOIN drug_t drug
USING (person_id)
WHERE dis.disease_id = 52 AND drug.drug_start_date < dis.disease_start_date AND ((drug.drug_id IN (234, 474) AND drug.drug_id NOT IN (26)) OR (drug.drug_id IN (371) AND drug.drug_id NOT IN (395, 791)));
Это сделает то, о чем вы просите. Утверждения IN в конце довольно понятны.