0

I have a number of tables 'App_build', 'Server_build' with a column called 'buildid' and it contains a large number of records. I.e.:

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

I want to write a function called getmax(tablename) i.e. getmax('App_build') which will return a recordset which lists the highest values only. I.e:

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

I am new to SQL so am not sure how to start - I guess I can use a split command and then the MAX function but I have no idea where to start.

Any help will be great.

Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
osmanager
  • 3
  • 1
  • 1
    If "old format" is to be disregarded, how is it defined *exactly*? Also, how is the application name "Application1" defined? Version of PostgreSQL? – Erwin Brandstetter Mar 16 '13 at 17:45
  • Hi - thanks for the help. The version of PostgreSQL is 9.2. – osmanager Mar 18 '13 at 11:27
  • Hi - thanks for the help the answer provided works fine mostly. The version of PostgreSQL is 9.2. It turns out I made a mistake in the thinking the old format was to be disregarded. Format of buildid varies depending on the table. Table App_build format is [Application Name]_BLD_[Major Vesrion]_[Minor Version]_[Bugfix]_[BuildNumber]_[Optional PatchNumber] for example MyApp_BLD_1_0_0_1 is the first release and another example with the optional patch number would be MyApp_BLD_2_1_1_25_P01. I need to find the highest release number. The Server_build table is as described above. @ErwinBrandstetter – osmanager Mar 18 '13 at 11:51
  • Not sure, what the question is now. In any case, please *edit* essential additional info into the question with proper format. – Erwin Brandstetter Mar 18 '13 at 15:50

2 Answers2

2

Assuming current version PostgreSQL 9.2 for lack of information.

Plain SQL

The simple query could look like this:

SELECT max(buildid)
FROM   app_build
WHERE  buildid !~ '\d+_\d+_\d+_\d+$'  -- to exclude old format
GROUP  BY substring(buildid, '^[^_]+')
ORDER  BY substring(buildid, '^[^_]+');
  • The WHERE condition used a regular expression:

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

    Excludes buildid that end in 4 integer numbers divided by _.

    \d .. character class shorthand for digits. Only one backslash \ in modern PostgreSQL with standard_conforming_strings = ON.
    + .. 1 or more of preceding atom. $ .. As last character: anchored to the end of the string.

    There may be a cheaper / more accurate way, you did not properly specify the format.

  • GROUP BY and ORDER BY extract the the string before the first occurrence of _ with substring() as app name to group and order by. The regexp explained:

    ^ .. As first character: anchor search expression to start of string.
    [^_] .. Character class: any chracter that is not _.

    Does the same as split_part(buildid, '_', 1). But split_part() may be faster ..

Function

If you want to write a function where the table name is variable, you need dynamic SQL. That is a plpgsql function with EXECUTE:

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;

Call:

SELECT * FROM getmax('app_build');

Or if you are, in fact, using mixed case identifiers:

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

->SQLfiddle demo.

More info on the object identifier class regclass in this related questions:
Table name as a PostgreSQL function parameter

Community
  • 1
  • 1
Erwin Brandstetter
  • 605,456
  • 145
  • 1,078
  • 1,228
0

What you want is a groupwise_max. It can be done with MAX() but the usual way is left join:

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;

But since you're using PG it can be done with 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

Jakub Kania
  • 15,665
  • 2
  • 37
  • 47