SQL function very slow compared to query without function wrapper

前端 未结 2 1425
难免孤独
难免孤独 2020-12-20 15:43

I have this PostgreSQL 9.4 query that runs very fast (~12ms):

SELECT 
  auth_web_events.id, 
  auth_web_events.time_stamp, 
  auth_web_events.description, 
          


        
相关标签:
2条回答
  • 2020-12-20 16:21

    You will get better performance by making this query dynamic and using plpgsql.

    CREATE OR REPLACE FUNCTION get_web_events_by_userid(uid int) RETURNS TABLE(
        id int,
        time_stamp timestamp with time zone,
        description text,
        origin text,
        userlogin text,
        customer text,
        client_ip inet
         ) AS $$
    BEGIN
    
    RETURN QUERY EXECUTE
    'SELECT 
      auth_web_events.id, 
      auth_web_events.time_stamp, 
      auth_web_events.description, 
      auth_web_events.origin,  
      auth_user.email AS user, 
      customers.name AS customer,
      auth_web_events.client_ip
    FROM 
      public.auth_web_events, 
      public.auth_user, 
      public.customers
    WHERE 
      auth_web_events.user_id_fk = auth_user.id AND
      auth_user.customer_id_fk = customers.id AND
      auth_web_events.user_id_fk = ' || uid ||
    'ORDER BY
      auth_web_events.id DESC;'
    
    END;
    $$ LANGUAGE plpgsql;
    
    0 讨论(0)
  • 2020-12-20 16:43

    user

    While rewriting your function I realized that you added column aliases here:

    SELECT 
      ...
      auth_user.email AS user, 
      customers.name AS customer,

    .. which wouldn't do anything to begin with, since those aliases are invisible outside the function and not referenced inside the function. So they would be ignored. For documentation purposes better use a comment.

    But it also makes your query invalid, because user is a completely reserved word and cannot be used as column alias unless double-quoted.

    Oddly, in my tests the function seems to work with the invalid alias. Probably because it is ignored (?). But I am not sure this couldn't have side effects.

    Your function rewritten (otherwise equivalent):

    CREATE OR REPLACE FUNCTION get_web_events_by_userid(int)
      RETURNS TABLE(
         id int
       , time_stamp timestamptz
       , description text
       , origin text
       , userlogin text
       , customer text
       , client_ip inet
      ) AS
    $func$
    SELECT w.id
         , w.time_stamp
         , w.description 
         , w.origin  
         , u.email     -- AS user   -- make this a comment!
         , c.name      -- AS customer
         , w.client_ip
    FROM   public.auth_user       u
    JOIN   public.auth_web_events w ON w.user_id_fk = u.id
    JOIN   public.customers       c ON c.id = u.customer_id_fk 
    WHERE  u.id = $1   -- reverted the logic here
    ORDER  BY w.id DESC
    $func$ LANGUAGE sql STABLE;
    

    Obviously, the STABLE keyword changed the outcome. Function volatility should not be an issue in the test situation you describe. The setting does not normally profit a single, isolated function call. Read details in the manual. Also, standard EXPLAIN does not show query plans for what's going on inside functions. You could employ the additional module auto-explain for that:

    • Postgres query plan of a UDF invocation written in pgpsql

    You have a very odd data distribution:

    auth_web_events table has 100000000 records, auth_user->2 records, customers-> 1 record

    Since you didn't define otherwise, the function assumes an estimate of 1000 rows to be returned. But your function is actually returning only 2 rows. If all your calls only return (in the vicinity of) 2 rows, just declare that with an added ROWS 2. Might change the query plan for the VOLATILE variant as well (even if STABLE is the right choice anyway here).

    0 讨论(0)
提交回复
热议问题