2016-06-14 24 views
6

Ho un impiegato tabella contenente informazioni Impiegato come indicato di seguito:SQL Server - trovare persone tra intervallo di date escludendo l'anno

ID  NAME  DOB 
1  ABC  1974-01-01 
2  BDS  1984-12-31 
3  QWE  1959-05-27 

and so on 

voglio elencare tutti i dipendenti il ​​cui DOB è nel range indicato.

select * from EMPLOYEE where DOB BETWEEN '1970-01-01' AND '1980-02-27' 

Ho una condizione di filtro per 'includere anno rispetto della data', che una volta selezionato 'NO', il giorno dei dipendenti DOB e DOB mese solo dovrebbe essere considerato per il confronto. e non l'anno.

Ad esempio: Se entro l'intervallo di date come '1970-01-01' e '1980/02/27' ed il filtro è stato selezionato come 'NO', allora si deve cercare solo i dipendenti il cui DOB è maggiore di uguale a JAN-01 e inferiore a quello di FEB-27.

Se selezionato 'Sì', è semplicemente l'intervallo di date indicato nella query precedente.

Ecco quello che ho provato finora:

select * from EMPLOYEE where DOB BETWEEN '1970-01-01' AND '1980-02-27' 
AND MONTH(DOB) >= CASE WHEN 'NO'='NO' THEN MONTH('1970-01-01') 
ELSE MONTH(DOB) END 
AND MONTH(DOB) <= CASE WHEN 'NO'='NO' THEN MONTH('1980-02-27') 
ELSE MONTH(DOB) END 
AND DAY(DOB) >= CASE WHEN 'NO'='NO' THEN DAY('1970-01-01') 
ELSE DAY(DOB) END 
AND DAY(DOB) <= CASE WHEN 'NO'='NO' THEN DAY('1980-02-27') 
ELSE DAY(DOB) END 

Funziona quando passo l'intervallo di date in cui la data FROM ha minore mese numero rispetto al TO data mese.

Ad esempio: Non funziona quando si passa l'intervallo di date come '1970-12-01' a '1980-01-31'. Dovrebbe elencare i dipendenti il ​​cui DOB è in dicembre e gennaio mese.

Hai bisogno di aiuto per favore.

+0

guardando le specifiche per la ricerca, quindi se si omette il confronto dell'anno, la query non dovrebbe restituire risultati. Dato che senza il confronto dell'anno, nessuno ha un compleanno dopo il 1 ° dicembre e prima del 31 gennaio – JamieA

+0

@JamieA - cosa intendi? Chiunque sia nato in un qualsiasi giorno di dicembre o di gennaio soddisfa il criterio. – Smeeheey

+0

Se sostituisco le date nella 4a frase con le date del test, viene visualizzato "Se inserisco l'intervallo di date come" 1970-12-01 "e" 1980-01-31 "e il filtro è selezionato come" NO ", quindi dovrebbe cercare solo quei dipendenti il ​​cui DOB è maggiore di uguale a Dec-01 e inferiore a quello di JAN-31 ". che non è nessuno – JamieA

risposta

2

Dati di esempio;

DECLARE @Date_From date; SET @Date_From = '1970-12-01' 
DECLARE @Date_To date; SET @Date_To = '1974-01-31' 
DECLARE @IncludeYear bit; SET @IncludeYear = 0 

CREATE TABLE #Employee (ID int, Name varchar(10), DOB date) 
INSERT INTO #Employee (ID, Name, DOB) 
VALUES 
(1,'ABC','1974-01-01') 
,(2,'BDS','1984-12-31') 
,(3,'QWE','1959-05-27') 

Questa è la query che ho effettuato. Ho cercato di coprire per ogni eventualità.

SELECT 
e.ID 
,e.Name 
,e.DOB 
FROM #Employee e 
WHERE 
    (
    @IncludeYear = 1 
    AND 
    DOB BETWEEN @Date_From AND @Date_To 
    ) 
OR 
(
@IncludeYear = 0 
AND 
(
    (
    DATEPART(DAYOFYEAR, @Date_From) = DATEPART(DAYOFYEAR, @Date_To) 
    AND 
    DATEPART(DAYOFYEAR, DOB) = DATEPART(DAYOFYEAR, @Date_To) 
    ) 
OR 
    (
    DATEPART(DAYOFYEAR, @Date_From) < DATEPART(DAYOFYEAR, @Date_To) 
    AND 
    DATEPART(DAYOFYEAR, DOB) BETWEEN DATEPART(DAYOFYEAR, @Date_From) AND DATEPART(DAYOFYEAR, @Date_To) 
    ) 
OR 
    (
    DATEPART(DAYOFYEAR, @Date_From) > DATEPART(DAYOFYEAR, @Date_To) 
    AND 
     (
     DATEPART(DAYOFYEAR, DOB) > DATEPART(DAYOFYEAR, @Date_From) 
     OR 
     DATEPART(DAYOFYEAR, DOB) < DATEPART(DAYOFYEAR, @Date_To) 
     ) 
    ) 
) 
) 
  • prima parte del cui controlli clausola se il @date_from e @date_to sono la stessa data, quindi restituisce solo questi.
  • La seconda parte controlla se il giorno dell'anno per @date_from arriva prima di @date_to. Se lo fa, restituisci tutto tra questi giorni dell'anno .
  • assegni parte finale se il giorno dell'anno per @date_to viene prima @date_from poi diventa tutto con il giorno dell'anno dopo @date_from o prima @date_to

I risultati per questo venire fuori come Questo;

ID Name DOB 
1 ABC  1974-01-01 
2 BDS  1984-12-31 
+0

Ho provato tutte le soluzioni fornite qui. Ma questo ha funzionato perfettamente bene per me. –

2
DECLARE @includeYear bit = 0, -- if 0 - we don't include year, 1 - include 
     @dateFrom date ='1970-12-01', 
     @dateTo date ='1980-05-30' 

IF @includeYear = 1 
BEGIN 
    SELECT e.* 
    FROM EMPLOYEE e 
    INNER JOIN (SELECT @dateFrom as dF, @dateTo as dT) d 
     ON e.DOB BETWEEN dF AND dT 
END 
ELSE 
BEGIN 
    SELECT e.* 
    FROM EMPLOYEE e 
    INNER JOIN (SELECT @dateFrom as dF, @dateTo as dT) d 
     ON e.DOB BETWEEN 
     (CASE WHEN MONTH(dF) > MONTH(dT) 
       THEN DATEADD(year,YEAR(e.DOB)-YEAR(d.dF)-1,dF) 
       ELSE DATEADD(year,YEAR(e.DOB)-YEAR(d.dF),dF) END) 
       AND DATEADD(year,YEAR(e.DOB)-YEAR(d.dT),dT) 
     OR e.DOB BETWEEN DATEADD(year,YEAR(e.DOB)-YEAR(d.dF),dF) AND 
     (CASE WHEN MONTH(dF) > MONTH(dT) 
       THEN DATEADD(year,YEAR(e.DOB)-YEAR(d.dT)+1,dT) 
       ELSE DATEADD(year,YEAR(e.DOB)-YEAR(d.dT),dT) END) 
END 

Per

dateFrom dateTo 
1970-12-01 1980-01-30 

uscita:

ID NAME DOB 
1 ABC  1974-01-01 
2 BDS  1984-12-31 

Per

dateFrom dateTo 
1970-05-01 1980-06-30 

uscita:

ID NAME DOB 
3 QWE  1959-05-27 

Per

dateFrom dateTo 
1970-05-01 1980-05-30 

uscita:

ID NAME DOB 
3 QWE  1959-05-27 

ecc

0

Prova la funzione DATEPART(dayofyear, date)
Nel caso in cui il giorno d'anno della prima data è più piccolo del giorno- anno della seconda data, quindi il giorno dell'anno del DOB dovrebbe essere compreso tra i giorni dell'anno specificato.
In caso contrario, il giorno dell'anno del DOB deve essere inferiore al giorno dell'anno della seconda data o maggiore del giorno dell'anno della prima data.

Spero di essermi espresso bene.

0

Piuttosto che caso per caso di lavoro e di smontaggio e rimontaggio parti di datteri, ho cercato di rendere la vita più facile:

declare @t table (ID int not null, Name varchar(17) not null, DOB date not null) 
insert into @t(ID,NAME,DOB) values 
(1,'ABC','19740101'), 
(2,'BDS','19841231'), 
(3,'QWE','19590527') 

declare @Start date 
declare @End date 
declare @IncludeYear bit 

select @Start='19701201',@End='19800131',@IncludeYear=0 

;With Normalized as (
    select 
     ID, 
     Name, 
     CASE WHEN @IncludeYear=1 THEN DOB 
      ELSE DATEADD(year,DATEDIFF(year,DOB,'20000101'),DOB) 
     END as DOB, 
     CASE WHEN @IncludeYear=1 THEN @Start 
      ELSE DATEADD(year,DATEDIFF(year,@Start,'20000101'),@Start) 
     END as StartRange, 
     CASE WHEN @IncludeYear=1 THEN @End 
      ELSE DATEADD(year,DATEDIFF(year,@End,'20000101'),@End) 
     END as EndRange 
    from 
     @t 
) 
select * from Normalized 
where 
    DOB between StartRange and EndRange or 
    (
     @IncludeYear=0 and StartRange>EndRange and 
     (
      DOB < EndRange or DOB > StartRange 
     ) 
    ) 

Creiamo la Normalized CTE che, non fa nulla se @IncludeYear è 1 o, se è zero, ripristina tutte le date in modo che si verifichino nel 2000 (selezionato arbitrariamente).

Quindi eseguiamo la query semplice basata sul CTE. L'unica circostanza in cui non corrisponderà correttamente è quando il tuo intervallo è definito su una transizione di fine anno e non ci importa più di anni - che possiamo verificare e soddisfare specificamente entro la fine della clausola WHERE.

Risultati:

ID   Name    DOB  StartRange EndRange 
----------- ----------------- ---------- ---------- ---------- 
1   ABC    2000-01-01 2000-12-01 2000-01-31 
2   BDS    2000-12-31 2000-12-01 2000-01-31 

risultati con @Start='19700101',@End='19800227',@IncludeYear=1:

ID   Name    DOB  StartRange EndRange 
----------- ----------------- ---------- ---------- ---------- 
1   ABC    1974-01-01 1970-01-01 1980-02-27 
+0

Per quanto mi piaccia la semplicità (e l'uso di SARG), è necessario eseguire la query sull'intera tabella riga per riga? –

+0

Abbastanza sicuro, come quando ho testato i risultati su [AdventureWorks2012]. [Vendite]. [SalesOrderDetail], la tua query costava il doppio con solo 19k righe. Non sapevo che sarebbe stato così ripido, ma ha senso dal momento che si cambia ogni singola riga. –

+0

@clifton_h - ** se ** questa è una query comune e ** se ** le sue prestazioni non sono accettabili, la prima cosa che vorrei fare sarebbe implementare una colonna calcolata persistente contenente il DOB normalizzato e l'indice che . La strategia generale di questa risposta tende a rimanere la stessa. –

0

provare questo.

declare @flag varchar(3) ='NO'; 
declare @sd date ='1980-02-27'; 
declare @ed date ='1970-01-01'; 

select tt.* 
from (select sd = month(@sd)*100 + day(@sd), 
     ed = month(@ed)*100 + day(@ed) 
    ) prm  
cross join 
    -- test data, place real table here 
    ( 
     values 
     (1,'ABC', cast('1974-01-05' as date)), 
     (2,'BDS','1984-12-31'), 
     (3,'QWE','1959-05-27') 
    ) tt(ID,NAME, DOB) 
cross apply (select md = month(DOB)*100 + day(DOB)) tx 
where @flag ='YES' and DOB between @sd and @ed 
    or @flag ='NO' and (prm.sd<=prm.ed and tx.md between prm.sd and prm.ed 
         or prm.sd>prm.ed and (tx.md <= prm.ed or tx.md >= prm.sd)); 
0

Ecco un'altra soluzione

DECLARE @employee table(EmployeeID varchar(10), DOB date); 

INSERT INTO @employee(EmployeeID, DOB) 
    VALUES('0001', '01-Dec-1990'), 
     ('0002', '06-Jan-1993'), 
     ('0003', '04-Mar-1987'), 
     ('0004', '12-Feb-1996'); 


DECLARE @dateStart date = '01-Jan-1990'; 
DECLARE @dateEnd date = '27-Feb-1997'; 
DECLARE @includeYear bit = 0; 

If @includeYear = 0 
Begin 
    SET @dateStart = CAST(('2000-' + CAST(MONTH(@dateStart) AS varchar(10)) + '-' + CAST(DAY(@dateStart) as varchar(10))) AS date); 
    SET @dateEnd = CAST(('2000-' + CAST(MONTH(@dateEnd) AS varchar(10)) + '-' + CAST(DAY(@dateEnd) as varchar(10))) AS date); 
End 

If @includeYear = 1 
Begin 
    SELECT * 
    FROM @employee 
    WHERE DOB BETWEEN @dateStart AND @dateEnd 
End 
Else 
Begin 
    SELECT * 
    FROM @employee 
    WHERE CAST(('2000-' + CAST(MONTH(DOB) AS varchar(10)) + '-' + CAST(DAY(DOB) as varchar(10))) AS date) BETWEEN @dateStart AND @dateEnd 
End 

Come si può vedere che stiamo solo facendo la parte anno della query una costante se non si desidera includere anno. Questa query sembra essere un po 'più lenta ma se si aggiunge un'altra colonna calcolata nella tabella, in cui si salva la data con un anno costante, è sufficiente specificare i criteri su quella particolare colonna.

0
  • Utilizzare sempre un SARG nel predicato. Qualsiasi risposta che non riesce a fare ciò porta solo a prestazioni perse e il tuo DBA si arrabbia.

Quello che vuoi è eseguire due query diverse a seconda della risposta alla Procedura.Poiché questo è un processo che probabilmente funziona molto, memorizza le risposte in una variabile nel tuo PROC ed esegui qualsiasi codice di regolazione da lì. Ciò non solo renderà il tuo codice più solido eliminando gli errori in anticipo, ma SQL Server ha una migliore possibilità di indovinare le variabili da utilizzare con gli indici.

La seguente procedura funzionerà. Sentiti libero di usarne una parte o tutta:

CREATE TABLE #table_E (ID INT, Name VARCHAR(3), DOB DATE) 
INSERT INTO #table_E (ID , Name, DOB) 
VALUES (1, 'ABC', '1997-01-02') 
    , (2, 'BDS', '1984-12-31') 
    , (3, 'QWE', '1993-03-22') 

GO 

CREATE PROC USP_EmpCompare (@Date_1 DATE, @Date_2 DATE, @Compare_Year VARCHAR(3)) 
AS 
BEGIN 
    DECLARE @MONTH_1 INT 
      , @Month_2 INT 
      , @Day_1 INT 
      , @Day_2 INT 
      , @Date1 DATE 
      , @Date2 DATE 
    SET @Date1 = CASE WHEN @Date_1 > @Date_2 THEN @Date_2 ELSE @Date_1 END 
    SET @Date2 = CASE WHEN @Date_1 > @Date_2 THEN @Date_1 ELSE @Date_2 END 
    SET @Month_1 = CASE WHEN DATEPART(MM, @Date2) > DATEPART(MM, @Date1) THEN DATEPART(MM, @Date1) ELSE DATEPART(MM, @Date2) END 
    SET @Month_2 = CASE WHEN DATEPART(MM, @Date1) > DATEPART(MM, @Date2) THEN DATEPART(MM, @Date1) ELSE DATEPART(MM, @Date2) END 
    SET @Day_1 = CASE WHEN DATEPART(DD, @Date2) > DATEPART(DD, @Date1) THEN DATEPART(DD, @Date1) ELSE DATEPART(DD, @Date2) END 
    SET @Day_2 = CASE WHEN DATEPART(DD, @Date1) > DATEPART(DD, @Date2) THEN DATEPART(DD, @Date1) ELSE DATEPART(DD, @Date2) END 
    -- SELECT @Date1, @Date2 
    IF @Compare_Year = 'no' 
    BEGIN 
    ;WITH C AS (SELECT ID 
        , Name 
        , DATEPART(DD, DOB) AS Day 
        , DATEPART(MM, DOB) AS Month 
    FROM #table_E) 
    SELECT ID, Name, @Date1, @Date2 
    FROM C 
    WHERE C.Month >= @MONTH_1 
     AND C.Month <= @Month_2 
     AND C.Day >= @Day_1 
     AND C.DAy <= @Day_2  
    END 
    IF @Compare_Year = 'yes' 
    BEGIN 

     SELECT ID, Name, DOB 
     FROM #table_E 
     WHERE DOB <= @Date2 
      AND DOB >= @Date1 
    END 
    ELSE 
     PRINT WHAT! FOLLOW THE RULES YOU FOOL!!! 

END 

jk. l'ultima parte riguardante gli sciocchi probabilmente non è inclusa nella bozza finale. ;)