Saya memiliki permintaan yang cukup kompleks yang berjalan hanya dalam beberapa detik saja, tetapi ketika dibungkus dengan fungsi bernilai tabel, itu jauh lebih lambat; Saya sebenarnya tidak membiarkannya selesai, tapi itu berjalan hingga sepuluh menit tanpa berakhir. Satu-satunya perubahan adalah mengganti dua variabel tanggal (diinisialisasi dengan literal tanggal) dengan parameter tanggal:
Berjalan dalam Tujuh Detik
DECLARE @StartDate DATE = '2011-05-21'
DECLARE @EndDate DATE = '2011-05-23'
DECLARE @Data TABLE (...)
INSERT INTO @Data(...) SELECT...
SELECT * FROM @Data
Berjalan setidaknya 10 menit
CREATE FUNCTION X (@StartDate DATE, @EndDate DATE)
RETURNS TABLE AS RETURN
SELECT ...
SELECT * FROM X ('2011-05-21', '2011-05-23')
Saya sebelumnya telah menulis fungsi sebagai TVF multi-pernyataan dengan klausa RETURNS @Data TABLE (...), tetapi menukar bahwa untuk struktur inline belum membuat perubahan nyata. Waktu jangka panjang TVF adalah SELECT * FROM X
waktu yang sebenarnya ; sebenarnya membuat UDF hanya membutuhkan beberapa detik.
Saya bisa memposting pertanyaan yang dimaksud, tetapi agak panjang (~ 165 baris) dan, berdasarkan keberhasilan pendekatan pertama, saya curiga ada sesuatu yang sedang terjadi. Melacak melalui rencana eksekusi, mereka tampaknya identik.
Saya sudah mencoba memecah kueri menjadi bagian yang lebih kecil, tanpa perubahan. Tidak ada satu bagian pun yang membutuhkan lebih dari beberapa detik ketika dieksekusi sendiri, tetapi TVF masih hang.
Saya melihat pertanyaan yang sangat mirip, /programming/4190506/sql-server-2005-table-valued-function-weird-performance , tapi saya tidak yakin bahwa solusinya berlaku. Mungkin seseorang telah melihat masalah ini dan tahu solusi yang lebih umum? Terima kasih!
Inilah dm_exec_requests setelah beberapa menit pemrosesan:
session_id 59
request_id 0
start_time 40688.46517
status running
command UPDATE
sql_handle 0x030015002D21AF39242A1101ED9E00000000000000000000
statement_start_offset 10962
statement_end_offset 16012
plan_handle 0x050015002D21AF3940C1E6B0040000000000000000000000
database_id 21
user_id 1
connection_id 314AE0E4-A1FB-4602-BF40-02D857BAD6CF
blocking_session_id 0
wait_type NULL
wait_time 0
last_wait_type SOS_SCHEDULER_YIELD
wait_resource
open_transaction_count 0
open_resultset_count 1
transaction_id 48030651
context_info 0x
percent_complete 0
estimated_completion_time 0
cpu_time 344777
total_elapsed_time 348632
scheduler_id 7
task_address 0x000000045FC85048
reads 1549
writes 13
logical_reads 30331425
text_size 2147483647
language us_english
date_format mdy
date_first 7
quoted_identifier 1
arithabort 1
ansi_null_dflt_on 1
ansi_defaults 0
ansi_warnings 1
ansi_padding 1
ansi_nulls 1
concat_null_yields_null 1
transaction_isolation_level 2
lock_timeout -1
deadlock_priority 0
row_count 105
prev_error 0
nest_level 1
granted_query_memory 170
executing_managed_code 0
group_id 2
query_hash 0xBE6A286546AF62FC
query_plan_hash 0xD07630B947043AF0
Inilah pertanyaan lengkapnya:
CREATE FUNCTION Routine.MarketingDashboardECommerceBase (@StartDate DATE, @EndDate DATE)
RETURNS TABLE AS RETURN
WITH RegionsByCode AS (SELECT CountryCode, MIN(Region) AS Region FROM Staging.Volusion.MarketingRegions GROUP BY CountryCode)
SELECT
D.Date, Div.Division, Region.Region, C.Category1, C.Category2, C.Category3,
COALESCE(V.Visits, 0) AS Visits,
COALESCE(Dem.Demos, 0) AS Demos,
COALESCE(S.GrossStores, 0) AS GrossStores,
COALESCE(S.PaidStores, 0) AS PaidStores,
COALESCE(S.NetStores, 0) AS NetStores,
COALESCE(S.StoresActiveNow, 0) AS StoresActiveNow
-- This line causes the run time to climb from a few seconds to over an hour!
--COALESCE(V.Visits, 0) * COALESCE(ACS.AvgClickCost, GAAC.AvgAdCost, 0.00) AS TotalAdCost
-- This line alone does not inflate the run time
--ACS.AvgClickCost
-- This line is enough to increase the run time to at least a couple minutes
--GAAC.AvgAdCost
FROM
--Dates AS D
(SELECT SQLDate AS Date FROM Dates WHERE SQLDate BETWEEN @StartDate AND @EndDate) AS D
CROSS JOIN (SELECT 'UK' AS Division UNION SELECT 'US' UNION SELECT 'IN' UNION SELECT 'Unknown') AS Div
CROSS JOIN (SELECT Category1, Category2, Category3 FROM Routine.MarketingDashboardCampaignMap UNION SELECT 'Unknown', 'Unknown', 'Unknown') AS C
CROSS JOIN (SELECT DISTINCT Region FROM Staging.Volusion.MarketingRegions) AS Region
-- Visitors
LEFT JOIN
(
SELECT
V.Date,
CASE WHEN V.Country IN ('United Kingdom', 'Guernsey', 'Ireland', 'Jersey') THEN 'UK'
WHEN V.Country IN ('United States', 'Canada', 'Puerto Rico', 'U.S. Virgin Islands') THEN 'US'
ELSE 'IN' END AS Division,
COALESCE(MR.Region, 'Unknown') AS Region,
C.Category1, C.Category2, C.Category3,
SUM(V.Visits) AS Visits
FROM
RawData.GoogleAnalytics.Visits AS V
INNER JOIN Routine.MarketingDashboardCampaignMap AS C ON V.LandingPage = C.LandingPage AND V.Campaign = C.Campaign AND V.Medium = C.Medium AND V.Referrer = C.Referrer AND V.Source = C.Source
LEFT JOIN Staging.Volusion.MarketingRegions AS MR ON V.Country = MR.CountryName
WHERE
V.Date BETWEEN @StartDate AND @EndDate
GROUP BY
V.Date,
CASE WHEN V.Country IN ('United Kingdom', 'Guernsey', 'Ireland', 'Jersey') THEN 'UK'
WHEN V.Country IN ('United States', 'Canada', 'Puerto Rico', 'U.S. Virgin Islands') THEN 'US'
ELSE 'IN' END,
COALESCE(MR.Region, 'Unknown'), C.Category1, C.Category2, C.Category3
) AS V ON D.Date = V.Date AND Div.Division = V.Division AND Region.Region = V.Region AND C.Category1 = V.Category1 AND C.Category2 = V.Category2 AND C.Category3 = V.Category3
-- Demos
LEFT JOIN
(
SELECT
OD.SQLDate,
G.Division,
COALESCE(MR.Region, 'Unknown') AS Region,
COALESCE(C.Category1, 'Unknown') AS Category1,
COALESCE(C.Category2, 'Unknown') AS Category2,
COALESCE(C.Category3, 'Unknown') AS Category3,
SUM(D.Demos) AS Demos
FROM
Demos AS D
INNER JOIN Orders AS O ON D."Order" = O."Order"
INNER JOIN Dates AS OD ON O.OrderDate = OD.DateSerial
INNER JOIN MarketingSources AS MS ON D.Source = MS.Source
LEFT JOIN RegionsByCode AS MR ON MS.CountryCode = MR.CountryCode
LEFT JOIN
(
SELECT
G.TransactionID,
MIN (
CASE WHEN G.Country IN ('United Kingdom', 'Guernsey', 'Ireland', 'Jersey') THEN 'UK'
WHEN G.Country IN ('United States', 'Canada', 'Puerto Rico', 'U.S. Virgin Islands') THEN 'US'
ELSE 'IN' END
) AS Division
FROM
RawData.GoogleAnalytics.Geography AS G
WHERE
TransactionDate BETWEEN @StartDate AND @EndDate
AND NOT EXISTS (SELECT * FROM RawData.GoogleAnalytics.Geography AS G2 WHERE G.TransactionID = G2.TransactionID AND G2.EffectiveDate > G.EffectiveDate)
GROUP BY
G.TransactionID
) AS G ON O.VolusionOrderID = G.TransactionID
LEFT JOIN RawData.GoogleAnalytics.Referrers AS R ON O.VolusionOrderID = R.TransactionID AND NOT EXISTS (SELECT * FROM RawData.GoogleAnalytics.Referrers AS R2 WHERE R.TransactionID = R2.TransactionID AND R2.EffectiveDate > R.EffectiveDate)
LEFT JOIN Routine.MarketingDashboardCampaignMap AS C ON MS.LandingPage = C.LandingPage AND MS.Campaign = C.Campaign AND MS.Medium = C.Medium AND COALESCE(R.ReferralPath, '(not set)') = C.Referrer AND MS.SourceName = C.Source
WHERE
O.IsDeleted = 'No'
AND OD.SQLDate BETWEEN @StartDate AND @EndDate
GROUP BY
OD.SQLDate,
G.Division,
COALESCE(MR.Region, 'Unknown'),
COALESCE(C.Category1, 'Unknown'),
COALESCE(C.Category2, 'Unknown'),
COALESCE(C.Category3, 'Unknown')
) AS Dem ON D.Date = Dem.SQLDate AND Div.Division = Dem.Division AND Region.Region = Dem.Region AND C.Category1 = Dem.Category1 AND C.Category2 = Dem.Category2 AND C.Category3 = Dem.Category3
-- Stores
LEFT JOIN
(
SELECT
OD.SQLDate,
CASE WHEN O.VolusionCountryCode = 'GB' THEN 'UK'
WHEN A.CountryShortName IN ('U.S.', 'Canada', 'Puerto Rico', 'U.S. Virgin Islands') THEN 'US'
ELSE 'IN' END AS Division,
COALESCE(MR.Region, 'Unknown') AS Region,
COALESCE(CpM.Category1, 'Unknown') AS Category1,
COALESCE(CpM.Category2, 'Unknown') AS Category2,
COALESCE(CpM.Category3, 'Unknown') AS Category3,
SUM(S.Stores) AS GrossStores,
SUM(CASE WHEN O.DatePaid <> -1 THEN 1 ELSE 0 END) AS PaidStores,
SUM(CASE WHEN O.DatePaid <> -1 AND CD.WeekEnding <> OD.WeekEnding THEN 1 ELSE 0 END) AS NetStores,
SUM(CASE WHEN O.DatePaid <> -1 THEN SH.ActiveStores ELSE 0 END) AS StoresActiveNow
FROM
Stores AS S
INNER JOIN Orders AS O ON S."Order" = O."Order"
INNER JOIN Dates AS OD ON O.OrderDate = OD.DateSerial
INNER JOIN Dates AS CD ON O.CancellationDate = CD.DateSerial
INNER JOIN Customers AS C ON O.CustomerNow = C.Customer
INNER JOIN MarketingSources AS MS ON C.Source = MS.Source
INNER JOIN StoreHistory AS SH ON S.MostRecentHistory = SH.History
INNER JOIN Addresses AS A ON C.Address = A.Address
LEFT JOIN RegionsByCode AS MR ON MS.CountryCode = MR.CountryCode
LEFT JOIN Routine.MarketingDashboardCampaignMap AS CpM ON CpM.LandingPage = 'N/A' AND MS.Campaign = CpM.Campaign AND MS.Medium = CpM.Medium AND CpM.Referrer = 'N/A' AND MS.SourceName = CpM.Source
WHERE
O.IsDeleted = 'No'
AND OD.SQLDate BETWEEN @StartDate AND @EndDate
GROUP BY
OD.SQLDate,
CASE WHEN O.VolusionCountryCode = 'GB' THEN 'UK'
WHEN A.CountryShortName IN ('U.S.', 'Canada', 'Puerto Rico', 'U.S. Virgin Islands') THEN 'US'
ELSE 'IN' END,
COALESCE(MR.Region, 'Unknown'),
COALESCE(CpM.Category1, 'Unknown'),
COALESCE(CpM.Category2, 'Unknown'),
COALESCE(CpM.Category3, 'Unknown')
) AS S ON D.Date = S.SQLDate AND Div.Division = S.Division AND Region.Region = S.Region AND C.Category1 = S.Category1 AND C.Category2 = S.Category2 AND C.Category3 = S.Category3
-- Google Analytics spend
LEFT JOIN
(
SELECT
AC.Date, C.Category1, C.Category2, C.Category3, SUM(AC.AdCost) / SUM(AC.Visits) AS AvgAdCost
FROM
RawData.GoogleAnalytics.AdCosts AS AC
INNER JOIN
(
SELECT Campaign, Medium, Source, MIN(Category1) AS Category1, MIN(Category2) AS Category2, MIN(Category3) AS Category3
FROM Routine.MarketingDashboardCampaignMap
WHERE Category1 <> 'Affiliate'
GROUP BY Campaign, Medium, Source
) AS C ON AC.Campaign = C.Campaign AND AC.Medium = C.Medium AND AC.Source = C.Source
WHERE
AC.Date BETWEEN @StartDate AND @EndDate
GROUP BY
AC.Date, C.Category1, C.Category2, C.Category3
HAVING
SUM(AC.AdCost) > 0.00 AND SUM(AC.Visits) > 0
) AS GAAC ON D.Date = GAAC.Date AND C.Category1 = GAAC.Category1 AND C.Category2 = GAAC.Category2 AND C.Category3 = GAAC.Category3
-- adCenter spend
LEFT JOIN
(
SELECT Date, SUM(Spend) / SUM(Clicks) AS AvgClickCost
FROM RawData.AdCenter.Spend
WHERE Date BETWEEN @StartDate AND @EndDate
GROUP BY Date
HAVING SUM(Spend) > 0.00 AND SUM(Clicks) > 0
) AS ACS ON D.Date = ACS.Date AND C.Category1 = 'PPC' AND C.Category2 = 'adCenter' AND C.Category3 = 'N/A'
WHERE
V.Visits > 0 OR Dem.Demos > 0 OR S.GrossStores > 0
GO
SELECT * FROM Routine.MarketingDashboardECommerceBase('2011-05-21', '2011-05-23')
sumber
Jawaban:
Saya mengisolasi masalah ke satu baris dalam kueri. Ingatlah bahwa kueri memiliki panjang 160 baris, dan saya menyertakan tabel yang relevan, jika saya menonaktifkan baris ini dari klausa SELECT:
... run time turun dari 63 menit menjadi lima detik (sebaris CTE telah membuatnya sedikit lebih cepat daripada permintaan tujuh detik yang asli). Termasuk salah satu
ACS.AvgClickCost
atauGAAC.AvgAdCost
menyebabkan run time meledak. Apa yang membuatnya sangat aneh adalah bahwa bidang ini berasal dari dua subquery yang masing-masing memiliki sepuluh baris dan tiga! Mereka masing-masing berjalan dalam nol detik ketika dijalankan secara independen, dan dengan jumlah baris yang begitu singkat, saya berharap waktu bergabungnya sepele bahkan menggunakan loop bersarang.Adakah tebakan mengapa perhitungan yang tampaknya tidak berbahaya ini akan membuat TVF sepenuhnya, sementara itu berjalan sangat cepat sebagai permintaan yang berdiri sendiri?
sumber
GAAC.AvgAdCost
(hari ini; kemarinACS.AvgClickCost
juga merupakan masalah), sehingga subquery tampaknya membuang rencana eksekusi .COALESCE()
denganISNULL()
untuk membantu pengoptimal rencana menyusun rencana yang lebih baik. Saya pikir itu ada hubungannya denganISNULL()
memiliki tipe output yang lebih dapat diprediksi daripadaCOALESCE()
. Layak dicoba? Saya tahu ini tidak jelas, tetapi dalam pengalaman kami yang terbatas, memengaruhi pengoptimal permintaan terhadap rencana yang lebih baik tampaknya seperti seni yang kabur, jadi mencoba sekelompok ide gila yang samar-samar karena putus asa adalah satu-satunya cara kami membuat kemajuan.Saya berharap ini ada hubungannya dengan parameter sniffing.
Beberapa pembicaraan tentang masalah ada di sini (dan Anda dapat mencari SO untuk sniffing parameter.)
http://blogs.msdn.com/b/queryoptteam/archive/2006/03/31/565991.aspx
sumber
ARITHABORT
mungkin?) Dari Layanan Pelaporan dan / atau jTDS, jadi salah satu dari mereka kadang-kadang akan datang dengan rencana "buruk" tetapi yang lain (dengan menyebalkan) akan baik-baik saja "pada permintaan yang sama".)Sayangnya mesin optimisasi query SQL tidak dapat melihat fungsi di dalamnya.
Jadi saya akan menggunakan rencana eksekusi dari yang cepat untuk mencari tahu petunjuk apa yang berlaku di TF. Bilas & ulangi sampai rencana eksekusi TF mendekati yang lebih cepat.
http://sqlblog.com/blogs/tibor_karaszi/archive/2008/08/29/execution-plan-re-use-sp-executesql-and-tsql-variables.aspx
sumber
Apa perbedaan dalam nilai-nilai ini?
Ini (terutama arithabort) telah terbukti secara serius mempengaruhi kinerja permintaan dengan cara ini.
sumber
arithabort
dirinya sendiri, bukan? Sejak SQL Server 2005 saya pikir pengaturan ini tidak berpengaruh selamaansi_warnings
aktif. (Pada 2000 tampilan yang diindeks tidak akan digunakan jika diatur secara tidak benar)arithabort
pengaturan harus memiliki pengaruh dramatis pada kinerja meskipun jadi saya agak skeptis tentang hal itu saat ini.