【问题标题】:PostgreSQL function to select max values of split recordPostgreSQL函数选择拆分记录的最大值
【发布时间】:2013-03-16 16:21:16
【问题描述】:

我有许多表“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 新手,所以不知道如何开始 - 我想我可以使用拆分命令,然后使用 MAX 函数,但我不知道从哪里开始。

任何帮助都会很棒。

【问题讨论】:

  • 如果要忽略“旧格式”,它是如何确切地定义的?另外,应用程序名称“Application1”是如何定义的? PostgreSQL 的版本?
  • 嗨 - 感谢您的帮助。 PostgreSQL 的版本是 9.2。
  • 嗨 - 感谢您的帮助,所提供的答案大部分工作正常。 PostgreSQL 的版本是 9.2。事实证明,我错误地认为旧格式应该被忽略。 buildid 的格式因表而异。表 App_build 格式为 [Application Name]_BLD_[Major Vesrion]_[Minor Version]_[Bugfix]_[BuildNumber]_[Optional PatchNumber] 例如 MyApp_BLD_1_0_0_1 是第一个版本,另一个带有可选补丁号的示例是 MyApp_BLD_2_1_1_25_P01。我需要找到最高版本号。 Server_build 表如上所述。 @ErwinBrandstetter
  • 不确定,现在的问题是什么。在任何情况下,请以适当的格式将必要的附加信息编辑到问题中。

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


【解决方案1】:

由于缺少信息,假设当前版本为 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 .. 字符类数字的简写。在现代 PostgreSQL 中只有一个反斜杠 \standard_conforming_strings = ON.
    + .. 1 个或多个前面的原子。 $ .. 作为最后一个字符:锚定到字符串的末尾。

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

  • 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

【讨论】:

    【解决方案2】:

    你想要的是一个 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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2012-05-18
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多