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