Saya mencoba untuk menyetel kueri tempat fungsi bernilai tabel (TVF) yang sama dipanggil pada 20 kolom.
Hal pertama yang saya lakukan adalah mengubah fungsi skalar menjadi fungsi bernilai tabel inline.
Apakah menggunakan CROSS APPLY
cara berkinerja terbaik untuk menjalankan fungsi yang sama pada banyak kolom dalam kueri?
Contoh sederhana:
SELECT Col1 = A.val
,Col2 = B.val
,Col3 = C.val
--do the same for other 17 columns
,Col21
,Col22
,Col23
FROM t
CROSS APPLY
dbo.function1(Col1) A
CROSS APPLY
dbo.function1(Col2) B
CROSS APPLY
dbo.function1(Col3) C
--do the same for other 17 columns
Apakah ada alternatif yang lebih baik?
Fungsi yang sama dapat dipanggil dalam beberapa kueri terhadap jumlah kolom X.
Inilah fungsinya:
CREATE FUNCTION dbo.ConvertAmountVerified_TVF
(
@amt VARCHAR(60)
)
RETURNS TABLE
WITH SCHEMABINDING
AS
RETURN
(
WITH cteLastChar
AS(
SELECT LastChar = RIGHT(RTRIM(@amt), 1)
)
SELECT
AmountVerified = CAST(RET.Y AS NUMERIC(18,2))
FROM (SELECT 1 t) t
OUTER APPLY (
SELECT N =
CAST(
CASE
WHEN CHARINDEX(L.LastChar COLLATE Latin1_General_CS_AS, '{ABCDEFGHI}', 0) >0
THEN CHARINDEX(L.LastChar COLLATE Latin1_General_CS_AS, '{ABCDEFGHI}', 0)-1
WHEN CHARINDEX(L.LastChar COLLATE Latin1_General_CS_AS, 'JKLMNOPQR', 0) >0
THEN CHARINDEX(L.LastChar COLLATE Latin1_General_CS_AS, 'JKLMNOPQR', 0)-1
WHEN CHARINDEX(L.LastChar COLLATE Latin1_General_CS_AS, 'pqrstuvwxy', 0) >0
THEN CHARINDEX(L.LastChar COLLATE Latin1_General_CS_AS, 'pqrstuvwxy', 0)-1
ELSE
NULL
END
AS VARCHAR(1))
FROM
cteLastChar L
) NUM
OUTER APPLY (
SELECT N =
CASE
WHEN CHARINDEX(L.LastChar COLLATE Latin1_General_CS_AS, '{ABCDEFGHI}', 0) >0
THEN 0
WHEN CHARINDEX(L.LastChar COLLATE Latin1_General_CS_AS, 'JKLMNOPQRpqrstuvwxy', 0) >0
THEN 1
ELSE 0
END
FROM cteLastChar L
) NEG
OUTER APPLY(
SELECT Amt= CASE
WHEN NUM.N IS NULL
THEN @amt
ELSE
SUBSTRING(RTRIM(@amt),1, LEN(@amt) - 1) + Num.N
END
) TP
OUTER APPLY(
SELECT Y = CASE
WHEN NEG.N = 0
THEN (CAST(TP.Amt AS NUMERIC) / 100)
WHEN NEG.N = 1
THEN (CAST (TP.Amt AS NUMERIC) /100) * -1
END
) RET
) ;
GO
Inilah versi fungsi skalar yang saya warisi, jika ada yang tertarik:
CREATE FUNCTION dbo.ConvertAmountVerified
(
@amt VARCHAR(50)
)
RETURNS NUMERIC (18,3)
AS
BEGIN
-- Declare the return variable here
DECLARE @Amount NUMERIC(18, 3);
DECLARE @TempAmount VARCHAR (50);
DECLARE @Num VARCHAR(1);
DECLARE @LastChar VARCHAR(1);
DECLARE @Negative BIT ;
-- Get Last Character
SELECT @LastChar = RIGHT(RTRIM(@amt), 1) ;
SELECT @Num = CASE @LastChar collate latin1_general_cs_as
WHEN '{' THEN '0'
WHEN 'A' THEN '1'
WHEN 'B' THEN '2'
WHEN 'C' THEN '3'
WHEN 'D' THEN '4'
WHEN 'E' THEN '5'
WHEN 'F' THEN '6'
WHEN 'G' THEN '7'
WHEN 'H' THEN '8'
WHEN 'I' THEN '9'
WHEN '}' THEN '0'
WHEN 'J' THEN '1'
WHEN 'K' THEN '2'
WHEN 'L' THEN '3'
WHEN 'M' THEN '4'
WHEN 'N' THEN '5'
WHEN 'O' THEN '6'
WHEN 'P' THEN '7'
WHEN 'Q' THEN '8'
WHEN 'R' THEN '9'
---ASCII
WHEN 'p' Then '0'
WHEN 'q' Then '1'
WHEN 'r' Then '2'
WHEN 's' Then '3'
WHEN 't' Then '4'
WHEN 'u' Then '5'
WHEN 'v' Then '6'
WHEN 'w' Then '7'
WHEN 'x' Then '8'
WHEN 'y' Then '9'
ELSE ''
END
SELECT @Negative = CASE @LastChar collate latin1_general_cs_as
WHEN '{' THEN 0
WHEN 'A' THEN 0
WHEN 'B' THEN 0
WHEN 'C' THEN 0
WHEN 'D' THEN 0
WHEN 'E' THEN 0
WHEN 'F' THEN 0
WHEN 'G' THEN 0
WHEN 'H' THEN 0
WHEN 'I' THEN 0
WHEN '}' THEN 1
WHEN 'J' THEN 1
WHEN 'K' THEN 1
WHEN 'L' THEN 1
WHEN 'M' THEN 1
WHEN 'N' THEN 1
WHEN 'O' THEN 1
WHEN 'P' THEN 1
WHEN 'Q' THEN 1
WHEN 'R' THEN 1
---ASCII
WHEN 'p' Then '1'
WHEN 'q' Then '1'
WHEN 'r' Then '1'
WHEN 's' Then '1'
WHEN 't' Then '1'
WHEN 'u' Then '1'
WHEN 'v' Then '1'
WHEN 'w' Then '1'
WHEN 'x' Then '1'
WHEN 'y' Then '1'
ELSE 0
END
-- Add the T-SQL statements to compute the return value here
if (@Num ='')
begin
SELECT @TempAmount=@amt;
end
else
begin
SELECT @TempAmount = SUBSTRING(RTRIM(@amt),1, LEN(@amt) - 1) + @Num;
end
SELECT @Amount = CASE @Negative
WHEN 0 THEN (CAST(@TempAmount AS NUMERIC) / 100)
WHEN 1 THEN (CAST (@TempAmount AS NUMERIC) /100) * -1
END ;
-- Return the result of the function
RETURN @Amount
END
Sampel data uji:
SELECT dbo.ConvertAmountVerified('00064170') -- 641.700
SELECT * FROM dbo.ConvertAmountVerified_TVF('00064170') -- 641.700
SELECT dbo.ConvertAmountVerified('00057600A') -- 5760.010
SELECT * FROM dbo.ConvertAmountVerified_TVF('00057600A') -- 5760.010
SELECT dbo.ConvertAmountVerified('00059224y') -- -5922.490
SELECT * FROM dbo.ConvertAmountVerified_TVF('00059224y') -- -5922.490
CROSS APPLY
s).Saya akan mulai dengan melemparkan beberapa data uji ke dalam tabel. Saya tidak tahu seperti apa data asli Anda sehingga saya hanya menggunakan integer berurutan:
Memilih semua baris dengan set hasil dimatikan menyediakan garis dasar:
Jika permintaan serupa dengan pemanggilan fungsi membutuhkan lebih banyak waktu maka kami memiliki taksiran kasar mengenai overhead fungsi. Inilah yang saya dapatkan dengan memanggil TVF Anda apa adanya:
Jadi fungsinya membutuhkan sekitar 40 detik waktu CPU untuk 6,5 juta baris. Lipat gandakan dengan 20 dan waktu CPU 800 detik. Saya perhatikan dua hal dalam kode fungsi Anda:
Penggunaan yang tidak perlu
OUTER APPLY
.CROSS APPLY
akan memberi Anda hasil yang sama, dan untuk permintaan ini, ia akan menghindari banyak gabungan yang tidak perlu. Itu bisa menghemat sedikit waktu. Sebagian besar tergantung pada apakah kueri lengkapnya berjalan paralel. Saya tidak tahu apa-apa tentang data atau permintaan Anda, jadi saya hanya mengujinyaMAXDOP 1
. Dalam hal ini saya lebih baik denganCROSS APPLY
.Ada banyak
CHARINDEX
panggilan saat Anda hanya mencari satu karakter dengan sejumlah kecil nilai yang cocok. Anda dapat menggunakanASCII()
fungsi dan sedikit matematika untuk menghindari semua perbandingan string.Berikut cara berbeda untuk menulis fungsi:
Di mesin saya, fungsi baru secara signifikan lebih cepat:
Mungkin ada beberapa optimasi tambahan juga tersedia, tetapi nyali saya mengatakan mereka tidak akan banyak berarti. Berdasarkan apa yang dilakukan kode Anda, saya tidak dapat melihat bagaimana Anda akan melihat peningkatan lebih lanjut dengan cara memanggil fungsi Anda dengan cara yang berbeda. Ini hanya sekelompok operasi string. Memanggil fungsi 20 kali per baris akan lebih lambat dari sekali saja, tetapi definisi tersebut sudah dimasukkan.
sumber
Coba gunakan yang berikut ini
sebagai gantinya
Satu varian dengan menggunakan tabel tambahan
Permintaan uji
Sebagai varian Anda juga dapat mencoba menggunakan tabel tambahan sementara
#LastCharLink
atau tabel variabel@LastCharLink
(tetapi bisa lebih lambat dari tabel nyata atau sementara)Dan gunakan sebagai
atau
Kemudian Anda juga dapat membuat fungsi inline sederhana dan memasukkan semua konversi ke dalamnya
Dan kemudian gunakan fungsi ini sebagai
sumber
Prefix
alih-alihDivider
.Atau Anda dapat membuat satu tabel permanen. Ini adalah satu kali pembuatan.
Lalu TVF
Dari contoh @Joe,
- Butuh 30 detik
Jika memungkinkan, Jumlah juga dapat diformat di tingkat UI. Ini pilihan terbaik. Kalau tidak, Anda juga dapat membagikan permintaan asli Anda. ATAU jika memungkinkan, simpan juga nilai yang diformat dalam tabel.
sumber