Thread: Select last there dates

Select last there dates

From
"Loredana Curugiu"
Date:
Hello again,

I have the following two tables:

Table 1:
uid | phone_number |
-----+-------------------------------
   8 | +40741775621 |
   8 | +40741775622 |
   8 | +40741775623 |
   9 | +40741775621 |
   9 | +40741775622 |
   9 | +40741775623 |
 10 | +40741775621 |
 10 | +40741775622 |
 10 | +40741775623 |
   7 | +40741775621 |
   7 | +40741775622 |
   7 | +40741775623 |
 11 | +40741775621 |
 11 | +40741775622 |
 11 | +40741775623 |

Table2:

           uid | phone_number |             date
 ---------------+-----------------------+-------------------------------
              8 | +40741775621 | 2007-06-21 10:40:00+00
              8 | +40741775621 | 2007-05-21 10:40:00+00
              8 | +40741775621 | 2007-04-21 10:40:00+00
              8 | +40741775621 | 2007-03-21 10:40:00+00
              8 | +40741775621 | 2007-06-20 10:40:00+00
              8 | +40741775621 | 2007-06-19 10:40:00+00
              8 | +40741775621 | 2007-06-18 10:40:00+00
              8 | +40741775622 | 2007-06-16 10:40:00+00
              8 | +40741775622 | 2007-06-15 10:40:00+00
              7 | +40741775622 | 2007-06-21 05:54:13.646457+00
              7 | +40741775621 | 2007-06-21 05:54:21.134469+00


For each uid  column from table1 I have different values phone_number
column.

For each uid and phone_number columns from table2 I have different
values for date column.

My task is to create a query which for a given uid returns all values
for phone_number column from table1 and last three values of date 
column from table2.

For example, if uid=8 the query should return:

 phone_number |    date
-----------------------+------------
 +40741775621 | 2007-06-21, 2007-06-20, 2007-06-19
 +40741775622 | 2007-06-16, 2007-06-15
 +40741775623 |

I created the query

    SELECT table1.phone_number,
                  TO_CHAR( table2.date, 'YYYY-MM-DD' ) AS date
       FROM ( SELECT * FROM table1 WHERE uid=8 ) table1
LEFT JOIN table2
            ON table1.uid=8
          AND table1.uid=table2.uid
          AND table1.phone_number=table2.phone_number

which returns.

 phone_number |    date
--------------+------------
 +40741775621 | 2007-06-18
 +40741775621 | 2007-06-19
 +40741775621 | 2007-06-20
 +40741775621 | 2007-03-21
 +40741775621 | 2007-04-21
 +40741775621 | 2007-05-21
 +40741775621 | 2007-06-21
 +40741775622 | 2007-06-15
 +40741775622 | 2007-06-16
 +40741775623 |

I don't know how to use this result for obtaining the result I need.
I was thinking to get the dates for the phone_number into a array
and then to use array_to string  function, but I don't know how to do it.
Any help, please?

Loredana

Re: Select last there dates

From
Richard Huxton
Date:
Loredana Curugiu wrote:
> My task is to create a query which for a given uid returns all values
> for phone_number column from table1 and last three values of date
> column from table2.
> 
> For example, if uid=8 the query should return:
> 
> phone_number |    date
> -----------------------+------------
> +40741775621 | 2007-06-21, 2007-06-20, 2007-06-19
> +40741775622 | 2007-06-16, 2007-06-15
> +40741775623 |

You either need a subquery with a LIMIT, or you could write a custom 
aggregate (see below):

BEGIN;

CREATE TABLE telnum_date_test (    telnum  text,    teldate date
);
INSERT INTO telnum_date_test SELECT '0123 456 789','2007-01-10'::date - 
generate_series(0,9);
INSERT INTO telnum_date_test SELECT '0234 567 890','2007-02-10'::date - 
generate_series(0,9);

SELECT * FROM telnum_date_test ORDER BY telnum,teldate;


CREATE FUNCTION date_top3_acc(topvals date[], newval date) RETURNS 
date[] AS $$
DECLARE    i       int4;    j       int4;    n       int4;    outvals date[];
BEGIN    -- array_upper returns null for an empty array and 1 for a 1 
element array    n := COALESCE( array_upper(topvals, 1), 0 );    j := 1;
    -- I suppose you could think of this as an insert-sort with an 
upper bound    FOR i IN 1..n LOOP        IF newval > topvals[i] AND j <= 3 THEN            outvals[j] := newval;
   j := j + 1;        END IF;        IF j <= 3 THEN            outvals[j] := topvals[i];            j := j + 1;
ENDIF;    END LOOP;
 
    IF j <= 3 THEN        outvals[j] := newval;    END IF;
    RETURN outvals;
END;
$$ LANGUAGE plpgsql;


CREATE AGGREGATE top3(date) (    sfunc = date_top3_acc,    stype = date[],    initcond = '{}'
);

SELECT telnum, top3(teldate) FROM telnum_date_test GROUP BY telnum ORDER 
BY telnum;

COMMIT;


--   Richard Huxton  Archonet Ltd


Re: Select last there dates

From
"A. Kretschmer"
Date:
am  Thu, dem 21.06.2007, um 11:18:13 +0300 mailte Loredana Curugiu folgendes:
> Hello again,
>
> I have the following two tables:
>
> Table 1:
> uid | phone_number |
> -----+-------------------------------
>    8 | +40741775621 |
>    8 | +40741775622 |
>    8 | +40741775623 |
>    9 | +40741775621 |
>    9 | +40741775622 |
>    9 | +40741775623 |
>  10 | +40741775621 |
>  10 | +40741775622 |
>  10 | +40741775623 |
>    7 | +40741775621 |
>    7 | +40741775622 |
>    7 | +40741775623 |
>  11 | +40741775621 |
>  11 | +40741775622 |
>  11 | +40741775623 |
>
> Table2:
>
>            uid | phone_number |             date
>  ---------------+-----------------------+-------------------------------
>               8 | +40741775621 | 2007-06-21 10:40:00+00
>               8 | +40741775621 | 2007-05-21 10:40:00+00
>               8 | +40741775621 | 2007-04-21 10:40:00+00
>               8 | +40741775621 | 2007-03-21 10:40:00+00
>               8 | +40741775621 | 2007-06-20 10:40:00+00
>               8 | +40741775621 | 2007-06-19 10:40:00+00
>               8 | +40741775621 | 2007-06-18 10:40:00+00
>               8 | +40741775622 | 2007-06-16 10:40:00+00
>               8 | +40741775622 | 2007-06-15 10:40:00+00
>               7 | +40741775622 | 2007-06-21 05:54:13.646457+00
>               7 | +40741775621 | 2007-06-21 05:54:21.134469+00
>
>
> For each uid  column from table1 I have different values phone_number
> column.
>
> For each uid and phone_number columns from table2 I have different
> values for date column.
>
> My task is to create a query which for a given uid returns all values
> for phone_number column from table1 and last three values of date
> column from table2.
>
> For example, if uid=8 the query should return:
>
>  phone_number |    date
> -----------------------+------------
>  +40741775621 | 2007-06-21, 2007-06-20, 2007-06-19
>  +40741775622 | 2007-06-16, 2007-06-15
>  +40741775623 |

lets try:

first, i need a comma-aggregat:

CREATE FUNCTION comma_aggregate(text,text) RETURNS text AS '
SELECT CASE WHEN $1 <> '''' THEN $1 || '', '' || $2 ELSE $2 END; '
LANGUAGE sql IMMUTABLE STRICT;

CREATE AGGREGATE comma (basetype=text, sfunc=comma_aggregate, stype=text, initcond='' );


Now your tables, with a typo in the phone_number - column, sorry ;-)

test=*# select * from t1;
 uid | phone_numer
-----+-------------
   8 | 40741775621
   8 | 40741775622
   8 | 40741775623
   9 | 40741775621
   9 | 40741775622
   9 | 40741775623
  10 | 40741775621
  10 | 40741775622
  10 | 40741775623
(9 rows)

test=*# select * from t2;
 uid | phone_numer |   datum
-----+-------------+------------
   8 | 40741775621 | 2007-06-21
   8 | 40741775621 | 2007-05-21
   8 | 40741775621 | 2007-04-21
   8 | 40741775621 | 2007-03-21
   8 | 40741775621 | 2007-06-20
   8 | 40741775621 | 2007-06-19
   8 | 40741775621 | 2007-06-18
   8 | 40741775622 | 2007-06-16
   8 | 40741775622 | 2007-06-15
   7 | 40741775622 | 2007-06-21
   7 | 40741775621 | 2007-06-21
(11 rows)


And now:

test=*# select t1.phone_numer, substring(comma(t2.datum) from 1 for 34)
from t1 left outer join (select uid, phone_numer, datum from t2 order by
2 ) t2 on (t1.uid,t1.phone_numer)=(t2.uid,t2.phone_numer) where t1.uid=8
group by t1.phone_numer;
 phone_numer |             substring
-------------+------------------------------------
 40741775621 | 2007-06-21, 2007-05-21, 2007-04-21
 40741775622 | 2007-06-16, 2007-06-15
 40741775623 |
(3 rows)


Perhaps there are better solutions possible...



Andreas
--
Andreas Kretschmer
Kontakt:  Heynitz: 035242/47150,   D1: 0160/7141639 (mehr: -> Header)
GnuPG-ID:   0x3FFF606C, privat 0x7F4584DA   http://wwwkeys.de.pgp.net

Re: Select last there dates

From
"Loredana Curugiu"
Date:
Richard, Andreas,

thank you very much for your solutions. I took a look on
both solutions, but I choosed Andreas's solution because
is shorter :)

So Andreas, would you please give some more explanations
on your solution? I didn't work with functions and aggregate till
now.

I don't understand how this comma_aggregate works. I can see that this
function it is defined with two arguments, but the aggredate it is called
with a single argument.

And what is LANGUAGE sql IMMUTABLE STRICT ?

CREATE FUNCTION comma_aggregate(text,text) RETURNS text AS '
SELECT CASE WHEN $1 <> '''' THEN $1 || '', '' || $2 ELSE $2 END; '
LANGUAGE sql IMMUTABLE STRICT;

CREATE AGGREGATE comma (basetype=text, sfunc=comma_aggregate, stype=text, initcond='' );



Best regards,
      Loredana

Re: Select last there dates

From
Richard Huxton
Date:
Loredana Curugiu wrote:
> Richard, Andreas,
> 
> thank you very much for your solutions. I took a look on
> both solutions, but I choosed Andreas's solution because
> is shorter :)

Not to mention clever, exploiting the fact that we know the length of a 
text-representation of three comma-separated dates.

I think there might be a small typo though. The left-join is to:  (select uid, phone_numer, datum from t2 order by 2 )
Probably want to order by "datum DESC" too, to ensure you get the latest 
dates for each telnum.

> So Andreas, would you please give some more explanations
> on your solution? I didn't work with functions and aggregate till
> now.
> 
> I don't understand how this comma_aggregate works. I can see that this
> function it is defined with two arguments, but the aggredate it is called
> with a single argument.

The comma_aggregate function takes two parameters (RUNNING-TOTAL, 
NEW-VALUE). The result forms the RUNNING-TOTAL for the next call. This 
RUNNING-TOTAL has a type set by "stype" below and with an initial value 
set by "initcond".

> And what is LANGUAGE sql IMMUTABLE STRICT ?

The function is pure SQL (mine was procedural plpgsql, other languages 
are available). It's IMMUTABLE because the output depends only on the 
inputs, not the contents of the database and begin STRICT if input 
values are NULL then the output is automatically NULL.

> CREATE FUNCTION comma_aggregate(text,text) RETURNS text AS '
>> SELECT CASE WHEN $1 <> '''' THEN $1 || '', '' || $2 ELSE $2 END; '
>> LANGUAGE sql IMMUTABLE STRICT;
>>
>> CREATE AGGREGATE comma (basetype=text, sfunc=comma_aggregate, stype=text,
>> initcond='' );


--   Richard Huxton  Archonet Ltd


Re: Select last there dates

From
"A. Kretschmer"
Date:
am  Thu, dem 21.06.2007, um 16:00:05 +0300 mailte Loredana Curugiu folgendes:
> So Andreas, would you please give some more explanations
> on your solution? I didn't work with functions and aggregate till
> now.

I will try it, but i'm not a nativ english speaker and thats why i have
some problems...

>
> I don't understand how this comma_aggregate works. I can see that this
> function it is defined with two arguments, but the aggredate it is called
> with a single argument.

An aggregate needs among others a so called 'SFUNC' with 2 parameters:
internal-state and next data item. It returns the next-internal-state.

This is explained here:
http://www.postgresql.org/docs/8.1/interactive/sql-createaggregate.html

>
> And what is LANGUAGE sql IMMUTABLE STRICT ?

An IMMUTABLE function can't modify the database
http://www.postgresql.org/docs/current/static/xfunc-volatility.html


STRICT:
http://www.postgresql.org/docs/8.2/static/xfunc-c.html

,----[  ]
| Notice that we have specified the functions as "strict", meaning that
| the system should automatically assume a null result if any input value
| is null.
`----



Andreas
--
Andreas Kretschmer
Kontakt:  Heynitz: 035242/47150,   D1: 0160/7141639 (mehr: -> Header)
GnuPG-ID:   0x3FFF606C, privat 0x7F4584DA   http://wwwkeys.de.pgp.net

Re: Select last there dates

From
"Loredana Curugiu"
Date:

I think there might be a small typo though. The left-join is to:
   (select uid, phone_numer, datum from t2 order by 2 )
Probably want to order by "datum DESC" too, to ensure you get the latest
dates for each telnum.
 
yes, I also observed this, so I have add to my query.

Thank you for your explanations, Richard.


Best regards,
       Loredana


Re: [NOVICE] Select last there dates

From
"Loredana Curugiu"
Date:
Thank you for documentation.

Best regards,
        Loredana