0

Here is my problem: I have a table that stores the results of surveys taken by users. A single user can take multiple surveys. The key columns are: user_id, the user identifier and survey_id, the unique identifier of the survey. survey_id is incremented each time a survey is taken so if I query a specific user_id, order by survey_id descending and limit to top 4 rows I can get the last 4 surveys for a given user. My problem is I would like to query the last 4 surveys for all users in the table. I'm stumped on how to do this but this is what I have so far:

SELECT *
FROM
 (
SELECT user_id
FROM
(
    SELECT
        user_id, count(all_survey_res_id) as numsurveys
    FROM
        all_survey_res
    GROUP BY user_id
    ORDER BY count(all_survey_res_id) DESC
) AS T1
WHERE numsurveys >= 4
)
ORDER BY user_id, all_survey_res_id

This gives me all of the records for each user that has more than 4 surveys but I then want to limit the result to just those top 4 surveys. I could solve this with code back in the application but I would rather see if I can just get the query to do this.

3
  • Can you provide some sample output? Do you want each row to be user_id and an array of their highest all_survey_res_id? Commented Nov 18, 2013 at 20:32
  • I was able to find part of the answer on another question note I'm parroting the use of OVER and PARTITION BY so if somebody could explain how those work (The postgres documentation is confusing) SELECT * FROM ( SELECT *, row_number() OVER (PARTITION BY user_id ORDER BY all_survey_res_id DESC) AS rn FROM all_survey_res ) AS T1, ( SELECT user_id, count(all_survey_res_id) as numsurveys FROM all_survey_res GROUP BY user_id ORDER BY count(all_survey_res_id) DESC ) AS T2 WHERE T1.rn <= 4 AND T2.numsurveys >= 4 AND T1.user_id = T2.user_id Commented Nov 18, 2013 at 20:47
  • @Johnathan - I think I found my answer (but couldn't get the code to format) - I was looking for last 4 rows out of the database for each user_id. But again - I found my solution! Commented Nov 18, 2013 at 20:52

1 Answer 1

1

I think you can do this with window functions:

select
    *
from (
    select
        user_id,
        survey_id,
        row_number() over (partition by user_id order by survey_id desc) rn
    from
        all_survey_res
    ) x
where
    x.rn <= 4
2
  • That is basically the solution I went with - can you help me understand how the OVER and PARTITION BY work? I tried the postgres documentation but it confused me more than it helped.... Commented Nov 18, 2013 at 20:56
  • just create some small sets of data and run the inner query. For each user id, it creates a sequence of 1, 2, 3, 4, ... against the rows, in descending order of survey_id. Commented Nov 18, 2013 at 21:09

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.