I've got myself in a little pickle with an SQL query that is a bit out of my experience.
Here's the two tables I'm using:
Table "public.Vote"
Column | Type | Collation | Nullable | Default
-----------+--------------------------------+-----------+----------+------------------------------------
id | integer | | not null | nextval('"Vote_id_seq"'::regclass)
createdAt | timestamp(3) without time zone | | not null | CURRENT_TIMESTAMP
serverId | integer | | not null |
Indexes:
"Vote_pkey" PRIMARY KEY, btree ("createdAt")
"Vote.id_unique" UNIQUE, btree (id)
Foreign-key constraints:
"Vote_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"(id) ON UPDATE CASCADE ON DELETE CASCADE
"Vote_serverId_fkey" FOREIGN KEY ("serverId") REFERENCES "Server"(id) ON UPDATE CASCADE ON DELETE CASCADE
Table "public.Server"
Column | Type | Collation | Nullable | Default
-----------+--------------------------------+-----------+----------+--------------------------------------
id | integer | | not null | nextval('"Server_id_seq"'::regclass)
title | text | | not null |
content | text | | |
createdAt | timestamp(3) without time zone | | not null | CURRENT_TIMESTAMP
Indexes:
"Server_pkey" PRIMARY KEY, btree (id)
Foreign-key constraints:
"Server_authorId_fkey" FOREIGN KEY ("authorId") REFERENCES "User"(id) ON UPDATE CASCADE ON DELETE CASCADE
"Server_versionId_fkey" FOREIGN KEY ("versionId") REFERENCES "Version"(id) ON UPDATE CASCADE ON DELETE CASCADE
Referenced by:
TABLE ""Vote"" CONSTRAINT "Vote_serverId_fkey" FOREIGN KEY ("serverId") REFERENCES "Server"(id) ON UPDATE CASCADE ON DELETE CASCADE
TABLE ""_ServerToTag"" CONSTRAINT "_ServerToTag_A_fkey" FOREIGN KEY ("A") REFERENCES "Server"(id) ON UPDATE CASCADE ON DELETE CASCADE
Here's how my current query looks:
SELECT s.id, s.title, s.content, count("serverId" WHERE "createdAt" >= '2020-01-01 00:00:00') AS "voteCount" FROM "Server" AS s LEFT JOIN "Vote" AS v ON (s.id = "serverId") GROUP BY s.id ORDER BY "voteCount" DESC;
At the moment the above query does this:
- Selects all entries in "Server"
- Counts how many entries in "Vote" each "Server" has
- Orders the output data by "voteCount"
It's working pretty well, but now I would like to add an extra layer of complexity to it and only count votes made after a certain date.
So I came up with this query:
SELECT s.id, s.title, s.content, count("serverId") AS "voteCount" FROM "Server" AS s LEFT JOIN "Vote" AS v ON (s.id = "serverId") WHERE v."createdAt" >= '2020-01-01 00:00:00' GROUP BY s.id ORDER BY "voteCount" DESC;
While the above technically does work, it's also not outputting rows in "Server" that have no rows in "Vote". Is there any way to get those rows as well?
Best Answer
So this is happening because even though you used a
LEFT JOIN
, you're filtering on a field in the right-side table of thatLEFT JOIN
in the part of your query that doesWHERE v."createdAt" >= '2020-01-01 00:00:00'
so this removes all the records in your left-side table "Server" where there are no rows it could join to in the "Vote" table (i.e. where createdAt is null).You can add this additional condition in your WHERE predicate to fix that: