PostgreSQL函数用于选择拆分记录的最大值

时间:2013-03-16 16:21:16

标签: sql function postgresql greatest-n-per-group plpgsql

我有许多表'App_build','Server_build',其中包含一个名为'buildid'的列,它包含大量记录。即:

buildid
-----------
Application1_BLD_01
Application1_BLD_02
Application1_BLD_03
Application2_BLD_01
Application3_BLD_01
Application3_BLD_02
Application4_1_0_0_1 - old format to be disregarded
Application4_1_0_0_2
Application4_BLD_03

我想写一个名为getmax(tablename)的函数,即getmax('App_build') 这将返回仅列出最高值的记录集。即:

buildid
--------
Application1_BLD_03
Application2_BLD_01
Application3_BLD_02
Application4_BLD_03

我是SQL的新手,所以我不知道如何开始 - 我想我可以使用split命令然后使用MAX函数,但我不知道从哪里开始。

任何帮助都会很棒。

2 个答案:

答案 0 :(得分:2)

假设当前版本PostgreSQL 9.2缺乏信息。

纯SQL

简单查询可能如下所示:

SELECT max(buildid)
FROM   app_build
WHERE  buildid !~ '\d+_\d+_\d+_\d+$'  -- to exclude old format
GROUP  BY substring(buildid, '^[^_]+')
ORDER  BY substring(buildid, '^[^_]+');
  • WHERE条件使用正则表达式:

    buildid !~ '\d+_\d+_\d+_\d+$'
    

    以4个整数除以buildid的结尾排除_

    \d ..数字的字符类简写。在standard_conforming_strings = ON的现代PostgreSQL中只有一个反斜杠\ + ..前面原子中的一个或多个。 $ ..作为最后一个字符:锚定在字符串的末尾。

    可能有更便宜/更准确的方式,您没有正确指定格式。

  • GROUP BYORDER BY在第一次出现_之前提取字符串,并将substring()作为应用名称进行分组和排序。正则表达式解释说:

    ^ ..作为第一个字符:锚点搜索表达式以开始字符串。
    [^_] ..字符类: _的任何字符。

    split_part(buildid, '_', 1)相同。但split_part()可能会更快..

功能

如果要编写表名可变的函数,则需要动态SQL 。这是一个带有EXECUTE的plpgsql函数:

CREATE OR REPLACE FUNCTION getmax(_tbl regclass) 
  RETURNS SETOF text AS
$func$
BEGIN

RETURN QUERY
EXECUTE format($$
   SELECT max(buildid)
   FROM   %s
   WHERE  buildid !~ '\d+_\d+_\d+_\d+$'
   GROUP  BY substring(buildid, '^[^_]+')
   ORDER  BY substring(buildid, '^[^_]+')$$, _tbl);

END
$func$ LANGUAGE plpgsql;

呼叫:

SELECT * FROM getmax('app_build');

或者,如果您实际上使用混合大小写标识符

SELECT * FROM getmax('"App_build"');

->SQLfiddle demo.

有关the object identifier class regclass 的更多信息,请参阅此相关问题:
Table name as a PostgreSQL function parameter

答案 1 :(得分:0)

你想要的是groupwise_max。可以使用MAX()完成,但通常的方法是加入:

SELECT b1.buildid
FROM builds AS b1
LEFT JOIN builds AS b2 ON 
split_part(b1.buildid, '_', 1)=split_part(b2.buildid, '_', 1)
AND
split_part(b1.buildid, '_', 3)::int<split_part(b2.buildid, '_', 3)::int
WHERE b2.buildid IS NULL;

但是既然你正在使用PG,可以使用DISTINCT ON ()

来完成
SELECT DISTINCT ON (split_part(buildid, '_', 1)) buildid
FROM builds 
ORDER BY split_part(buildid, '_', 1),split_part(buildid, '_', 3)::int DESC

http://sqlfiddle.com/#!12/308bf/9