Aiuto con Slow UDF in SQL Server 2005
-
22-08-2019 - |
Domanda
Ho una tabella di date chiamare [BadDates], ma ha solo una colonna in cui ogni record è una data da escludere. Ho un UDF come segue:
CREATE FUNCTION [dbo].[udf_GetDateInBusinessDays]
(
@StartDate datetime, --Start Date
@NumberDays int --Good days ahead
)
RETURNS datetime
AS
BEGIN
-- Declare the return variable here
DECLARE @ReturnDate datetime
SET @ReturnDate = @StartDate
DECLARE @Counter int
SET @Counter = 0
WHILE @Counter < @NumberDays
BEGIN
SET @ReturnDate = DateAdd(d,1,@ReturnDate)
IF ((SELECT COUNT(ID)
FROM dbo.[BadDates]
WHERE StartDate = @ReturnDate) = 0)
BEGIN
SET @Counter = @Counter + 1
END
END
RETURN @ReturnDate
END
Questa UDF funziona alla grande, ma è lento nella lavorazione. La stored procedure che utilizza questo corre l'UDF in ogni record. C'è altri modi per fornire la stessa funzionalità in un metodo più veloce.
Ogni aiuto è molto apprezzato!
Soluzione
Non ho ancora testato questo, ma in teoria dovrebbe funzionare. Aggiungo il numero di giorni. Poi controllo se ci fossero baddates in tale intervallo. Se ci fossero aggiungo il numero di giorni cattivi e verificare se ci fossero più baddates nel range Ho appena aggiunto. Ripetere fino a quando non date cattivi.
CREATE FUNCTION [dbo].[udf_GetDateInBusinessDays]
(
@StartDate datetime, --Start Date
@NumberDays int --Good days ahead
)
RETURNS datetime
AS
BEGIN
-- Declare the return variable here
DECLARE @ReturnDate datetime
SET @ReturnDate = dateadd(d, @NumberDays, @StartDate);
DECLARE @d int;
SET @d = (select count(1) from baddates where startdate >= @StartDate and startdate <= @ReturnDate);
declare @t datetime;
WHILE @d > 0
BEGIN
set @t = @ReturnDate;
set @ReturnDate = dateadd(d, @d, @ReturnDate);
SET @d = (select count(1) from baddates where startdate > @t and startdate <= @ReturnDate);
END
RETURN @ReturnDate
END
Altri suggerimenti
Sto assumendo che ciò che si sta cercando di fare è calcolare la data che è x giorni lavorativi passato un dato date.e.g. quale data è di 10 giorni lavorativi a partire da oggi. Sono anche supponendo che la tabella contiene baddates giorni non lavorativi per esempio Fine settimana e nei giorni festivi.
Ho incontrato requisiti simili in passato e di solito finito con tavolo da giorni che contiene tutte le date possibili con un flag che indica se una data particolare, è una giornata di lavoro o meno.
Ho quindi utilizzare quella tabella per calcolare quale data è x giorni lavorativi dalla data prevista selezionando il record che è x giorni dopo la data di inizio.
Quindi, qualcosa di simile
CREATE TABLE all_days (
dated DATETIME,
day_state CHAR(1)
)
Dove day_state è il valore di
D - Giorno Lavorativo
W - Weekend
B - Bank Holiday
L'SQL per trovare la data dopo x giorni lavorativi diventa allora
SELECT MAX(dated)
FROM (
SELECT TOP(@number_days) dated
FROM all_days
WHERE day_state = 'D'
AND dated >= @start_date
ORDER by dated ASC
)
Questo codice non è testato, ma dovrebbe darvi l'idea generale. Non si può decidere di differenziare tra i fine settimana ei giorni festivi, nel qual caso si potrebbe rinominare il day_state a working_day e ne fanno un campo di bit.
Si dovrebbe creare un indice univoco composito datato e day_state.
Si consiglia di mettere un indice su BadDates.StartDate, ma ci possono essere altre soluzioni, migliori.
Ok perché stai contando quando è possibile utilizzare la parola chiave esista? Se la sua perché si può avere date multiple dello stesso tipo in Badates questo sembra sbagliato. COUNT sarà probabilmente alla ricerca attraverso l'intera tabella di contare i casi di startdate quando tutto ciò che serve è 1 da escludere.
Hai avuto uno sguardo al piano di query per vedere cosa sta succedendo?
Sembra che si sta utilizzando questo UDF per calcolare la differenza tra due date. Se sto interpretando correttamente questo allora vi consiglio di utilizzare il built-in funzione di DateDiff.