PostgreSQL
 sql >> Teknologi Basis Data >  >> RDS >> PostgreSQL

Bagaimana cara menghindari beberapa evaluasi fungsi dengan sintaks (func()).* dalam kueri SQL?

Anda dapat membungkusnya dalam subkueri tetapi itu tidak dijamin aman tanpa OFFSET 0 meretas Di 9.3, gunakan LATERAL . Masalahnya disebabkan oleh parser yang secara efektif memperluas makro * ke dalam daftar kolom.

Solusi

Dimana:

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

akan mengevaluasi my_func n kali untuk n kolom hasil dari fungsi, formulasi ini:

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

umumnya tidak akan, dan cenderung tidak menambahkan scan tambahan saat runtime. Untuk menjamin bahwa beberapa evaluasi tidak akan dilakukan, Anda dapat menggunakan OFFSET 0 meretas atau menyalahgunakan kegagalan PostgreSQL untuk mengoptimalkan lintas batas CTE:

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

atau:

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

Di PostgreSQL 9.3 Anda dapat menggunakan LATERAL untuk mendapatkan perilaku yang lebih waras:

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

LEFT JOIN LATERAL ... ON true mempertahankan semua baris seperti kueri asli, bahkan jika panggilan fungsi tidak mengembalikan baris.

Demo

Buat fungsi yang tidak sebaris sebagai demonstrasi:

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;

dan tabel data dummy:

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

kemudian coba versi di atas. Anda akan melihat bahwa yang pertama memunculkan tiga pemberitahuan per doa; yang terakhir hanya meningkatkan satu.

Mengapa?

Pertanyaan bagus. Mengerikan.

Sepertinya:

(func(x)).*

diperluas sebagai:

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

dalam penguraian, menurut lihat debug_print_parse , debug_print_rewritten dan debug_print_plan . Pohon parse (dipangkas) terlihat seperti ini:

   :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 
       ...
      }
   )

Jadi pada dasarnya, kami menggunakan peretasan parser bodoh untuk memperluas wildcard dengan mengkloning node.




  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. Bagaimana cara memilih id dengan grup tanggal maksimum berdasarkan kategori di PostgreSQL?

  2. Bagaimana saya bisa menambahkan kolom yang tidak mengizinkan nol dalam database Postgresql?

  3. Bagaimana cara mengetahui apakah upsert adalah pembaruan dengan PostgreSQL 9.5+ UPSERT?

  4. Kolom PostgreSQL foo tidak ada di mana foo adalah nilainya

  5. Bagaimana cara saya menulis data dari tabel R ke PostgreSQL dengan kunci utama peningkatan otomatis?