I have two queries:
SELECT
users.id,
users.gender,
users.status,
users.icon_id,
users.image_name,
coords.lat,
coords.lng,
users.mess_count
FROM
users
INNER JOIN
coords ON users.id = coords.user_id
then I select blocked users:
SELECT
first_user,
second_user
FROM
blocks
WHERE
first_user = $1 OR second_user = $1
From first table I need to select all users which has coordinates and not blocked, I also need some public information(gender and etc.). Then because I need two side blocking. I have to select is user blocked him, or he was blocked by that user. So $1 is current user, and I select is my id in block
table, if it is - I exclude another user from first query.
Then using string operations in my programming language I transform my string to exclude results I get from second query.
I probably can do it with EXCEPT
, but I can't do it, because I have only 2 column selected with second query, and I need much more, in final result: users.id, users.gender, users.status, users.icon_id, users.image_name, coords.lat, coords.lng, users.mess_count
.
There are several ways to do it, the only mildly compromising factor is that I believe you want to exclude users that appear in either of two columns in the blocks
table.
SQL traditionally has weaker performance with OR
logic, which the following query attempts to work around. (In part due to its ability to make better use of indexes)
SELECT
users.id,
users.gender,
users.status,
users.icon_id,
users.image_name,
coords.lat,
coords.lng,
users.mess_count
FROM
users
INNER JOIN
coords
ON users.id=coords.user_id
WHERE
NOT EXISTS (SELECT * FROM blocks WHERE first_user = users.id AND second_user = $1)
AND NOT EXISTS (SELECT * FROM blocks WHERE second_user = users.id AND first_user = $1)
Depending on the version of PostgreSQL, the optimiser may be less efficient with correlated sub-queries, such as those I have used above. In such a case, the following may be more performant still. (It still avoids using OR.)
SELECT
users.id,
users.gender,
users.status,
users.icon_id,
users.image_name,
coords.lat,
coords.lng,
users.mess_count
FROM
users
INNER JOIN
coords
ON users.id=coords.user_id
LEFT JOIN
(
SELECT first_user AS user_id FROM blocks WHERE second_user = $1
UNION
SELECT second_user AS user_id FROM blocks WHERE first_user = $1
)
AS blocks
ON blocks.users_id = users.id
WHERE
blocks.user_id IS NULL