从LEFT OUTER JOIN中选择最小值的最佳方法是什么?

时间:2015-03-23 17:27:41

标签: sql postgresql

我试图编写一个SQL语句(在本例中为postgresql)来汇编名为" subfield "的表格中的值。这些都与特定的varfield_id有关。

来自子字段的大部分数据不会重复,但标记为" c"的数据除外。带有" c"标签的值的顺序然后由display_order定义,它是一个int。

我的目标是只选择具有最小值display_order的行的内容值,标记为" c" (并符合其他JOIN标准)。我在下面列出了我的SQL以及一个record_num的输出样本,特别是可以帮助清理的东西。

SELECT 
r.record_num, a.content, q.content, c.display_order, c.content, d.content
FROM
db.subfield     a
LEFT OUTER JOIN
db.subfield     q
ON
  a.varfield_id = q.varfield_id and q.tag = 'q'
LEFT OUTER JOIN
db.subfield     c
ON
  a.varfield_id = c.varfield_id and c.tag = 'c'
LEFT OUTER JOIN
db.subfield     d
ON
  a.varfield_id = d.varfield_id and d.tag = 'd'
JOIN
db.record_metadata  r
ON
  a.record_id = r.id and r.record_type_code = 'b'
WHERE 
a.marc_tag = '100' and a.tag = 'a' and r.record_num = 2594119

--SAMPLE OF RETURNED DATA
--r.record_num; a.content;  q.content;      c.display_order;    c.content;      d.content;
--2594119;      "Name";     "(other Name)"; 2;                  "Name Title 2"; "YEAR-YEAR";
--2594119;      "Name";     "(other Name)"; 1;                  "Name Title 1"; "YEAR-YEAR";

是否有一种简单的方法可以只返回c.display_order最小的行?我想最终针对完整的子字段数据表运行此查询,并且不会限制特定的记录号。换句话说,我希望每个标签值只返回一行' a'在子字段中找到,包含具有非重复标记值的所有其他子字段值,' q'并且只有标签值' c'使用最小的display_order。

感谢愿意在这里帮忙的人!

2 个答案:

答案 0 :(得分:0)

SELECT r.record_num, a.content, q.content
 , c.display_order, c.content, d.content
FROM db.subfield     a
LEFT OUTER JOIN db.subfield  q
    ON a.varfield_id = q.varfield_id AND q.tag = 'q'
LEFT OUTER JOIN db.subfield     c
     ON a.varfield_id = c.varfield_id AND c.tag = 'c'
     AND NOT EXISTS (
         SELECT * FROM db.subfield nx
         WHERE nx.varfield_id = c.varfield_id   -- <<-- same
         AND nx.tag = c.tag                     -- <<-- same 
         AND nx.display_order < c.display_order -- <<--- different
         )
LEFT OUTER JOIN db.subfield     d
    ON a.varfield_id = d.varfield_id and d.tag = 'd'
JOIN db.record_metadata  r
    ON a.record_id = r.id and r.record_type_code = 'b'
WHERE a.marc_tag = '100' and a.tag = 'a'
and r.record_num = 2594119
    ;

答案 1 :(得分:0)

如果您使用的是postgresql 9.3或更高版本,则可以使用LATERAL JOIN。这与带有子查询的LEFT JOIN非常相似,但您可以从子查询中访问外部查询中的列:

SELECT  r.record_num, a.content, q.content, c.display_order, c.content, d.content
FROM    db.subfield     a
        LEFT OUTER JOIN db.subfield AS q
            ON a.varfield_id = q.varfield_id AND q.tag = 'q'
        LEFT JOIN LATERAL
        (   SELECT  c.display_order, c.content
            FROM    db.subfield AS c
            WHERE   a.varfield_id = c.varfield_id 
            AND     c.tag = 'c'
            ORDER BY c.display_order
        ) AS c ON TRUE
        LEFT OUTER JOIN db.subfield AS d
            ON a.varfield_id = d.varfield_id AND d.tag = 'd'
        JOIN db.record_metadata AS r
            ON a.record_id = r.id AMD r.record_type_code = 'b'
WHERE   a.marc_tag = '100' 
AND     a.tag = 'a' 
AND     r.record_num = 2594119;

<强> Simplified example on SQL Fiddle

您拥有的另一个选择是使用DISTINCT ON ()

SELECT  r.record_num, a.content, q.content, c.display_order, c.content, d.content
FROM    db.subfield     a
        LEFT OUTER JOIN db.subfield AS q
            ON a.varfield_id = q.varfield_id AND q.tag = 'q'
        LEFT OUTER JOIN 
        (   SELECT  DISTINCT ON (c.varfield_id), c.varfield_id c.display_order, c.content
            FROM    db.subfield AS c
            WHERE   c.tag = 'c'
            ORDER BY c.varfield_id, c.display_order
        ) AS c 
            ON a.varfield_id = c.varfield_id 
        LEFT OUTER JOIN db.subfield AS d
            ON a.varfield_id = d.varfield_id AND d.tag = 'd'
        JOIN db.record_metadata AS r
            ON a.record_id = r.id AMD r.record_type_code = 'b'
WHERE   a.marc_tag = '100' 
AND     a.tag = 'a' 
AND     r.record_num = 2594119;

<强> Simplified Example on SQL Fiddle