2

I have a table with people and another with visits. I want to count all visits but if the person signed up with 'emp' or 'oth' on ref_signup then remove the first visit. Example:

This are my tables:

PEOPLE:
   id    |  ref_signup
---------------------
   20    |   emp
   30    |   oth
   23    |   fri

VISITS
  id     |   date
-------------------------
  20     |   10-01-2019
  20     |   10-05-2019
  23     |   10-09-2019
  23     |   10-10-2019
  30     |   09-10-2019
  30     |   10-07-2019

On this example the visit count should be 4 because persons with id's 20 and 30 have their ref_signup as emp or oth, so it should exclude their first visit, but count from the second and forward.

This is what I have as a query:

SELECT COUNT(*) as visit_count FROM visits
LEFT JOIN people ON people.id = visits.people_id
WHERE visits.group_id = 1

Would using a case on the count help on this case as I just want to remove one visit not all of the visits from the person.

1
  • Your example data doesn't have group_id and people_id? Commented Oct 24, 2019 at 19:57

5 Answers 5

6

Subtract from COUNT(*) the distinct number of person.ids with person.ref_signup IN ('emp', 'oth'):

 SELECT 
  COUNT(*) - 
  COUNT(DISTINCT CASE WHEN p.ref_signup IN ('emp', 'oth') THEN p.id END) as visit_count 
FROM visits v LEFT JOIN people p
ON p.id = v.id

See the demo.
Result:

| visit_count |
| ----------- |
| 4           |

Note: this code and demo fiddle use the column names of your sample data.

Sign up to request clarification or add additional context in comments.

Comments

1

Premise, select the count of visits from each person, along with a synthetic column that contains a 1 if the referral was from emp or oth, a 0 otherwise. Select the sum of the count minus the sum of that column.

SELECT SUM(count) - SUM(ignore_first) FROM  (SELECT COUNT(*) as count, CASE WHEN ref_signup in ('emp', 'oth') THEN 1 ELSE 0 END as ignore_first as visit_count FROM visits
LEFT JOIN people ON people.id = visits.people_id
WHERE visits.group_id = 1 GROUP BY id) a

Comments

1

where's "people_id" in your example ?

SELECT COUNT(*) as visit_count 
FROM visits v
JOIN people p ON p.id = v.people_id 
WHERE p.ref_signup IN ('emp','oth');

then remove the first visit.

You cannot select count and delete the first visit at same time.

DELETE FROM visits 
  WHERE id IN (
      SELECT id 
      FROM visits v
      JOIN people p ON p.id = v.people_id 
      WHERE p.ref_signup IN ('emp','oth')
      ORDER BY v.id
      LIMIT 1
);

edit: typos

Comments

1

First, I create the tables

create table people (id int primary key, ref_signup varchar(3));
insert into people (id, ref_signup) values (20, 'emp'), (30, 'oth'), (23, 'fri');
create table visits (people_id int not null, visit_date date not null);
insert into visits (people_id, visit_date) values (20, '10-01-2019'), (20, '10-05-2019'), (23, '10-09-2019'), (23, '10-10-2019'), (30, '09-10-2019'), (30, '10-07-2019');

You can use the row_number() window function to mark which visit is "visit number one":

select
  *,
  row_number() over (partition by people_id order by visit_date) as visit_num
from people
join visits
  on people.id = visits.people_id

Once you have that, you can do another query on those results, and use the filter clause to count up the correct rows that match the condition where visit_num > 1 or ref_signup = 'fri':

-- wrap the first query in a WITH clause
with joined_visits as (
  select
    *,
    row_number() over (partition by people_id order by visit_date) as visit_num
  from people
  join visits
    on people.id = visits.people_id
)
select count(1) filter (where visit_num > 1 or ref_signup = 'fri')
from joined_visits;

Comments

1
-- First get the corrected counts for all users
WITH grouped_visits AS (
  SELECT
    COUNT(visits.*) -
      CASE WHEN people.ref_signup IN ('emp', 'oth') THEN 1 ELSE 0 END
      AS visit_count
  FROM visits
    INNER JOIN people ON (people.id = visits.id)
  GROUP BY people.id, people.ref_signup
)
-- Then sum them
SELECT SUM(visit_count)
FROM grouped_visits;

This should give you the result you're looking for.


On a side note, I can't help but think clever use of a window function could do this in a single shot without the CTE.

EDIT: No, it can't since window functions run after needed WHERE and GROUP BY and HAVING clauses.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.