对PostgreSQL中的CASE查询进行故障诊断

时间:2012-04-17 15:17:01

标签: postgresql case

我在PostgreSQL中有一个CASE查询的情况。

我的表格(“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”为空。

有什么想法吗?

谢谢。

2 个答案:

答案 0 :(得分: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_inceputNULL(我将此列视为instance_start_date),则获取给定persoane_fizice的最新修订版的值;
  2. 如果se_data_sfarsitNULL(我相信instance_end_date),那么请再次获取最新版本的值;
  3. 如果两列都是NOT NULL,则获取此类日期之间的修订值。
  4. 您在设置中没有提到任何约束,但我认为有几个条目具有重叠的日期范围是非法的。

    以下是我重写初始查询的方法,产生了正确的结果:

    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
    • 的默认值

    使用此组合,您始终可以期望查询从历史记录表中返回当前对象实例。

    我希望我的假设是正确的。

答案 1 :(得分: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)