PostgreSQL
 sql >> Cơ Sở Dữ Liệu >  >> RDS >> PostgreSQL

Làm cách nào để tránh nhiều hàm ẩn với cú pháp (func ()). * Trong truy vấn SQL?

Bạn có thể gói nó trong một truy vấn con nhưng điều đó không được đảm bảo an toàn nếu không có OFFSET 0 gian lận. Trong 9.3, sử dụng LATERAL . Sự cố là do trình phân tích cú pháp mở rộng hiệu quả macro * vào danh sách cột.

Giải pháp thay thế

Ở đâu:

SELECT (my_func(x)).* FROM some_table;

sẽ đánh giá my_func n thời gian cho n cột kết quả từ hàm, công thức này:

SELECT (mf).* FROM (
    SELECT my_func(x) AS mf FROM some_table
) sub;

nói chung sẽ không và có xu hướng không thêm một lần quét bổ sung trong thời gian chạy. Để đảm bảo rằng nhiều lần đánh giá sẽ không được thực hiện, bạn có thể sử dụng OFFSET 0 hack hoặc lạm dụng PostgreSQL không thể tối ưu hóa qua các ranh giới CTE:

SELECT (mf).* FROM (
    SELECT my_func(x) AS mf FROM some_table OFFSET 0
) sub;

hoặc:

WITH tmp(mf) AS (
    SELECT my_func(x) FROM some_table
)
SELECT (mf).* FROM tmp;

Trong PostgreSQL 9.3, bạn có thể sử dụng LATERAL để có được hành vi tốt hơn:

SELECT mf.*
FROM some_table
LEFT JOIN LATERAL my_func(some_table.x) AS mf ON true;

LEFT JOIN LATERAL ... ON true giữ lại tất cả các hàng giống như truy vấn ban đầu, ngay cả khi lệnh gọi hàm không trả về hàng nào.

Bản trình diễn

Tạo một hàm không thể nội dòng như một trình diễn:

CREATE OR REPLACE FUNCTION my_func(integer)
RETURNS TABLE(a integer, b integer, c integer) AS $$
BEGIN
    RAISE NOTICE 'my_func(%)',$1;
    RETURN QUERY SELECT $1, $1, $1;
END;
$$ LANGUAGE plpgsql;

và một bảng dữ liệu giả:

CREATE TABLE some_table AS SELECT x FROM generate_series(1,10) x;

sau đó thử các phiên bản trên. Bạn sẽ thấy rằng điều đầu tiên nêu ra ba thông báo cho mỗi lần gọi; cái sau chỉ nâng một cái.

Tại sao?

Câu hỏi hay. Thật kinh khủng.

Nó trông giống như:

(func(x)).*

được mở rộng thành:

(my_func(x)).i, (func(x)).j, (func(x)).k, (func(x)).l

trong phân tích cú pháp, theo một cái nhìn tại debug_print_parse , debug_print_rewrittendebug_print_plan . Cây phân tích cú pháp (đã được cắt tỉa) trông như thế này:

   :targetList (
      {TARGETENTRY 
      :expr 
         {FIELDSELECT 
         :arg 
            {FUNCEXPR 
            :funcid 57168 
                 ...
            }
         :fieldnum 1 
         :resulttype 23 
         :resulttypmod -1 
         :resultcollid 0
         }
      :resno 1 
      :resname i 
       ...
      }
      {TARGETENTRY 
      :expr 
         {FIELDSELECT 
         :arg 
            {FUNCEXPR 
            :funcid 57168 
                 ...
            }
         :fieldnum 2 
         :resulttype 20 
         :resulttypmod -1 
         :resultcollid 0
         }
      :resno 2 
      :resname j 
       ...
      }
      {TARGETENTRY 
      :expr 
         {FIELDSELECT 
         :arg 
            {FUNCEXPR 
            :funcid 57168 
             ...
            }
         :fieldnum 3 
         :...
         }
      :resno 3 
      :resname k 
       ...
      }
      {TARGETENTRY 
      :expr 
         {FIELDSELECT 
         :arg 
            {FUNCEXPR 
            :funcid 57168 
             ...
            }
         :fieldnum 4 
          ...
         }
      :resno 4 
      :resname l 
       ...
      }
   )

Vì vậy, về cơ bản, chúng tôi đang sử dụng một bản hack phân tích cú pháp ngu ngốc để mở rộng các ký tự đại diện bằng cách sao chép các nút.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Làm thế nào để trả về kết quả của một SELECT bên trong một hàm trong PostgreSQL?

  2. Khi nào sử dụng bảng kế thừa trong PostgreSQL?

  3. Truy vấn ILIKE PostgreSQL với SQLAlchemy

  4. Sự khác biệt giữa LATERAL JOIN và một truy vấn con trong PostgreSQL là gì?

  5. Trình điều khiển PostgreSQL 9.2 JDBC sử dụng múi giờ máy khách?