Bulan lalu saya memposting tantangan untuk membuat generator seri angka yang efisien. Tanggapannya luar biasa. Ada banyak ide dan saran yang brilian, dengan banyak aplikasi yang melampaui tantangan khusus ini. Itu membuat saya menyadari betapa hebatnya menjadi bagian dari sebuah komunitas, dan bahwa hal-hal luar biasa dapat dicapai ketika sekelompok orang pintar bergabung. Terima kasih Alan Burstein, Joe Obbish, Adam Machanic, Christopher Ford, Jeff Moden, Charlie, NoamGr, Kamil Kosno, Dave Mason, dan John Number2 untuk berbagi ide dan komentar Anda.
Awalnya saya berpikir untuk menulis satu artikel saja untuk merangkum ide-ide yang diajukan orang, tapi ternyata terlalu banyak. Jadi saya akan membagi liputan menjadi beberapa artikel. Bulan ini saya akan fokus terutama pada perbaikan yang disarankan Charlie dan Alan Burstein pada dua solusi asli yang saya posting bulan lalu dalam bentuk TVF inline yang disebut dbo.GetNumsItzikBatch dan dbo.GetNumsItzik. Saya akan menamai versi yang ditingkatkan masing-masing dbo.GetNumsAlanCharlieItzikBatch dan dbo.GetNumsAlanCharlieItzik.
Ini sangat menarik!
Solusi orisinal Itzik
Sebagai pengingat cepat, fungsi yang saya bahas bulan lalu menggunakan CTE dasar yang mendefinisikan konstruktor nilai tabel dengan 16 baris. Fungsi menggunakan serangkaian cascading CTE, masing-masing menerapkan produk (cross join) dari dua contoh CTE sebelumnya. Dengan cara ini, dengan lima CTE di luar basis, Anda bisa mendapatkan satu set hingga 4.294.967.296 baris. CTE yang disebut Nums menggunakan fungsi ROW_NUMBER untuk menghasilkan serangkaian angka yang dimulai dengan 1. Terakhir, kueri luar menghitung angka dalam rentang yang diminta antara input @low dan @high.
Fungsi dbo.GetNumsItzikBatch menggunakan dummy join ke tabel dengan indeks columnstore untuk mendapatkan pemrosesan batch. Berikut kode untuk membuat tabel dummy:
CREATE TABLE dbo.BatchMe(col1 INT NOT NULL, INDEX idx_cs CLUSTERED COLUMNSTORE);
Dan inilah kode yang mendefinisikan fungsi dbo.GetNumsItzikBatch:
CREATE OR ALTER FUNCTION dbo.GetNumsItzikBatch(@low AS BIGINT, @high AS BIGINT) RETURNS TABLE AS RETURN WITH L0 AS ( SELECT 1 AS c FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1), (1),(1),(1),(1),(1),(1),(1),(1)) AS D(c) ), L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ), L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ), L3 AS ( SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B ), Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum FROM L3 ) SELECT TOP(@high - @low + 1) @low + rownum - 1 AS n FROM Nums LEFT OUTER JOIN dbo.BatchMe ON 1 = 0 ORDER BY rownum;
Saya menggunakan kode berikut untuk menguji fungsi dengan "Buang hasil setelah eksekusi" diaktifkan di SSMS:
SELECT n FROM dbo.GetNumsItzikBatch(1, 100000000) OPTION(MAXDOP 1);
Berikut adalah statistik kinerja yang saya dapatkan untuk eksekusi ini:
CPU time = 16985 ms, elapsed time = 18348 ms.
Fungsi dbo.GetNumsItzik serupa, hanya saja tidak memiliki dummy join, dan biasanya mendapatkan pemrosesan mode baris di seluruh paket. Berikut definisi fungsinya:
CREATE OR ALTER FUNCTION dbo.GetNumsItzik(@low AS BIGINT, @high AS BIGINT) RETURNS TABLE AS RETURN WITH L0 AS ( SELECT 1 AS c FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1), (1),(1),(1),(1),(1),(1),(1),(1)) AS D(c) ), L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ), L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ), L3 AS ( SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B ), Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum FROM L3 ) SELECT TOP(@high - @low + 1) @low + rownum - 1 AS n FROM Nums ORDER BY rownum;
Berikut kode yang saya gunakan untuk menguji fungsi:
SELECT n FROM dbo.GetNumsItzik(1, 100000000) OPTION(MAXDOP 1);
Berikut adalah statistik kinerja yang saya dapatkan untuk eksekusi ini:
CPU time = 19969 ms, elapsed time = 21229 ms.
Peningkatan Alan Burstein dan Charlie
Alan dan Charlie menyarankan beberapa perbaikan pada fungsi saya, beberapa dengan implikasi kinerja sedang dan beberapa dengan yang lebih dramatis. Saya akan mulai dengan temuan Charlie tentang overhead kompilasi dan pelipatan konstan. Saya kemudian akan membahas saran Alan, termasuk urutan berbasis 1 versus @ rendah (juga dibagikan oleh Charlie dan Jeff Moden), menghindari pengurutan yang tidak perlu, dan menghitung rentang angka dalam urutan yang berlawanan.
Temuan waktu kompilasi
Seperti yang dicatat Charlie, generator seri angka sering digunakan untuk menghasilkan seri dengan jumlah baris yang sangat kecil. Dalam kasus tersebut, waktu kompilasi kode dapat menjadi bagian penting dari total waktu pemrosesan kueri. Itu sangat penting saat menggunakan iTVF, karena tidak seperti prosedur tersimpan, bukan kode kueri berparameter yang dioptimalkan, melainkan kode kueri setelah penyematan parameter dilakukan. Dengan kata lain, parameter diganti dengan nilai input sebelum pengoptimalan, dan kode dengan konstanta dioptimalkan. Proses ini dapat memiliki implikasi negatif dan positif. Salah satu implikasi negatifnya adalah Anda mendapatkan lebih banyak kompilasi karena fungsi dipanggil dengan nilai input yang berbeda. Untuk alasan ini, waktu kompilasi harus benar-benar diperhitungkan—terutama saat menggunakan fungsi sangat sering dengan rentang kecil.
Berikut adalah waktu kompilasi yang ditemukan Charlie untuk berbagai kardinalitas CTE dasar:
2: 22ms 4: 9ms 16: 7ms 256: 35ms
Sangat menarik untuk melihat bahwa di antara ini, 16 adalah yang optimal, dan ada lompatan yang sangat dramatis ketika Anda naik ke tingkat berikutnya, yaitu 256. Ingat bahwa fungsi dbo.GetNumsItzikBacth dan dbo.GetNumsItzik menggunakan kardinalitas CTE dasar 16 .
Pelipatan konstan
Pelipatan konstan sering kali merupakan implikasi positif bahwa dalam kondisi yang tepat dapat diaktifkan berkat proses penyisipan parameter yang dialami iTVF. Misalnya, anggaplah fungsi Anda memiliki ekspresi @x + 1, di mana @x adalah parameter input dari fungsi tersebut. Anda memanggil fungsi dengan @x =5 sebagai input. Ekspresi inline kemudian menjadi 5 + 1, dan jika memenuhi syarat untuk pelipatan konstan (lebih lanjut tentang ini segera), maka menjadi 6. Jika ekspresi ini adalah bagian dari ekspresi yang lebih rumit yang melibatkan kolom, dan diterapkan ke jutaan baris, ini dapat menghasilkan penghematan yang tidak dapat diabaikan dalam siklus CPU.
Bagian yang sulit adalah bahwa SQL Server sangat pilih-pilih tentang apa yang harus dilipat konstan dan apa yang tidak dilipat konstan. Misalnya, SQL Server tidak lipatan konstan col1 + 5 + 1, juga tidak akan melipat 5 + col1 + 1. Tapi itu akan melipat 5 + 1 + col1 menjadi 6 + col1. Saya tahu. Jadi, misalnya, jika fungsi Anda mengembalikan SELECT @x + col1 + 1 AS mycol1 FROM dbo.T1, Anda dapat mengaktifkan folding konstan dengan perubahan kecil berikut:SELECT @x + 1 + col1 AS mycol1 FROM dbo.T1. Tidak percaya padaku? Periksa paket untuk tiga kueri berikut di database PerformanceV5 (atau kueri serupa dengan data Anda) dan lihat sendiri:
SELECT orderid + 5 + 1 AS myorderid FROM dbo.orders; SELECT 5 + orderid + 1 AS myorderid FROM dbo.orders; SELECT 5 + 1 + orderid AS myorderid FROM dbo.orders;
Saya mendapatkan tiga ekspresi berikut di operator Hitung Skalar untuk ketiga kueri ini, masing-masing:
[Expr1003] = Scalar Operator([PerformanceV5].[dbo].[Orders].[orderid]+(5)+(1)) [Expr1003] = Scalar Operator((5)+[PerformanceV5].[dbo].[Orders].[orderid]+(1)) [Expr1003] = Scalar Operator((6)+[PerformanceV5].[dbo].[Orders].[orderid])
Lihat ke mana saya akan pergi dengan ini? Dalam fungsi saya, saya menggunakan ekspresi berikut untuk mendefinisikan kolom hasil n:
@low + rownum - 1 AS n
Charlie menyadari bahwa dengan perubahan kecil berikut, dia dapat mengaktifkan folding konstan:
@low - 1 + rownum AS n
Misalnya, rencana untuk kueri sebelumnya yang saya berikan terhadap dbo.GetNumsItzik, dengan @low =1, awalnya memiliki ekspresi berikut yang ditentukan oleh operator Hitung Skalar:
[Expr1154] = Scalar Operator((1)+[Expr1153]-(1))
Setelah menerapkan perubahan kecil di atas, ekspresi dalam rencana menjadi:
[Expr1154] = Scalar Operator((0)+[Expr1153])
Itu brilian!
Mengenai implikasi kinerja, ingatlah bahwa statistik kinerja yang saya dapatkan untuk kueri terhadap dbo.GetNumsItzikBatch sebelum perubahan adalah sebagai berikut:
CPU time = 16985 ms, elapsed time = 18348 ms.
Berikut adalah angka yang saya dapatkan setelah perubahan:
CPU time = 16375 ms, elapsed time = 17932 ms.
Berikut adalah angka yang saya dapatkan untuk kueri terhadap dbo.GetNumsItzik awalnya:
CPU time = 19969 ms, elapsed time = 21229 ms.
Dan berikut adalah angka setelah perubahan:
CPU time = 19266 ms, elapsed time = 20588 ms.
Performa meningkat hanya beberapa persen. Tapi tunggu, masih ada lagi! Jika Anda perlu memproses data yang dipesan, implikasi kinerjanya bisa jauh lebih dramatis, seperti yang akan saya bahas nanti di bagian tentang pemesanan.
Urutan berbasis 1 versus @urutan berbasis rendah dan nomor baris berlawanan
Alan, Charlie, dan Jeff mencatat bahwa di sebagian besar kasus kehidupan nyata di mana Anda memerlukan rentang angka, Anda memerlukannya untuk memulai dengan 1, atau terkadang 0. Jauh lebih jarang membutuhkan titik awal yang berbeda. Jadi akan lebih masuk akal jika fungsi selalu mengembalikan rentang yang dimulai dengan, katakanlah, 1, dan ketika Anda membutuhkan titik awal yang berbeda, terapkan perhitungan apa pun secara eksternal dalam kueri terhadap fungsi tersebut.
Alan sebenarnya datang dengan ide elegan untuk membuat TVF sebaris mengembalikan kolom yang dimulai dengan 1 (hanya hasil langsung dari fungsi ROW_NUMBER) alias rn, dan kolom yang dimulai dengan @low alias n. Karena fungsi menjadi sebaris, ketika kueri luar hanya berinteraksi dengan kolom rn, kolom n bahkan tidak dievaluasi, dan Anda mendapatkan manfaat kinerja. Saat Anda membutuhkan urutan untuk memulai dengan @low, Anda berinteraksi dengan kolom n dan membayar biaya tambahan yang berlaku, jadi tidak perlu menambahkan perhitungan eksternal eksplisit. Alan bahkan menyarankan untuk menambahkan kolom yang disebut op yang menghitung angka dalam urutan yang berlawanan, dan berinteraksi dengannya hanya ketika membutuhkan urutan seperti itu. Kolom op didasarkan pada perhitungan:@high + 1 – rownum. Kolom ini memiliki arti penting ketika Anda perlu memproses baris dalam urutan nomor menurun seperti yang saya tunjukkan nanti di bagian pemesanan.
Jadi, mari kita terapkan peningkatan Charlie dan Alan pada fungsi saya.
Untuk versi mode batch, pastikan Anda membuat tabel dummy dengan indeks columnstore terlebih dahulu, jika belum ada:
CREATE TABLE dbo.BatchMe(col1 INT NOT NULL, INDEX idx_cs CLUSTERED COLUMNSTORE);
Kemudian gunakan definisi berikut untuk fungsi dbo.GetNumsAlanCharlieItzikBatch:
CREATE OR ALTER FUNCTION dbo.GetNumsAlanCharlieItzikBatch(@low AS BIGINT = 1, @high AS BIGINT) RETURNS TABLE AS RETURN WITH L0 AS ( SELECT 1 AS c FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1), (1),(1),(1),(1),(1),(1),(1),(1)) AS D(c) ), L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ), L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ), L3 AS ( SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B ), Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum FROM L3 ) SELECT TOP(@high - @low + 1) rownum AS rn, @high + 1 - rownum AS op, @low - 1 + rownum AS n FROM Nums LEFT OUTER JOIN dbo.BatchMe ON 1 = 0 ORDER BY rownum;
Berikut ini contoh penggunaan fungsi:
SELECT * FROM dbo.GetNumsAlanCharlieItzikBatch(-2, 3) AS F ORDER BY rn;
Kode ini menghasilkan output berikut:
rn op n --- --- --- 1 3 -2 2 2 -1 3 1 0 4 0 1 5 -1 2 6 -2 3
Selanjutnya, uji kinerja fungsi dengan 100 juta baris, pertama kembalikan kolom n:
SELECT n FROM dbo.GetNumsAlanCharlieItzikBatch(1, 100000000) OPTION(MAXDOP 1);
Berikut adalah statistik kinerja yang saya dapatkan untuk eksekusi ini:
CPU time = 16375 ms, elapsed time = 17932 ms.
Seperti yang Anda lihat, ada sedikit peningkatan dibandingkan dengan dbo.GetNumsItzikBatch di CPU dan waktu yang telah berlalu berkat pelipatan konstan yang terjadi di sini.
Uji fungsinya, hanya kali ini mengembalikan kolom rn:
SELECT rn FROM dbo.GetNumsAlanCharlieItzikBatch(1, 100000000) OPTION(MAXDOP 1);
Berikut adalah statistik kinerja yang saya dapatkan untuk eksekusi ini:
CPU time = 15890 ms, elapsed time = 18561 ms.
Waktu CPU semakin berkurang, meskipun waktu yang telah berlalu tampaknya sedikit meningkat dalam eksekusi ini dibandingkan dengan saat menanyakan kolom n.
Gambar 1 memiliki rencana untuk kedua kueri.
Gambar 1:Rencana GetNumsAlanCharlieItzikBatch kembali n versus rn
Anda dapat dengan jelas melihat dalam rencana bahwa ketika berinteraksi dengan kolom rn, tidak diperlukan operator Compute Scalar tambahan. Perhatikan juga dalam rencana pertama hasil dari aktivitas pelipatan konstan yang saya jelaskan sebelumnya, di mana @low – 1 + rownum digariskan menjadi 1 – 1 + rownum, dan kemudian dilipat menjadi 0 + rownum.
Berikut definisi versi mode baris dari fungsi yang disebut dbo.GetNumsAlanCharlieItzik:
CREATE OR ALTER FUNCTION dbo.GetNumsAlanCharlieItzik(@low AS BIGINT = 1, @high AS BIGINT) RETURNS TABLE AS RETURN WITH L0 AS ( SELECT 1 AS c FROM (VALUES(1),(1),(1),(1),(1),(1),(1),(1), (1),(1),(1),(1),(1),(1),(1),(1)) AS D(c) ), L1 AS ( SELECT 1 AS c FROM L0 AS A CROSS JOIN L0 AS B ), L2 AS ( SELECT 1 AS c FROM L1 AS A CROSS JOIN L1 AS B ), L3 AS ( SELECT 1 AS c FROM L2 AS A CROSS JOIN L2 AS B ), Nums AS ( SELECT ROW_NUMBER() OVER(ORDER BY (SELECT NULL)) AS rownum FROM L3 ) SELECT TOP(@high - @low + 1) rownum AS rn, @high + 1 - rownum AS op, @low - 1 + rownum AS n FROM Nums ORDER BY rownum;
Gunakan kode berikut untuk menguji fungsi, pertama-tama kueri kolom n:
SELECT n FROM dbo.GetNumsAlanCharlieItzik(1, 100000000) OPTION(MAXDOP 1);
Berikut statistik performa yang saya dapatkan:
CPU time = 19047 ms, elapsed time = 20121 ms.
Seperti yang Anda lihat, ini sedikit lebih cepat daripada dbo.GetNumsItzik.
Selanjutnya, query kolom rn:
SELECT rn FROM dbo.GetNumsAlanCharlieItzik(1, 100000000) OPTION(MAXDOP 1);
Angka kinerja semakin meningkat pada CPU dan front waktu yang telah berlalu:
CPU time = 17656 ms, elapsed time = 18990 ms.
Pertimbangan pemesanan
Peningkatan yang disebutkan di atas tentu saja menarik, dan dampak kinerjanya tidak dapat diabaikan, tetapi tidak terlalu signifikan. Dampak kinerja yang jauh lebih dramatis dan mendalam dapat diamati ketika Anda perlu memproses data yang diurutkan berdasarkan kolom angka. Ini bisa sesederhana perlu mengembalikan baris yang dipesan, tetapi sama relevannya untuk kebutuhan pemrosesan berbasis pesanan, misalnya, operator Stream Aggregate untuk pengelompokan dan agregasi, algoritme Gabung Gabung untuk bergabung, dan seterusnya.
Saat menanyakan dbo.GetNumsItzikBatch atau dbo.GetNumsItzik dan memesan dengan n, pengoptimal tidak menyadari bahwa ekspresi pemesanan yang mendasari @low + rownum – 1 adalah mempertahankan pesanan sehubungan dengan rownum. Implikasinya agak mirip dengan ekspresi pemfilteran non-SARGable, hanya dengan ekspresi pengurutan ini menghasilkan operator Sortir eksplisit dalam rencana. Penyortiran ekstra memengaruhi waktu respons. Ini juga memengaruhi penskalaan, yang biasanya menjadi n log n, bukan n.
Untuk mendemonstrasikan ini, kueri dbo.GetNumsItzikBatch, meminta kolom n, diurutkan oleh n:
SELECT n FROM dbo.GetNumsItzikBatch(1,100000000) ORDER BY n OPTION(MAXDOP 1);
Saya mendapatkan statistik kinerja berikut:
CPU time = 34125 ms, elapsed time = 39656 ms.
Waktu berjalan lebih dari dua kali lipat dibandingkan dengan pengujian tanpa klausa ORDER BY.
Uji fungsi dbo.GetNumsItzik dengan cara yang sama:
SELECT n FROM dbo.GetNumsItzik(1,100000000) ORDER BY n OPTION(MAXDOP 1);
Saya mendapat nomor berikut untuk tes ini:
CPU time = 52391 ms, elapsed time = 55175 ms.
Juga di sini waktu berjalan lebih dari dua kali lipat dibandingkan dengan pengujian tanpa klausa ORDER BY.
Gambar 2 memiliki rencana untuk kedua kueri.
Gambar 2:Rencana pemesanan GetNumsItzikBatch dan GetNumsItzik oleh n
Dalam kedua kasus, Anda dapat melihat operator Sortir eksplisit dalam paket.
Saat menanyakan dbo.GetNumsAlanCharlieItzikBatch atau dbo.GetNumsAlanCharlieItzik dan memesan dengan rn, pengoptimal tidak perlu menambahkan operator Urutkan ke paket. Jadi Anda bisa mengembalikan n, tetapi pesan berdasarkan rn, dan cara ini menghindari pengurutan. Apa yang sedikit mengejutkan, meskipun — dan maksud saya dengan cara yang baik — adalah bahwa versi revisi dari n yang mengalami pelipatan konstan, adalah pelestarian pesanan! Sangat mudah bagi pengoptimal untuk menyadari bahwa 0 + rownum adalah ekspresi yang mempertahankan urutan sehubungan dengan rownum, dan dengan demikian menghindari pengurutan.
Cobalah. Kueri dbo.GetNumsAlanCharlieItzikBatch, mengembalikan n, dan memesan dengan n atau rn, seperti ini:
SELECT n FROM dbo.GetNumsAlanCharlieItzikBatch(1,100000000) ORDER BY n -- same with rn OPTION(MAXDOP 1);
Saya mendapatkan nomor kinerja berikut:
CPU time = 16500 ms, elapsed time = 17684 ms.
Itu tentu saja berkat fakta bahwa tidak diperlukan operator Sortir dalam rencana.
Jalankan pengujian serupa terhadap dbo.GetNumsAlanCharlieItzik:
SELECT n FROM dbo.GetNumsAlanCharlieItzik(1,100000000) ORDER BY n -- same with rn OPTION(MAXDOP 1);
Saya mendapatkan nomor berikut:
CPU time = 19546 ms, elapsed time = 20803 ms.
Gambar 3 memiliki rencana untuk kedua kueri:
Gambar 3:Rencana untuk GetNumsAlanCharlieItzikBatch dan GetNumsAlanCharlieItzik memesan dengan n atau rn
Perhatikan bahwa tidak ada operator Sortir dalam paket.
Membuat Anda ingin bernyanyi…
All you need is constant folding All you need is constant folding All you need is constant folding, constant folding Constant folding is all you need
Terima kasih Charlie!
Tetapi bagaimana jika Anda perlu mengembalikan atau memproses angka dalam urutan menurun? Upaya yang jelas adalah menggunakan ORDER BY n DESC, atau ORDER BY rn DESC, seperti:
SELECT n FROM dbo.GetNumsAlanCharlieItzikBatch(1,100000000) ORDER BY n DESC OPTION(MAXDOP 1); SELECT n FROM dbo.GetNumsAlanCharlieItzikBatch(1,100000000) ORDER BY rn DESC OPTION(MAXDOP 1);
Sayangnya, kedua kasus menghasilkan semacam eksplisit dalam rencana, seperti yang ditunjukkan pada Gambar 4.
Gambar 4:Rencana GetNumsAlanCharlieItzikBatch memesan dengan n atau rn menurun
Di sinilah trik cerdas Alan dengan operasi kolom menjadi penyelamat. Kembalikan kolom op saat memesan dengan n atau rn, seperti:
SELECT op FROM dbo.GetNumsAlanCharlieItzikBatch(1,100000000) ORDER BY n OPTION(MAXDOP 1);
Rencana untuk kueri ini ditunjukkan pada Gambar 5.
Gambar 5:Rencana GetNumsAlanCharlieItzikBatch mengembalikan operasi dan memesan dengan n atau rn naik
Anda mendapatkan kembali data yang diurutkan dengan n turun dan tidak perlu mengurutkan dalam rencana.
Terima kasih Alan!
Ringkasan kinerja
Jadi apa yang telah kita pelajari dari semua ini?
Waktu kompilasi dapat menjadi faktor, terutama saat sering menggunakan fungsi dengan rentang kecil. Pada skala logaritmik dengan basis 2, 16 manis tampaknya merupakan angka ajaib yang bagus.
Pahami kekhasan pelipatan konstan dan gunakan untuk keuntungan Anda. Ketika iTVF memiliki ekspresi yang melibatkan parameter, konstanta, dan kolom, letakkan parameter dan konstanta di bagian depan ekspresi. Ini akan meningkatkan kemungkinan untuk melipat, mengurangi overhead CPU, dan meningkatkan kemungkinan pelestarian pesanan.
Tidak apa-apa memiliki beberapa kolom yang digunakan untuk tujuan berbeda di iTVF, dan menanyakan kolom yang relevan di setiap kasus tanpa khawatir membayar untuk kolom yang tidak dirujuk.
Saat Anda membutuhkan urutan nomor yang dikembalikan dalam urutan yang berlawanan, gunakan kolom n atau rn asli dalam klausa ORDER BY dengan urutan menaik, dan kembalikan kolom op, yang menghitung angka dalam urutan terbalik.
Gambar 6 merangkum angka kinerja yang saya dapatkan dalam berbagai tes.
Gambar 6:Ringkasan kinerja
Bulan depan saya akan terus mengeksplorasi ide, wawasan, dan solusi tambahan untuk tantangan generator seri angka.