2012-04-17 8 views
1

У меня возникла ситуация с запросом CASE в PostgreSQL.Устранение неполадок запроса CASE в PostgreSQL

Мой стол («statut_existenta_pf») выглядит так, как на изображении здесь http://cl.ly/0L1Q2f3v3L0s3V1p3P3X

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

SELECT p.*, se.se_id_statut, ne.ne_denumire 
    FROM persoane_fizice p, 
     statut_existenta_pf se, 
     nom_statut_existenta ne 
    WHERE p.pf_id = :id_pf 
    AND se.se_id_pf = p.pf_id 
    AND se.se_id_statut IN 
     (CASE 
      WHEN se_data_inceput IS NULL THEN 
      (SELECT se_id_statut FROM statut_existenta_pf 
       WHERE se_id_pf = :id_pf 
       ORDER BY se_id DESC LIMIT 1) 
      WHEN se_data_inceput IS NOT NULL AND se_data_sfarsit IS NULL THEN 
      (SELECT se_id_statut FROM statut_existenta_pf 
       WHERE se_id_pf = :id_pf 
       ORDER BY se_id DESC LIMIT 1) 
      ELSE 
      (SELECT se_id_statut FROM statut_existenta_pf 
       WHERE se_id_pf = :id_pf 
        AND CURRENT_DATE BETWEEN se_data_inceput AND se_data_sfarsit) 
     END) 
    AND se.se_id_statut = ne.ne_id 

Дело в том, Я получаю 0 результатов, и я должен вернуть один результат, тот, где «se_data_inceput» - «2010-03-31» и «se_data_sfarsit» - null.

Любые идеи?

спасибо.

ответ

0

Я не уверен CASE может вернуть список, который вы получаете в части ELSE, может быть, кто-то еще может нас просветить. В том же время, я думаю, что вы можете поместить se.se_id_statut IN части внутри CASE:

AND 
    (CASE 
     WHEN se_data_inceput IS NULL THEN 
     se.se_id_statut IN (SELECT se_id_statut FROM statut_existenta_pf 
      WHERE se_id_pf = :id_pf 
      ORDER BY se_id DESC LIMIT 1) 
     WHEN se_data_inceput IS NOT NULL AND se_data_sfarsit IS NULL THEN 
     se.se_id_statut IN (SELECT se_id_statut FROM statut_existenta_pf 
      WHERE se_id_pf = :id_pf 
      ORDER BY se_id DESC LIMIT 1) 
     ELSE 
     se.se_id_statut IN (SELECT se_id_statut FROM statut_existenta_pf 
      WHERE se_id_pf = :id_pf 
       AND CURRENT_DATE BETWEEN se_data_inceput AND se_data_sfarsit) 
    END) 
1

Похоже, ты хранишь повременные экземпляры объектов в statut_existenta_pf таблицы.

Я использовал следующий тестовый стенд:

CREATE TABLE statut_existenta_pf (
    se_id     int4, 
    se_id_pf    int4, 
    se_id_statut   int2, 
    se_data_inceput   date, 
    se_data_sfarsit   date 
); -- the rest fields are irrelevant for this example 
CREATE TABLE nom_statut_existenta (
    id   int2, 
    ne_denumire varchar(30) 
); -- my wild guess bout this table 
CREATE TABLE persoane_fizice (
    pf_id   int4, 
    pf_name   varchar(60) 
); --- same here, irrelevant for the example 

со следующими данными теста:

INSERT INTO nom_statut_existenta VALUES 
    (1, 'Status: Vive'), (2, 'Status: Morto'); 
INSERT INTO persoane_fizice VALUES (3489, 'Giuseppe Garibaldi'); 
INSERT INTO statut_existenta_pf VALUES 
    (4275, 3489, 2, '2012-04-18', '2012-05-18'), 
    (3669, 3489, 1, '2010-03-31', NULL); 

Теперь вы ищете текущих объект в это время на основе серии , Ваша логика, как представляется, следующее:

  1. если se_data_inceput является NULL (я лечу эту колонку как instance_start_date), а затем получить значение последнего пересмотра для данной persoane_fizice;
  2. если se_data_sfarsit is NULL (instance_end_date Я верю), то снова получите значение самой последней версии;
  3. если оба столбца: NOT NULL, то получите значение для ревизии, падающего между такими датами.

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

Вот как я переписал свой первоначальный запрос, получая соответствующие результаты:

WITH max_se_id AS (
SELECT se_id_pf, max(se_id) se_id_max FROM statut_existenta_pf 
GROUP BY se_id_pf 
) 
SELECT p.*, se.se_id_statut, ne.ne_denumire 
    FROM persoane_fizice p 
    JOIN statut_existenta_pf se ON se.se_id_pf = p.pf_id 
    JOIN nom_statut_existenta ne ON ne.id = se.se_id_statut 
    JOIN max_se_id mse ON se.se_id_pf = mse.se_id_pf 
WHERE p.pf_id = :id_pf 
    AND se.se_id_statut IN 
    (CASE 
     WHEN se_data_inceput IS NULL THEN mse.se_id_max 
     WHEN se_data_inceput IS NOT NULL AND se_data_sfarsit IS NULL THEN mse.se_id_max 
     ELSE se_id_statut 
    END) ; 

Но это дает запрос неверные результаты для данного тестового стенда он возвращает версию с наивысшим se_id, несмотря на то, что имеет начало времени в будущем.

Я использую тот же подход, чтобы сохранить историю объектов в базе данных, и я рекомендую использовать такой запрос вместо:

SELECT p.*, se.se_id_statut, ne.ne_denumire 
    FROM persoane_fizice p 
    JOIN statut_existenta_pf se ON se.se_id_pf = p.pf_id 
    JOIN nom_statut_existenta ne ON ne.id = se.se_id_statut 
WHERE p.pf_id = :id_pf 
    AND statement_timestamp() BETWEEN coalesce(se.se_data_inceput, now()) 
     AND coalesce(se.se_data_sfarsit, clock_timestamp()); 

Если вы не перекрывающиеся даты в столбцах с se_data_inceput + se_data_sfarsit, этим запросом даст текущую активную строку.

Я использую:

  • now() в качестве значения по умолчанию для se_data_inceput, это дает время начала текущей транзакции;
  • statement_timestamp() как тока момент времени и
  • clock_timestamp() как по умолчанию для se_data_sfarsit.

С помощью этой комбинации вы всегда можете ожидать ваш запрос, чтобы вернуть текущий экземпляр объектаиз таблицы истории.

Надеюсь, мои предположения были правильными.

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