2010-09-17 6 views
0

У меня есть следующие таблицы:Как бы написать этот SQL-запрос?

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 

Я хочу написать запрос, который принимает ввод идентификатора болезни и возвращает одну строку для каждого человека в базе данных с колонкой для пола, столбец для того, или нет, у них когда-либо было заболевание, и колонка для каждого препарата, которая указывает, принимали ли они препарат до заражения этой болезнью. И.Е. true будет означать drug_start_date < disease_start_date. False будет означать drug_start_date> disease_start_date или человек никогда не принимал этот конкретный препарат.

В настоящее время мы извлекаем все данные из базы данных и используем Java для создания 2D-массива со всеми этими значениями. Мы изучаем перемещение этой логики в базу данных. Можно ли создать запрос, который вернет результирующий набор, как я хочу, или мне нужно создать хранимую процедуру? Мы используем Postgres, но я предполагаю, что ответ SQL для другой базы данных легко перейдет в Postgres.

+0

Является ли это точным представлением проблемной области - то есть. вы заинтересованы в лечении пациентов/болезней/наркотиков, или это переработка совершенно другой темы с аналогичными отношениями? Если это первый, есть ли дополнительные таблицы для конкретных лекарств и болезней (в отличие от того, когда они были связаны с конкретным пациентом), и есть ли еще одна таблица, в которой можно было бы применить конкретный препарат к определенному заболеванию (возможно, с противопоказанием -инфекции - например, не лечить гипертензию таблеток с солью)? –

ответ

3

На основании информации, предоставленной:

SELECT p.name, 
      p.gender, 
      CASE WHEN d.disease_id IS NULL THEN 'N' ELSE 'Y' END AS had_disease, 
      dt.drug_id 
    FROM PERSON p 
LEFT JOIN DISEASE d ON d.person_id = p.person_id 
        AND d.disease_id = ? 
LEFT JOIN DRUG_T dt ON dt.person_id = p.person_id 
        AND dt.drug_start_date < d.disease_start_date 

..но там будет много строк, которые будут выглядеть дубликатом для drug_id столбца, за исключением.

+0

Не должно ли второе соединение быть внешним соединением для случая, когда человек никогда не принимал наркотик? –

+0

@Jay Askren: LEFT JOIN - это соединение OUTER, ключевое слово 'OUTER' является необязательным. –

+0

@OMG: LEFT JOIN вернет записи PERSON, которые не имеют подходящих лекарств любого рода - это не приведет к возврату записей PERSON, которые не имели * специфической * лекарственной терапии. (Это неизбежное условие со структурой, как указано.) –

1

Вы, по сути, хотите создать запрос cross-tab с наркотиками. Хотя есть много инструментов OLAP, которые могут делать такие вещи (среди всех видов других нарезки и обработки данных), делать что-то подобное в традиционном SQL нелегко (и вообще невозможно обойтись без какой-то процедурный синтаксис во всех, кроме простейших сценариях).

Вы в основном есть два варианта, когда делать это с помощью SQL (ну, точнее, у вас есть один варианта, и еще более сложный, но гибкий вариант, производные от него):

  1. Используйте серию CASE в вашем запросе для создания столбцов, которые являются репрезентативными для каждого отдельного препарата. Для этого требуется , зная список переменных значений (например, лекарств) досрочно
  2. Использовать процедурный язык SQL, такой как T-SQL, для динамического построения запроса, который использует операторы case, как описано выше, но наряду с получением этого список значений из самих данных.

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

Например, используя вариант 1:

select 
    p.NAME, 
    p.GENDER, 
    (case when d.DISEASE_ID is null then 0 else 1 end) as HAD_DISEASE, 
    (case when sum(case when dr.DRUG_ID = 1 then 1 else 0 end) > 0 then 1 else 0 end) as TOOK_DRUG_1, 
    (case when sum(case when dr.DRUG_ID = 2 then 1 else 0 end) > 0 then 1 else 0 end) as TOOK_DRUG_2, 
    (case when sum(case when dr.DRUG_ID = 3 then 1 else 0 end) > 0 then 1 else 0 end) as TOOK_DRUG_3 

from PERSON_T p 

left join DISEASE_T d on d.PERSON_ID = p.PERSON_ID and d.DISEASE_ID = @DiseaseId 
left join DRUG_T dr on dr.PERSON_ID = p.PERSON_ID and dr.DRUG_START_DATE < d.DISEASE_START_DATE 

group by p.PERSON_ID, p.NAME, p.GENDER, d.DISEASE_ID 

Как вы можете сказать, это становится немного трудоемким, как вы получите за пределами всего несколько потенциальных значений.

Другой вариант - построить этот запрос динамически.Я не знаю, PostgreSQL и что, если таковые имеются, процедурные возможностями она имеет, но в целом процедура будет следующим образом:

  1. Собирает список потенциальных DRUG_ID значений наряду с именами для колонн
  2. Подготовьте три строковые значений : префикс SQL (все до первого связанного с наркотиками оператора CASE, SQL stuffix (все после последнего связанного с наркотиками оператора CASE) и динамической части
  3. Построить динамическую часть путем объединения лекарственного средства CASE операторов, основанных на ранее извлеченный список
  4. Объедините их в один (надеюсь, действительный) оператор SQL и выполните
+0

PostgreSQL имеет PgPLSQL, с большим количеством сходства с PLSQL от Oracle. –

Смежные вопросы