Search code examples
sqlpostgresqlhaving

How to apply HAVING to a alias


I have query that creates a column on the fly like so:

SELECT 
v_list_client.id AS "id",
EXISTS(
  SELECT id FROM message 
  WHERE to_phone_number = v_list_client."phoneNumber" 
    AND  created_by_agent_id != 
  (SELECT id FROM agent WHERE agent_role_id = (
    SELECT id FROM agent_role WHERE label = 'Bot'))
) AS "hasReceivedMessageFromAgent",
*
FROM v_list_client
LEFT JOIN v_form
ON (v_form."client" ->> 'id')::integer = id
WHERE (v_form."formTypeId")::integer = 1
AND (v_form."isReceived")::boolean = true
HAVING "hasReceivedMessageFromAgent" = true

I'd like for the HAVING clause to only return results if the client has received a message from an agent, i.e if the hasReceivedMessageFromAgent column is true.


Solution

  • HAVING is only applicable after GROUP BY. But you also cannot refer to output columns in the WHERE clause. (That's what made you bark up the wrong tree.) See:

    Drop the column "hasReceivedMessageFromAgent" from the result. By definition, it would always be true and hence noise. You really want to use the expression as filter in another WHERE clause.

    I suspect more problems in your query. In particular the subquery on agent looks like it could return multiple rows, which would raise an exception. See:

    Consider this rewrite:

    SELECT *
    FROM   v_list_client l
    JOIN   v_form        f ON (f.client ->> 'id')::int = l.id  -- was a fake LEFT JOIN
    WHERE  f."formTypeId"::int = 1  -- why the need to cast?
    AND    f."isReceived"::boolean  -- why the need to cast?
    AND    EXISTS (
       SELECT FROM message m
       LEFT   JOIN (agent a JOIN agent_role ar ON ar.label = 'Bot' AND ar.id = a.agent_role_id) ON a.id = m.created_by_agent_id
       WHERE  m.to_phone_number = l."phoneNumber" 
       AND    a.id IS NULL
       );
    

    About the fake LEFT JOIN, See:

    "formTypeId" should already be integer.
    "isReceived" should already be boolean.
    The cast for either is expensive nonsense. If the values are always valid, the columns should have the proper data type already. If not, the cast would fail.

    Aside: If at all possible, use legal, lower-case, unquoted identifiers in Postgres. See: