Обсуждение: Refining query statement

Поиск
Список
Период
Сортировка

Refining query statement

От
Rich Shepard
Дата:
   Working with my sales/client management system using psql I have a select
statement to identify contacts to be made. This statement works:

select (C.contact_id, C.lname, C.fname, C.direct_phone, O.org_name, 
A.next_contact)
from Contacts as C, Organizations as O, Activities as A
where C.org_id = O.org_id and C.contact_id = A.contact_id and
       A.next_contact <= 'today' and A.next_contact > '2018-12-31' and
       A.next_contact is not null;

but would benefit from tweaking. When I have had multiple contacts with
someone I want only the most recent one displayed, not all, and they should
be no more ancient than a defined period (e.g., a month).

   I want to learn how to make this query cleaner and more flexible. When I
write the UI for this I want to be able to specify a data range in addition
to a fixed 'today'. Pointers on what to read will be very helpful.

TIA,

Rich


Re: Refining query statement

От
Thomas Kellerer
Дата:
Rich Shepard schrieb am 15.01.2019 um 16:39:
>   Working with my sales/client management system using psql I have a select
> statement to identify contacts to be made. This statement works:
> 
> select (C.contact_id, C.lname, C.fname, C.direct_phone, O.org_name, A.next_contact)
> from Contacts as C, Organizations as O, Activities as A
> where C.org_id = O.org_id and C.contact_id = A.contact_id and
>       A.next_contact <= 'today' and A.next_contact > '2018-12-31' and
>       A.next_contact is not null;
> 
> but would benefit from tweaking. When I have had multiple contacts with
> someone I want only the most recent one displayed, not all, and they should
> be no more ancient than a defined period (e.g., a month).
> 
>   I want to learn how to make this query cleaner and more flexible. When I
> write the UI for this I want to be able to specify a data range in addition
> to a fixed 'today'. Pointers on what to read will be very helpful.

With regards to "cleaner": the first thing to do is to remove the parentheses around the column list. 
In Postgres "(a,b,c)" creates a single column with an anonymous record type (that contains three fields), rather than
selectingthree columns.
 
In other DBMS those parentheses are simply useless.

"cleaner" is always subjective, but I find explicit JOIN operators a lot cleaner than the old implicit joins. 

The condition "A.next_contact is not null" is actually no necessary because you already have a condition on that
column,so NULL values won't be returned anyway. 
 

To get the "most recent one" in Postgres, DISTINCT ON () is usually the best way to do it: 

So we end up with something like this: 

    select distinct on (C.contact_id) C.contact_id, C.lname, C.fname, C.direct_phone, O.org_name, A.next_contact
    from Contacts as C
      join Organizations as O on C.org_id = O.org_id
      join Activities as A on C.contact_id = A.contact_id 
    where A.next_contact <= 'today' 
      and A.next_contact > '2018-12-31' 
    order by c.contact_id, a.next_contact DESC;

      



Re: Refining query statement

От
Adrian Klaver
Дата:
On 1/15/19 7:39 AM, Rich Shepard wrote:
>    Working with my sales/client management system using psql I have a 
> select
> statement to identify contacts to be made. This statement works:
> 
> select (C.contact_id, C.lname, C.fname, C.direct_phone, O.org_name, 
> A.next_contact)
> from Contacts as C, Organizations as O, Activities as A
> where C.org_id = O.org_id and C.contact_id = A.contact_id and
>        A.next_contact <= 'today' and A.next_contact > '2018-12-31' and
>        A.next_contact is not null;
> 
> but would benefit from tweaking. When I have had multiple contacts with
> someone I want only the most recent one displayed, not all, and they should
> be no more ancient than a defined period (e.g., a month).
> 
>    I want to learn how to make this query cleaner and more flexible. When I
> write the UI for this I want to be able to specify a data range in addition
> to a fixed 'today'. Pointers on what to read will be very helpful.

For the above I could see using a datepicker widget that allows for 
multidate select. The specifics would depend on the software you are 
using to write the UI.

> 
> TIA,
> 
> Rich
> 
> 


-- 
Adrian Klaver
adrian.klaver@aklaver.com


Re: Refining query statement

От
Ron
Дата:
On 1/15/19 9:47 AM, Thomas Kellerer wrote:
> Rich Shepard schrieb am 15.01.2019 um 16:39:
>>    Working with my sales/client management system using psql I have a select
>> statement to identify contacts to be made. This statement works:
>>
>> select (C.contact_id, C.lname, C.fname, C.direct_phone, O.org_name, A.next_contact)
>> from Contacts as C, Organizations as O, Activities as A
>> where C.org_id = O.org_id and C.contact_id = A.contact_id and
>>        A.next_contact <= 'today' and A.next_contact > '2018-12-31' and
>>        A.next_contact is not null;
>>
>> but would benefit from tweaking. When I have had multiple contacts with
>> someone I want only the most recent one displayed, not all, and they should
>> be no more ancient than a defined period (e.g., a month).
>>
>>    I want to learn how to make this query cleaner and more flexible. When I
>> write the UI for this I want to be able to specify a data range in addition
>> to a fixed 'today'. Pointers on what to read will be very helpful.
> With regards to "cleaner": the first thing to do is to remove the parentheses around the column list.
> In Postgres "(a,b,c)" creates a single column with an anonymous record type (that contains three fields), rather than
selectingthree columns.
 
> In other DBMS those parentheses are simply useless.
>
> "cleaner" is always subjective, but I find explicit JOIN operators a lot cleaner than the old implicit joins.
>
> The condition "A.next_contact is not null" is actually no necessary because you already have a condition on that
column,so NULL values won't be returned anyway.
 
>
> To get the "most recent one" in Postgres, DISTINCT ON () is usually the best way to do it:
>
> So we end up with something like this:
>
>      select distinct on (C.contact_id) C.contact_id, C.lname, C.fname, C.direct_phone, O.org_name, A.next_contact
>      from Contacts as C
>        join Organizations as O on C.org_id = O.org_id
>        join Activities as A on C.contact_id = A.contact_id
>      where A.next_contact <= 'today'
>        and A.next_contact > '2018-12-31'
>      order by c.contact_id, a.next_contact DESC;

And I've never liked this method (though I'm old and crotchety)....

-- 
Angular momentum makes the world go 'round.


Re: Refining query statement

От
Rich Shepard
Дата:
On Tue, 15 Jan 2019, Adrian Klaver wrote:

> For the above I could see using a datepicker widget that allows for
> multidate select. The specifics would depend on the software you are using
> to write the UI.

Adrian,

   I'm using wxPython4, and I will use a calendar with that. But, is there a
way to enhance the psql version, too?

Thanks,

Rich


Re: Refining query statement

От
Rich Shepard
Дата:
On Tue, 15 Jan 2019, Thomas Kellerer wrote:

> With regards to "cleaner": the first thing to do is to remove the
> parentheses around the column list. In Postgres "(a,b,c)" creates a single
> column with an anonymous record type (that contains three fields), rather
> than selecting three columns. In other DBMS those parentheses are simply
> useless.

Thomas,

I should have seen that myself. Thanks for pointing out the obvious to me.

> "cleaner" is always subjective, but I find explicit JOIN operators a lot
> cleaner than the old implicit joins.

   This does make sense; I need to refresh my knowledge of JOIN operators and
will do so.

> The condition "A.next_contact is not null" is actually no necessary
> because you already have a condition on that column, so NULL values won't
> be returned anyway.

   True that.

> To get the "most recent one" in Postgres, DISTINCT ON () is usually the
> best way to do it: So we end up with something like this:
>
>    select distinct on (C.contact_id) C.contact_id, C.lname, C.fname, C.direct_phone, O.org_name, A.next_contact
>    from Contacts as C
>      join Organizations as O on C.org_id = O.org_id
>      join Activities as A on C.contact_id = A.contact_id
>    where A.next_contact <= 'today'
>      and A.next_contact > '2018-12-31'
>    order by c.contact_id, a.next_contact DESC;

   This insight really helps. Now I know how to approach other queries.

Many thanks,

Rich


Re: Refining query statement

От
Rich Shepard
Дата:
On Tue, 15 Jan 2019, Ron wrote:

> And I've never liked this method (though I'm old and crotchety)....

Ron,

   I'm older but not crotchety (most days), and I'm taking my SQL knowledge
beyone what I've used in the past. I would appreciate you're explaining why
you don't like the explicit JOINs presented by Thomas. The more I learn from
you more experienced SQL programmers the better I am.

Regards,

Rich


Re: Refining query statement

От
Adrian Klaver
Дата:
On 1/15/19 8:26 AM, Rich Shepard wrote:
> On Tue, 15 Jan 2019, Adrian Klaver wrote:
> 
>> For the above I could see using a datepicker widget that allows for
>> multidate select. The specifics would depend on the software you are 
>> using
>> to write the UI.
> 
> Adrian,
> 
>    I'm using wxPython4, and I will use a calendar with that. But, is 
> there a
> way to enhance the psql version, too?

Use BETWEEN?:
https://www.postgresql.org/docs/10/functions-comparison.html
a BETWEEN x AND y     between

So:

next_contact BETWEEN '01/01/2019'::date AND '01/15/2019'::date

Or a range function:

https://www.postgresql.org/docs/10/functions-range.html

<@     element is contained by



> 
> Thanks,
> 
> Rich
> 
> 


-- 
Adrian Klaver
adrian.klaver@aklaver.com


Re: Refining query statement

От
Adrian Klaver
Дата:
On 1/15/19 8:02 AM, Ron wrote:

>> the best way to do it:
>>
>> So we end up with something like this:
>>
>>      select distinct on (C.contact_id) C.contact_id, C.lname, C.fname, 
>> C.direct_phone, O.org_name, A.next_contact
>>      from Contacts as C
>>        join Organizations as O on C.org_id = O.org_id
>>        join Activities as A on C.contact_id = A.contact_id
>>      where A.next_contact <= 'today'
>>        and A.next_contact > '2018-12-31'
>>      order by c.contact_id, a.next_contact DESC;
> 
> And I've never liked this method (though I'm old and crotchety)....
> 

What is the specific objection?

To me:

1) Plus side. It saves an explicit sub-query

2) Minus side. It is not portable. But then complete portability is a 
unicorn in my opinion.

-- 
Adrian Klaver
adrian.klaver@aklaver.com


Re: Refining query statement

От
Rich Shepard
Дата:
On Tue, 15 Jan 2019, Adrian Klaver wrote:

> Use BETWEEN?:
> https://www.postgresql.org/docs/10/functions-comparison.html
> a BETWEEN x AND y     between
>
> So:
>
> next_contact BETWEEN '01/01/2019'::date AND '01/15/2019'::date
>
> Or a range function:
>
> https://www.postgresql.org/docs/10/functions-range.html

Adrian,

   Ah, yes! I do need to refresh my SQL knowledge and am working on that.

Thanks very much again,

Rich


Re: Refining query statement

От
Thomas Kellerer
Дата:
Adrian Klaver schrieb am 15.01.2019 um 17:44:
>>> So we end up with something like this:
>>>
>>>      select distinct on (C.contact_id) C.contact_id, C.lname, C.fname, C.direct_phone, O.org_name, A.next_contact
>>>      from Contacts as C
>>>        join Organizations as O on C.org_id = O.org_id
>>>        join Activities as A on C.contact_id = A.contact_id
>>>      where A.next_contact <= 'today'
>>>        and A.next_contact > '2018-12-31'
>>>      order by c.contact_id, a.next_contact DESC;
>>
>> And I've never liked this method (though I'm old and crotchety)....
>>
> 
> What is the specific objection?
> 
> To me:
> 
> 1) Plus side. It saves an explicit sub-query
> 
> 2) Minus side. It is not portable. But then complete portability is a unicorn in my opinion.

I think Ron was referring to the JOIN operator, rather than the DISTINCT ON

  



Re: Refining query statement

От
Rob Sargent
Дата:
On 1/15/19 9:02 AM, Ron wrote: select distinct on (C.contact_id) 
C.contact_id, C.lname, C.fname, C.direct_phone, O.org_name, A.next_contact
>>      from Contacts as C
>>        join Organizations as O on C.org_id = O.org_id
>>        join Activities as A on C.contact_id = A.contact_id
>>      where A.next_contact <= 'today'
>>        and A.next_contact > '2018-12-31'
>>      order by c.contact_id, a.next_contact DESC;
>
> And I've never liked this method (though I'm old and crotchety)....
>
I suspect I can match your old and crotchety, but jump in, the water is 
fine (once you get used to it)


Re: Refining query statement

От
Rich Shepard
Дата:
On Tue, 15 Jan 2019, Thomas Kellerer wrote:

>    select distinct on (C.contact_id) C.contact_id, C.lname, C.fname, C.direct_phone, O.org_name, A.next_contact
>    from Contacts as C
>      join Organizations as O on C.org_id = O.org_id
>      join Activities as A on C.contact_id = A.contact_id
>    where A.next_contact <= 'today'
>      and A.next_contact > '2018-12-31'
>    order by c.contact_id, a.next_contact DESC;

Combining this with Adrian's advice to use BETWEEN I have this statement
that almost works:

SELECT DISTINCT ON (c.contact_id) c.contact_id, c.lname, c.fname, c.direct_phone, o.org_name, a.next_contact
FROM Contacts AS c
   JOIN Organizations AS o ON c.org_id = o.org_id
   JOIN Activities AS a ON c.contact_id = a.contact_id 
WHERE next_contact BETWEEN '01/01/2019'::date AND 'today'::date 
ORDER BY c.contact_id, a.next_contact DESC;

It fails when the most recent next_contact column in Activities is NULL and
an earier row has a non-NULL date in the specified range.

I tried specifying max(a.next_contact) and added GROUP BY, but the result
set all returned o.org_name columns to the same one.

The WHERE clause needs to exclude a contact_id where the most current row in
Activities has NULL for the next_contact column. I've tried a few ideas but
none work so I need to learn the proper syntax, and I don't find that in
Rick van der Lans' or Joe Celko's books I have.

Looking forward to learning,

Rich


Re: Refining query statement

От
Adrian Klaver
Дата:
On 1/17/19 8:14 AM, Rich Shepard wrote:
> On Tue, 15 Jan 2019, Thomas Kellerer wrote:
> 
>>    select distinct on (C.contact_id) C.contact_id, C.lname, C.fname, 
>> C.direct_phone, O.org_name, A.next_contact
>>    from Contacts as C
>>      join Organizations as O on C.org_id = O.org_id
>>      join Activities as A on C.contact_id = A.contact_id
>>    where A.next_contact <= 'today'
>>      and A.next_contact > '2018-12-31'
>>    order by c.contact_id, a.next_contact DESC;
> 
> Combining this with Adrian's advice to use BETWEEN I have this statement
> that almost works:
> 
> SELECT DISTINCT ON (c.contact_id) c.contact_id, c.lname, c.fname, 
> c.direct_phone, o.org_name, a.next_contact
> FROM Contacts AS c
>    JOIN Organizations AS o ON c.org_id = o.org_id
>    JOIN Activities AS a ON c.contact_id = a.contact_id WHERE 
> next_contact BETWEEN '01/01/2019'::date AND 'today'::date ORDER BY 
> c.contact_id, a.next_contact DESC;
> 
> It fails when the most recent next_contact column in Activities is NULL and
> an earier row has a non-NULL date in the specified range.
> 
> I tried specifying max(a.next_contact) and added GROUP BY, but the result
> set all returned o.org_name columns to the same one.
> 
> The WHERE clause needs to exclude a contact_id where the most current 
> row in
> Activities has NULL for the next_contact column. I've tried a few ideas but
> none work so I need to learn the proper syntax, and I don't find that in
> Rick van der Lans' or Joe Celko's books I have.

?
...

WHERE
    next_contact
BETWEEN
    '01/01/2019'::date AND 'today'::date
AND
    a.next_contact IS NOT NULL
ORDER BY
    c.contact_id, a.next_contact DESC;


> 
> Looking forward to learning,
> 
> Rich
> 
> 


-- 
Adrian Klaver
adrian.klaver@aklaver.com


Re: Refining query statement

От
"David G. Johnston"
Дата:
On Thu, Jan 17, 2019 at 9:14 AM Rich Shepard <rshepard@appl-ecosys.com> wrote:
> ORDER BY c.contact_id, a.next_contact DESC;

> The WHERE clause needs to exclude a contact_id where the most current row in
> Activities has NULL for the next_contact column.

Why is next_contact allowed to be null?

Your concept of "most current row" is strictly based upon next_contact
so if next_contact is null it is impossible to know whether it is the
most current row (consider that two activities could be missing the
next_contact date - which of those is the most current?)

David J.


Re: Refining query statement

От
Adrian Klaver
Дата:
On 1/17/19 8:14 AM, Rich Shepard wrote:
> On Tue, 15 Jan 2019, Thomas Kellerer wrote:
> 
>>    select distinct on (C.contact_id) C.contact_id, C.lname, C.fname, 
>> C.direct_phone, O.org_name, A.next_contact
>>    from Contacts as C
>>      join Organizations as O on C.org_id = O.org_id
>>      join Activities as A on C.contact_id = A.contact_id
>>    where A.next_contact <= 'today'
>>      and A.next_contact > '2018-12-31'
>>    order by c.contact_id, a.next_contact DESC;
> 
> Combining this with Adrian's advice to use BETWEEN I have this statement
> that almost works:
> 
> SELECT DISTINCT ON (c.contact_id) c.contact_id, c.lname, c.fname, 
> c.direct_phone, o.org_name, a.next_contact
> FROM Contacts AS c
>    JOIN Organizations AS o ON c.org_id = o.org_id
>    JOIN Activities AS a ON c.contact_id = a.contact_id WHERE 
> next_contact BETWEEN '01/01/2019'::date AND 'today'::date ORDER BY 
> c.contact_id, a.next_contact DESC;
> 
> It fails when the most recent next_contact column in Activities is NULL and
> an earier row has a non-NULL date in the specified range.
> 
> I tried specifying max(a.next_contact) and added GROUP BY, but the result
> set all returned o.org_name columns to the same one.
> 
> The WHERE clause needs to exclude a contact_id where the most current 
> row in
> Activities has NULL for the next_contact column. I've tried a few ideas but
> none work so I need to learn the proper syntax, and I don't find that in
> Rick van der Lans' or Joe Celko's books I have.

Got to thinking more and realized the answer depends on what you want 
the query to produce. Can you let us know what is you are trying to pull 
out with the query?

> 
> Looking forward to learning,
> 
> Rich
> 
> 


-- 
Adrian Klaver
adrian.klaver@aklaver.com


Re: Refining query statement

От
Rich Shepard
Дата:
On Thu, 17 Jan 2019, David G. Johnston wrote:

> Why is next_contact allowed to be null?

David,

There are a number of reasons. The prospect might have retired, told me to
pound sand, or has put off a decision.

> Your concept of "most current row" is strictly based upon next_contact so
> if next_contact is null it is impossible to know whether it is the most
> current row (consider that two activities could be missing the
> next_contact date - which of those is the most current?)

That's quite true. I realize I've been asking the question incorrectly.

What I want is a list of contacts to make today. This includes ones that
should have been made earlier but weren't and excludes earlier contacts that
have no scheduled next contact (therefore, the nulls.).

Does this clarify what I'm asking of the data?

Thanks,

Rich



Re: Refining query statement

От
Rich Shepard
Дата:
On Thu, 17 Jan 2019, Adrian Klaver wrote:

> Got to thinking more and realized the answer depends on what you want the
> query to produce. Can you let us know what is you are trying to pull out
> with the query?

Adrian,

Certainly. Over breakfast I realized the same thing: the existing SELECT
query is not asking for the correct data.

As I replied in response to David's message, what I want is a list of
contacts to make today. This includes ones that should have been made
earlier but weren't and excludes earlier contacts that have no scheduled
next contact (therefore, the nulls.).

Thanks,

Rich



Re: Refining query statement

От
"David G. Johnston"
Дата:
On Thu, Jan 17, 2019 at 9:47 AM Rich Shepard <rshepard@appl-ecosys.com> wrote:
> What I want is a list of contacts to make today. This includes ones that
> should have been made earlier but weren't and excludes earlier contacts that
> have no scheduled next contact (therefore, the nulls.).
>
> Does this clarify what I'm asking of the data?

Yes...though now it just sounds like a flawed data model.  How stuck
are you in that regard?  Those "future" contacts should have their own
records and not be derived via an optional field on an existing
record.

Put differently, how do you know which activities are completed and
which are not?

Dave


Re: Refining query statement

От
Rich Shepard
Дата:
On Thu, 17 Jan 2019, David G. Johnston wrote:

> Yes...though now it just sounds like a flawed data model.

David,

   This is what I thought.

> How stuck are you in that regard? Those "future" contacts should have their
> own records and not be derived via an optional field on an existing
> record.

   My goal is to make a functioning business tracking application for my
consulting services. Almost all my prior postgres databases hold
environmental data for statistical and spatio-temporal analyses so writing a
business application is a new experience for me and I want to get it
correct.

> Put differently, how do you know which activities are completed and
> which are not?

   The direct answer is that a completed activity has a row with either a
future next-activity date or a null (which is the case when the status of
that organization or contact is 'no further contact'.)

   I should rename the Contacts table as People and the Activities table as
Contacts. The original names came from a sales management system I used as a
design guide, but they're probably confusing to others as well as to me. :-)

   I can provide my current schema (eight tables) to the list (perhaps as an
attachment), an individual, or put in on a cloud site and pass the URL.

Thanks,

Rich


Re: Refining query statement

От
Adrian Klaver
Дата:
On 1/17/19 9:07 AM, Rich Shepard wrote:
> On Thu, 17 Jan 2019, David G. Johnston wrote:
> 
>> Yes...though now it just sounds like a flawed data model.
> 
> David,
> 
>    This is what I thought.
> 
>> How stuck are you in that regard? Those "future" contacts should have 
>> their
>> own records and not be derived via an optional field on an existing
>> record.
> 
>    My goal is to make a functioning business tracking application for my
> consulting services. Almost all my prior postgres databases hold
> environmental data for statistical and spatio-temporal analyses so 
> writing a
> business application is a new experience for me and I want to get it
> correct.
> 
>> Put differently, how do you know which activities are completed and
>> which are not?
> 
>    The direct answer is that a completed activity has a row with either a
> future next-activity date or a null (which is the case when the status of
> that organization or contact is 'no further contact'.)

To be clear the next-activity date = next_contact in the database, correct?

NULL basically means unknown, so having it stand for something is a bit 
of a stretch. Seems to me a boolean field of name active to denote 
contacts you need to keep up with is in order. Then make the 
next_contact field NOT NULL and replace the current NULL values with 
'infinity':

https://www.postgresql.org/docs/10/datatype-datetime.html

8.5.1.4. Special Values


As to current situation:

1) Use my previous suggestion.

or

2) WHERE COALESCE(next_contact, 'infinity') BETWEEN '01/01/2019'::date 
AND 'today'::date

https://www.postgresql.org/docs/10/functions-conditional.html#FUNCTIONS-COALESCE-NVL-IFNULL

> 
>    I should rename the Contacts table as People and the Activities table as
> Contacts. The original names came from a sales management system I used 
> as a
> design guide, but they're probably confusing to others as well as to me. 
> :-)
> 
>    I can provide my current schema (eight tables) to the list (perhaps 
> as an
> attachment), an individual, or put in on a cloud site and pass the URL.
> 
> Thanks,
> 
> Rich
> 


-- 
Adrian Klaver
adrian.klaver@aklaver.com


Re: Refining query statement

От
"David G. Johnston"
Дата:
On Thu, Jan 17, 2019 at 10:07 AM Rich Shepard <rshepard@appl-ecosys.com> wrote:
>    The direct answer is that a completed activity has a row with either a
> future next-activity date or a null (which is the case when the status of
> that organization or contact is 'no further contact'.)

Off the top of my head (and this is a model I am quite familiar with
even if I'm doing this email at speed):

I'd suggest an actual activity table:

activity (
created_on date not null,
due_on date not null,
performed_on date null,
contact_id bigint not null references (contact)
);

contact (
contact_id bigserial primary key,
told_me_to_go_pound_sand boolean default false
);

Now, an exception report can be made for every contact where
"told_me_to_go_pound_sand" is false and there is no record on activity
where performed_on is null (and also pound sand is true and there is
one performed_on is null)

The report you want is basically everything on activity where
performed_on is null and due_on is today or earlier.

A unique partial index can be made (activity.contact_id) where
performed_on is null; to ensure that at most only one open activity
exists for each contact.

David J.


Re: Refining query statement

От
Rich Shepard
Дата:
On Thu, 17 Jan 2019, Adrian Klaver wrote:

> To be clear the next-activity date = next_contact in the database, correct?

Adrian,

   Yes. I've renamed the Activities table to Contacts and the Contacts table
to People.

> NULL basically means unknown, so having it stand for something is a bit of
> a stretch. Seems to me a boolean field of name active to denote contacts
> you need to keep up with is in order. Then make the next_contact field NOT
> NULL and replace the current NULL values with 'infinity':

   This makes good sense.

> As to current situation:
>
> 1) Use my previous suggestion.

   I did so, but will modify it as you suggest:
> 2) WHERE COALESCE(next_contact, 'infinity') BETWEEN '01/01/2019'::date AND 
> 'today'::date

Thanks again,

Rich


Re: Refining query statement

От
Rich Shepard
Дата:
On Thu, 17 Jan 2019, Adrian Klaver wrote:

> Seems to me a boolean field of name active to denote contacts you need to
> keep up with is in order. Then make the next_contact field NOT NULL and
> replace the current NULL values with 'infinity':

> WHERE COALESCE(next_contact, 'infinity') BETWEEN '01/01/2019'::date AND
> 'today'::date

Adrian,

   Having added a new column, 'active' with boolean values (and a default of
true), would this WHERE clause work:

WHERE active = TRUE and next_contact <= 'today'::date

?

Regards,

Rich


Re: Refining query statement

От
Rich Shepard
Дата:
On Thu, 17 Jan 2019, David G. Johnston wrote:

> Off the top of my head (and this is a model I am quite familiar with
> even if I'm doing this email at speed):
>
> I'd suggest an actual activity table:

David,

   Adrian's suggestion of a Contacts table column called 'active' having a
boolean data type seems to me to be more compact. Testing active = true as a
condition in the WHERE clause will display only those People with active
contact needs.

Thanks very much,

Rich


Re: Refining query statement

От
Adrian Klaver
Дата:
On 1/17/19 10:01 AM, Rich Shepard wrote:
> On Thu, 17 Jan 2019, Adrian Klaver wrote:
> 
>> Seems to me a boolean field of name active to denote contacts you need to
>> keep up with is in order. Then make the next_contact field NOT NULL and
>> replace the current NULL values with 'infinity':
> 
>> WHERE COALESCE(next_contact, 'infinity') BETWEEN '01/01/2019'::date AND
>> 'today'::date
> 
> Adrian,
> 
>    Having added a new column, 'active' with boolean values (and a 
> default of
> true), would this WHERE clause work:
> 
> WHERE active = TRUE and next_contact <= 'today'::date

It would produce results, so yes it would work.

The question are they the results you want?

The above would return anything with a next_contact less then today. 
That could extend backwards to some undetermined point in the past, 
depending on how diligent you where on updating next_contact. Before you 
go much further I would look over the suggestions posted and then sit 
down with pen and paper and figure out what it you want to track and how 
you are going to maintain that tracking. Then draw up an outline of how 
you will achieve that in code.

> 
> ?
> 
> Regards,
> 
> Rich
> 


-- 
Adrian Klaver
adrian.klaver@aklaver.com


Re: Refining query statement

От
Rich Shepard
Дата:
On Thu, 17 Jan 2019, Adrian Klaver wrote:

> It would produce results, so yes it would work. The question are they the
> results you want?

Adrian,

   To which the answer is no as I just discovered.

> The above would return anything with a next_contact less then today. That
> could extend backwards to some undetermined point in the past, depending
> on how diligent you where on updating next_contact. Before you go much
> further I would look over the suggestions posted and then sit down with
> pen and paper and figure out what it you want to track and how you are
> going to maintain that tracking. Then draw up an outline of how you will
> achieve that in code.

   I have that tracking defined (who is active and needs a contact today or
earlier) and am now working on how to code it so only the last contact for a
person is examined to determine if the active column = true. I'll ponder
that.

Thanks,

Rich



Re: Refining query statement

От
Rich Shepard
Дата:
On Thu, 17 Jan 2019, Adrian Klaver wrote:

> Got to thinking more and realized the answer depends on what you want the
> query to produce. Can you let us know what is you are trying to pull out
> with the query?

Adrian, et al.,

Took your advice and re-thought what I need the query to return. This
allowed me to realize that I don't need a separate contact history query as
I can incorporate it in a single query. The goal and pseudocode are:

Purpose: List all contact information and contact history for active people
where next contact date is less than today.

For each person select person_id, lname, fname, and direct_phone from People.

For each person get the org_name from Organizations.

For each person get contact history in date order and next contact date from
Contacts where active = True.

Order by next contact dates in ascending order.

Query code:

SELECT p.person_id, p.lname, p.fname, p.direct_phone, o.org_name, c.cont_date,
        c.cont_type, c.notes, c.next_contact, c.comment
FROM People AS p
      JOIN Organizations AS o ON p.org_id = o.org_id
      JOIN Contacts AS c ON c.person_id = p.person_id
WHERE c.active = TRUE AND c.next_contact <= 'today'::date
GROUP BY o.org_name, p.person_id, c.person_id, c.cont_date, c.cont_type,
       c.next_contact
ORDER BY p.person_id, c.next_contact

The results are not correctly grouped or ordered; I'll work on fixing these
issues.

The other issue that needs fixing is identifying the most recent 'active'
value in the Contacts table for each person_id and including that person in
the results only when active = 't'. Here's a MWE of a redacted set of rows
that should not have been returned by the query:

  person_id | cont_date  | cont_type  |
                              notes
                           | active | next_contact | comment 
--------------------------+--------+--------------+---------
         36 | 2018-12-12 | Conference | Notes on this conversation.
                         | t      | 2018-12-17   |
         36 | 2018-12-17 | Phone      | Left message asking for a meeting.
                           | t      | 2019-01-03   |
         36 | 2019-01-03 | Phone      | Left another message.
                           | t      | 2019-01-07   |
         36 | 2019-01-07 | Phone      | Going into a meeting.
                           | t      | 2019-01-14   |
         36 | 2019-01-15 | Phone      | Will call when/if.
                           | f      | infinity     | 
(5 rows)

Because the most recent row's active value is 'f' these rows should not be
included as there is no next contact date. I'll keep reading looking for
this answer and pointers will be helpful.

Regards,

Rich


Re: Refining query statement

От
Adrian Klaver
Дата:
On 1/17/19 2:44 PM, Rich Shepard wrote:
> On Thu, 17 Jan 2019, Adrian Klaver wrote:
> 
>> Got to thinking more and realized the answer depends on what you want the
>> query to produce. Can you let us know what is you are trying to pull out
>> with the query?
> 
> Adrian, et al.,
> 
> Took your advice and re-thought what I need the query to return. This
> allowed me to realize that I don't need a separate contact history query as
> I can incorporate it in a single query. The goal and pseudocode are:
> 
> Purpose: List all contact information and contact history for active people
> where next contact date is less than today.
> 
> For each person select person_id, lname, fname, and direct_phone from 
> People.
> 
> For each person get the org_name from Organizations.
> 
> For each person get contact history in date order and next contact date 
> from
> Contacts where active = True.
> 
> Order by next contact dates in ascending order.
> 
> Query code:
> 
> SELECT p.person_id, p.lname, p.fname, p.direct_phone, o.org_name, 
> c.cont_date,
>         c.cont_type, c.notes, c.next_contact, c.comment
> FROM People AS p
>       JOIN Organizations AS o ON p.org_id = o.org_id
>       JOIN Contacts AS c ON c.person_id = p.person_id
> WHERE c.active = TRUE AND c.next_contact <= 'today'::date
> GROUP BY o.org_name, p.person_id, c.person_id, c.cont_date, c.cont_type,
>        c.next_contact
> ORDER BY p.person_id, c.next_contact
> 
> The results are not correctly grouped or ordered; I'll work on fixing these
> issues.
> 
> The other issue that needs fixing is identifying the most recent 'active'
> value in the Contacts table for each person_id and including that person in
> the results only when active = 't'. Here's a MWE of a redacted set of rows
> that should not have been returned by the query:
> 
>   person_id | cont_date  | cont_type  |
>                               notes
>                            | active | next_contact | comment 
> --------------------------+--------+--------------+---------
>          36 | 2018-12-12 | Conference | Notes on this conversation.
>                          | t      | 2018-12-17   |
>          36 | 2018-12-17 | Phone      | Left message asking for a meeting.
>                            | t      | 2019-01-03   |
>          36 | 2019-01-03 | Phone      | Left another message.
>                            | t      | 2019-01-07   |
>          36 | 2019-01-07 | Phone      | Going into a meeting.
>                            | t      | 2019-01-14   |
>          36 | 2019-01-15 | Phone      | Will call when/if.
>                            | f      | infinity     | (5 rows)
> 
> Because the most recent row's active value is 'f' these rows should not be
> included as there is no next contact date. I'll keep reading looking for
> this answer and pointers will be helpful.

I would think the active would be on People or Organizations. Then you 
can eliminate then from the query results before you ever got to the 
contact history.

Also not sure how you the above result when the query showed:

WHERE c.active = TRUE AND c.next_contact <= 'today'::date

Or did the history results come from an undisclosed query?

> 
> Regards,
> 
> Rich
> 
> 


-- 
Adrian Klaver
adrian.klaver@aklaver.com


Re: Refining query statement

От
Rich Shepard
Дата:
On Thu, 17 Jan 2019, Adrian Klaver wrote:

> I would think the active would be on People or Organizations. Then you can
> eliminate then from the query results before you ever got to the contact
> history.

Adrian,

Excellent point. I don't know why I put active in the contacts table as it
does make more sense to put it in the people table (as some clients have
multiple sites, each with its own contact person).

> Also not sure how you the above result when the query showed:
> WHERE c.active = TRUE AND c.next_contact <= 'today'::date
> Or did the history results come from an undisclosed query?

I don't recall which query I used for the MWE. I'll move the active column
to the new table, as well as those values, and keep working on learning how
to formulate complex, multi-table queries.

Also, moving the active table to peoples, where there is only one row per
person (rather than multiple contact rows per person) removes the need to
find only the most recent row in the many of the one-to-many relationship.

Thanks for the advice,

Rich



Re: Refining query statement

От
"David G. Johnston"
Дата:
On Thu, Jan 17, 2019 at 3:44 PM Rich Shepard <rshepard@appl-ecosys.com> wrote:
> FROM People AS p
>       JOIN Organizations AS o ON p.org_id = o.org_id
>       JOIN Contacts AS c ON c.person_id = p.person_id

I would advise changing Contacts to "Activities" as the former can
readily be interpreted (and is in the wild) as both "an instance of
contacting a person" and "the person at the organization who is being
contacted" (i.e., your People class).

David J.


Re: Refining query statement

От
Rich Shepard
Дата:
On Thu, 17 Jan 2019, David G. Johnston wrote:

> I would advise changing Contacts to "Activities" as the former can readily
> be interpreted (and is in the wild) as both "an instance of contacting a
> person" and "the person at the organization who is being contacted" (i.e.,
> your People class).

David,

   That's why I originally named the Contacts table Activities and the Peoples table
as Contacts. :-) I'll change it back again since there's now a Peoples
table.

Many thanks!

Rich



Re: Refining query statement [DONE]

От
Rich Shepard
Дата:
On Tue, 15 Jan 2019, Rich Shepard wrote:

> Working with my sales/client management system using psql I have a select
> statement to identify contacts to be made. This statement works:

With much patient advice from Adrian, David, Thomas, and Ron I fixed the
schema and the query statement. To close this thread I post the query that
meets my goal and provide the information I need. Formatting this as a
report will be the job of SQLAlchemy and wxPython.

/* This query selects all contact information and notes from those people
    whose next_contact date is today or earlier; only active persons. */

/* Specify columns */
SELECT p.lname, p.fname, p.direct_phone, p.active, o.org_name,
        a.act_date, a.act_type, a.notes, a.next_contact, a.comment
/* Specify tables. */ 
FROM People AS p
      JOIN Organizations AS o ON o.org_id = p.org_id
      JOIN Activities AS a ON a.person_id = p.person_id
/* Specify rows */ 
WHERE p.active = TRUE AND
       a.next_contact <= 'today'::date
GROUP BY o.org_name, p.person_id, a.person_id, a.act_date, a.act_type,
       a.next_contact
ORDER BY p.person_id, a.next_contact DESC;

I really appreciate your help.

Best regards,

Rich