it-swarm-pt.tech

Próximos aniversários do SQL Select

Estou tentando escrever um procedimento armazenado para selecionar funcionários que têm aniversários futuros.

SELECT * FROM Employees WHERE Birthday > @Today AND Birthday < @Today + @NumDays

Isso não funcionará porque o ano de nascimento faz parte do aniversário, portanto, se meu aniversário foi '09 -18-1983 ', não ocorrerá entre '09 -18-2008' e '09 -25-2008 '.

Existe uma maneira de ignorar a parte do ano dos campos de data e apenas comparar mês/dia?

Isso será executado toda segunda-feira de manhã para alertar os gerentes sobre os próximos aniversários, portanto, possivelmente, se estenderá por anos novos.

Aqui está a solução que acabei criando, graças à Kogus.

SELECT * FROM Employees 
WHERE Cast(DATEDIFF(dd, birthdt, getDate()) / 365.25 as int)
    - Cast(DATEDIFF(dd, birthdt, futureDate) / 365.25 as int) 
<> 0
24
Crob

Nota: editei isso para corrigir o que considero um bug significativo. A versão atualmente postada funciona para mim.

Isso deve funcionar depois que você modifica os nomes de campo e tabela para corresponder ao seu banco de dados.

SELECT 
  BRTHDATE AS BIRTHDAY
 ,FLOOR(DATEDIFF(dd,EMP.BRTHDATE,GETDATE()) / 365.25) AS AGE_NOW
 ,FLOOR(DATEDIFF(dd,EMP.BRTHDATE,GETDATE()+7) / 365.25) AS AGE_ONE_WEEK_FROM_NOW
FROM 
  "Database name".dbo.EMPLOYEES EMP
WHERE 1 = (FLOOR(DATEDIFF(dd,EMP.BRTHDATE,GETDATE()+7) / 365.25))
          -
          (FLOOR(DATEDIFF(dd,EMP.BRTHDATE,GETDATE()) / 365.25))

Basicamente, ele recebe o número de dias do aniversário deles até agora e o divide por 365 (para evitar problemas de arredondamento que surgem quando você converte diretamente em anos).

Depois, obtém o número de dias do aniversário deles para uma semana a partir de agora e os divide em 365 para obter a idade daqui a uma semana.

Se o aniversário deles ocorrer dentro de uma semana, a diferença entre esses dois valores será 1. Portanto, ele retornará todos esses registros.

33
JosephStyons

Caso alguém ainda esteja procurando uma solução em MySQL (comandos ligeiramente diferentes), aqui está a consulta:

SELECT
 name,birthday,
 FLOOR(DATEDIFF(DATE(NOW()),birthday) / 365.25) AS age_now,
 FLOOR(DATEDIFF(DATE_ADD(DATE(NOW()),INTERVAL 30 DAY),birthday) / 365.25) AS age_future

FROM user

WHERE 1 = (FLOOR(DATEDIFF(DATE_ADD(DATE(NOW()),INTERVAL 30 DAY),birthday) / 365.25)) - (FLOOR(DATEDIFF(DATE(NOW()),birthday) / 365.25))

ORDER BY MONTH(birthday),DAY(birthday)
14
Andres SK

Melhor uso de datatediff e dateadd. Sem arredondamentos, sem aproximações, sem erros de 29 de fevereiro, nada além de funções de data

  1. ageOfThePerson = DATEDIFF(yyyy,dateOfBirth, GETDATE())

  2. dateOfNextBirthday = DATEADD(yyyy,ageOfThePerson + 1, dateOfBirth)

  3. daysBeforeBirthday = DATEDIFF(d,GETDATE(), dateofNextBirthday)

Graças a @Gustavo Cardoso, nova definição para a idade da pessoa

  1. ageOfThePerson = FLOOR(DATEDIFF(d,dateOfBirth, GETDATE())/365.25)
8
Philippe Grondier

Gostou da abordagem do @strelc, mas seu sql estava um pouco errado. Aqui está uma versão atualizada que funciona bem e é simples de usar:

SELECT * FROM User 
WHERE (DATEDIFF(dd, getdate(), DATEADD(yyyy, 
    DATEDIFF(yyyy, birthdate, getdate()) + 1, birthdate)) + 1) % 366 <= <number of days>

editar 10/2017: adicione um único dia ao final

3
Edyn

Eu encontrei a solução para isso. Isso pode economizar o tempo precioso de alguém.

 select EmployeeID,DOB,dates.date  from emp_tb_eob_employeepersonal 
 cross join dbo.GetDays(Getdate(),Getdate()+7) as dates where weekofmonthnumber>0
 and month(dates.date)=month(DOB) and day(dates.date)=day(DOB)



GO
/****** Object:  UserDefinedFunction [dbo].[GetDays]    Script Date: 11/30/2011 13:19:17 ******/
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO
--SELECT [dbo].[GetDays] ('02/01/2011','02/28/2011')

ALTER FUNCTION [dbo].[GetDays](@startDate datetime, @endDate datetime)
RETURNS @retValue TABLE
(Days int ,Date datetime, WeekOfMonthNumber int, WeekOfMonthDescription varchar(10), DayName varchar(10))
AS
BEGIN
    DECLARE @nextDay int
    DECLARE @nextDate datetime 
    DECLARE @WeekOfMonthNum int 
    DECLARE @WeekOfMonthDes varchar(10) 
    DECLARE @DayName varchar(10) 
    SELECT @nextDate = @startDate, @WeekOfMonthNum = DATEDIFF(week, DATEADD(MONTH, DATEDIFF(MONTH,0,@startDate),0),@startDate) + 1, 
    @WeekOfMonthDes = CASE @WeekOfMonthNum 
        WHEN '1' THEN 'First' 
        WHEN '2' THEN 'Second' 
        WHEN '3' THEN 'Third' 
        WHEN '4' THEN 'Fourth' 
        WHEN '5' THEN 'Fifth' 
        WHEN '6' THEN 'Sixth' 
        END, 
    @DayName 
    = DATENAME(weekday, @startDate)
SET @nextDay=1
WHILE @nextDate <= @endDate 
BEGIN 
    INSERT INTO @retValue values (@nextDay,@nextDate, @WeekOfMonthNum, @WeekOfMonthDes, @DayName) 
    SELECT @[email protected] + 1 
SELECT @nextDate = DATEADD(day,1,@nextDate), 
    @WeekOfMonthNum 
    = DATEDIFF(week, DATEADD(MONTH, DATEDIFF(MONTH,0, @nextDate),0), @nextDate) + 1, 
    @WeekOfMonthDes 
    = CASE @WeekOfMonthNum 
    WHEN '1' THEN 'First' 
    WHEN '2' THEN 'Second' 
    WHEN '3' THEN 'Third' 
    WHEN '4' THEN 'Fourth' 
    WHEN '5' THEN 'Fifth' 
    WHEN '6' THEN 'Sixth' 
    END, 
    @DayName 
    = DATENAME(weekday, @nextDate) 
    CONTINUE 
END 

WHILE(@nextDay <=31)
BEGIN


    INSERT INTO @retValue values (@nextDay,@nextDate, 0, '', '') 
    SELECT @[email protected] + 1

END

    RETURN
END

Faça uma junção cruzada com as datas e verifique a comparação de mês e datas.

2
Jovial

Você pode usar a função DAYOFYEAR, mas tenha cuidado ao procurar os aniversários de janeiro em dezembro. Acho que você ficará bem, desde que o período que você procura não abranja o Ano Novo.

2
Dave Webb

Meu palpite é que "365.25" cedo ou tarde seria um fracasso.

Então, testo a solução de trabalho usando "365.25" e ela não retorna o mesmo número de linhas para cada caso. Aqui está um exemplo:

http://sqlfiddle.com/#!3/94c3ce/7

teste com o ano de 2016 e 2116 e você verá a diferença. Só posso postar um link, mas mude de/7 por/8 para ver as duas consultas. (/ 10 e/11 para a primeira resposta)

Portanto, sugiro outra consulta, em que o ponto é determinado no próximo aniversário a partir de uma data de início e, em seguida, comparo se estiver no meu intervalo de interesse.

SELECT * FROM Employees 
WHERE 
CASE WHEN (DATEADD(yyyy,DATEDIFF(yyyy, birthdt, @fromDate),birthdt) < @fromDate )
THEN DATEADD(yyyy,DATEDIFF(yyyy, birthdt, @fromDate)+1,birthdt)
ELSE DATEADD(yyyy,DATEDIFF(yyyy, birthdt, @fromDate),birthdt) END
BETWEEN @fromDate AND @toDate
2
Fede H

Desculpe, não vi o requisito de neutralizar o ano.

select * from Employees
where DATEADD (year, DatePart(year, getdate()) - DatePart(year, Birthday), Birthday)
      between convert(datetime, getdate(), 101) 
              and convert(datetime, DateAdd(day, 5, getdate()), 101)

Isso deve funcionar.

2
Nick Berardi

Em menos de um mês:

SELECT * FROM people WHERE MOD( DATEDIFF( CURDATE( ) , `date_birth`) /30, 12 ) <1 and (((month(`date_birth`)) = (month(curdate())) and (day(`date_birth`)) > (day (curdate() ))) or ((month(`date_birth`)) > (month(curdate())) and (day(`date_birth`)) < (day (curdate() ))))
1
Danilo

Esta é a solução para o MS SQL Server: retorna funcionários com aniversários em 30 dias.

SELECT * FROM rojstni_dnevi
  WHERE (DATEDIFF   (dd, 
                    getdate(), 
                    DATEADD (   yyyy, 
                                DATEDIFF(yyyy, rDan, getdate()),
                                rDan)
    nex             )
        +365) % 365 < 30
1
strelc

Eu usei isso para MySQL, provavelmente não a maneira mais eficiente de consultar, mas simples o suficiente para implementar.

select * from `schema`.`table` where date_format(birthday,'%m%d') >= date_format(now(),'%m%d') and date_format(birthday,'%m%d') < date_format(DATE_ADD(NOW(), INTERVAL 5 DAY),'%m%d');
0
Xocoatzin

Você também pode usar DATEPART:

-- To find out Today's Birthday
DECLARE @today DATETIME
SELECT  @today = getdate()

SELECT *
FROM SMIS_Registration 
WHERE (DATEPART (month, DOB) >= DATEPART (month, @today)
      AND DATEPART (day, DOB) = DATEPART (day, @today))
0
Raj Sharma

Essa solução também cuida de aniversários no próximo ano e da ordem: (db = dia de nascimento; bty = aniversário deste ano; nbd = próximo aniversário)

with rs (bty) as (
    SELECT DATEADD(Year, DATEPART(Year, GETDATE()) - DATEPART(Year, dob), dob) as bty FROM Employees
),
rs2 (nbd) as (
  select case when bty < getdate() then DATEADD(yyyy, 1, bty) else bty end as nbd from rs
)
select nbd, DATEDIFF(d, getdate(), nbd) as diff from rs2 where DATEDIFF(d, getdate(), nbd) < 14 order by diff

Esta versão, que evita a comparação das datas, pode ser mais rápida:

with rs (dob, bty) as (
    SELECT dob, DATEADD(Year, DATEPART(Year, GETDATE()) - DATEPART(Year, DOB), DOB) as bty FROM employee
),
rs2 (dob, nbd) as (
  select dob,  DATEADD(yyyy, FLOOR(ABS((-1*(SIGN(DATEDIFF(d, getdate(), bty))))+0.1)), bty) as nbd from rs
),
rs3 (dob, diff) as (
  select dob, datediff(d, getdate(), nbd) as diff from rs2
)
select dob, diff  from rs3 where diff < 14 order by diff

Se o intervalo abranger o dia 29 de fevereiro do próximo ano, use:

with rs (dob, ydiff) as (
  select dob, DATEPART(Year, GETDATE()) - DATEPART(Year, DOB) as ydiff from Employee
),
rs2 (dob, bty, ydiff) as (
  select dob, DATEADD(Year, ydiff, dob) as bty, ydiff from rs
),
rs3 (dob, nbd) as (
  select dob, DATEADD(yyyy, FLOOR(ABS((-1*(SIGN(DATEDIFF(d, getdate(), bty))))+0.1)) +  ydiff, dob) as nbd from rs2
),
rs4 (dob, ddiff, nbd) as (
  select dob, datediff(d, getdate(), nbd) as diff, nbd from rs3
)
select dob, nbd, ddiff from rs4 where ddiff < 68 order by ddiff
0
Pilso

Solução melhor e fácil:

select * from users with(nolock)
where date_of_birth is not null 
and 
(
      DATEDIFF(dd,
                  DATEADD(yy, -(YEAR(GETDATE())-1900),GETDATE()), --Today
                  DATEADD(yy, -(YEAR(date_of_birth)-1901),date_of_birth)
      ) % 365
) = 30
0
AjitChahal

selecione Data de nascimento, Nome da ordem dos funcionários por Case WHEN convert (nvarchar (5), BirthDate, 101)> convert (nvarchar (5), GETDATE (), 101) e 2 WHEN convert (nvarchar (5), BirthDate, 101) < converter (nvarchar (5), GETDATE (), 101) e depois 3 WHEN converter (nvarchar (5), Data de nascimento, 101) = converter (nvarchar (5), GETDATE (), 101) e depois mais 1 extremidade 4, converter (nvarchar (2), Data de nascimento, 101), converter (nvarchar (2), Data de nascimento, 105)

0
hardik maheshwari

Nozes! Uma boa solução entre quando comecei a pensar sobre isso e quando voltei para responder. :)

Eu vim com:

select  (365 + datediff(d,getdate(),cast(cast(datepart(yy,getdate()) as varchar(4)) + '-' + cast(datepart(m,birthdt) as varchar(2)) + '-' + cast(datepart(d,birthdt) as varchar(2)) as datetime))) % 365
from    employees
where   (365 + datediff(d,getdate(),cast(cast(datepart(yy,getdate()) as varchar(4)) + '-' + cast(datepart(m,birthdt) as varchar(2)) + '-' + cast(datepart(d,birthdt) as varchar(2)) as datetime))) % 365 < @NumDays

Você não precisa converter getdate () como data e hora, certo?

0
clweeks

Você pode usar DATE_FORMAT para extrair as partes do dia e do mês das datas de aniversário.

EDIT: desculpe, eu não vi que ele não estava usando o MySQL.

0
p4bl0

A melhor maneira de conseguir o mesmo é

DECLARE @StartDate DATETIME
DECLARE @EndDate DATETIME

SELECT Member.* from vwMember AS Member  
WHERE (DATEADD(YEAR, (DATEPART(YEAR, @StartDate) -
                      DATEPART(YEAR, Member.dBirthDay)), Member.dBirthDay)
BETWEEN @StartDate AND @EndDate)
0
Ramandeep Singh

Eu enfrentei o mesmo problema com o meu projeto de faculdade há alguns anos atrás. Eu respondi (de uma maneira bastante comum) dividindo o ano e a data (MM: DD) em duas colunas separadas. E antes disso, meu companheiro de projeto estava simplesmente obtendo todas as datas e programando-as. Nós mudamos isso porque era muito ineficiente - não que minha solução fosse mais elegante também. Além disso, provavelmente não é possível fazer isso em um banco de dados que esteja em uso há algum tempo por vários aplicativos.

0
Mostlyharmless

Esta é uma combinação de algumas das respostas que foram testadas. Ele encontrará o próximo brithday após uma determinada data e a idade que eles terão. Os números também limitarão o intervalo que você procura 7 dias = semana etc.

SELECT DISTINCT FLOOR(DATEDIFF(dd,Birthday, @BeginDate) / 365.25) + 1 age,
DATEADD(yyyy, FLOOR(DATEDIFF(dd,Birthday, @BeginDate) / 365.25) + 1, Birthday) nextbirthday, birthday
FROM         table
WHERE     DATEADD(yyyy, FLOOR(DATEDIFF(dd,Birthday, @BeginDate) / 365.25) + 1, Birthday) > @BeginDate  
AND DATEADD(yyyy, FLOOR(DATEDIFF(dd,Birthday, @BeginDate) / 365.25) + 1, Birthday) < DATEADD(dd, @NumDays, @BeginDate)
order by nextbirthday
0
Kyle

Solução para SQLite:

SELECT
    *, 
    strftime('%j', birthday) - strftime('%j', 'now') AS days_remaining
FROM
    person
WHERE :n_days >= CASE
    WHEN days_remaining >= 0 THEN days_remaining
    ELSE days_remaining + strftime('%j', strftime('%Y-12-31', 'now'))
    END
;

As soluções divididas por 325,25 para obter a idade ou trazer a data de nascimento para o ano atual etc. não funcionaram para mim. O que isso faz é calcular o delta dos dois dias do ano (1-366). Se o aniversário ainda não aconteceu este ano, você obtém automaticamente o número correto de dias restantes, com o qual você pode comparar. Se o aniversário já aconteceu, os dias restantes serão negativos e você poderá obter o número correto de dias restantes, adicionando a quantidade total de dias no ano atual. Isso também lida corretamente com os anos bissextos, pois nesse caso o dia extra também será adicionado (usando dayOfYear (31 de dezembro))

0
smoothware

eu acredito que este ticket foi fechado há muito tempo, mas, para o benefício de obter a consulta sql correta, dê uma olhada.

SELECT Employee_Name, DATE_OF_BIRTH
FROM Hr_table 
WHERE 

/**
fetching the original birth_date and replacing the birth year to the current but have to  deduct 7 days to adjust jan 1-7 birthdate.
**/

datediff(d,getdate(),DATEADD(year,datediff(year,DATEADD(d,-7,hr.DATE_OF_BIRTH),getdate()),hr.date_of_birth)) between 0 and 7

-- current date looks ahead to 7 days for upcoming modified year birth date.

order by

-- sort by no of days before the birthday
datediff(d,getdate(),DATEADD(year,datediff(year,DATEADD(d,-7,hr.DATE_OF_BIRTH),getdate()),hr.date_of_birth))
0
maSTAShuFu

A consulta abaixo retornará todo o próximo aniversário do funcionário, é a consulta mais curta.

SELECT 
    Employee.DOB,
    DATEADD(
                mm, 
                (
                    (
                        (
                            (
                                DATEPART(yyyy, getdate())-DATEPART(yyyy, Employee.DOB )
                            )
                            +
                            (
                                1-
                                (
                                    ((DATEPART(mm, Employee.DOB)*100)+DATEPART(dd, Employee.DOB))
                                    /
                                    ((DATEPART(mm, getdate())*100) + DATEPART(dd, getdate()))
                                )
                            )
                        )
                    *12
                    )
                ), 
                Employee.DOB
            ) NextDOB
FROM 
    Employee 
ORDER BY 
    NextDOB ;

A consulta acima cobrirá todo o próximo mês, exceto a data atual.

0
Paresh Patel

Supondo que seja T-SQL, use DATEPART para comparar o mês e a data separadamente.

http://msdn.Microsoft.com/en-us/library/ms174420.aspx

Como alternativa, subtraia 1º de janeiro do ano atual do aniversário de todos e compare com o ano de 1900 (ou o que for o seu ano da época).

0
Patrick Szalapski

A maioria dessas soluções é próxima, mas você precisa se lembrar de alguns cenários extras. Ao trabalhar com aniversários e uma escala móvel, você deve poder lidar com a transição para o próximo mês.

Por exemplo, o exemplo de Stephens funciona muito bem para aniversários até os últimos 4 dias do mês. Então você tem uma falha lógica, pois as datas válidas, se hoje fosse o dia 29, seriam: 29, 30 e 1, 2, 3 do PRÓXIMO mês, então você também precisa fazer isso.

Uma alternativa seria analisar a data do campo de aniversário e sub no ano atual e, em seguida, fazer uma comparação de intervalo padrão.

0
Mitchel Sellers

Outro pensamento: adicione a idade deles em anos inteiros ao aniversário deles (ou mais, se o aniversário deles ainda não tiver acontecido e compare como você fez acima). Use DATEPART e DATEADD para fazer isso.

http://msdn.Microsoft.com/en-us/library/ms186819.aspx

O caso Edge de um intervalo que abrange o ano precisaria ter um código especial.

Dica de bônus: considere usar ENTRE ... E em vez de repetir o operando Birthday.

0
Patrick Szalapski

Faça uma tentativa:

SELECT * FROM Employees
WHERE DATEADD(yyyy, DATEPART(yyyy, @Today)-DATEPART(yyyy, Birthday), Birthday) > @Today 
AND DATEADD(yyyy, DATEPART(yyyy, @Today)-DATEPART(yyyy, Birthday), Birthday) < DATEADD(dd, @NumDays, @Today)
0
Esteban Brenes

Isso deve funcionar ...

DECLARE @endDate DATETIME
DECLARE @today DATETIME

SELECT @endDate = getDate()+6, @today = getDate()

SELECT * FROM Employees 
    WHERE 
    (DATEPART (month, birthday) >= DATEPART (month, @today)
        AND DATEPART (day, birthday) >= DATEPART (day, @today))
    AND
    (DATEPART (month, birthday) < DATEPART (month, @endDate)
        AND DATEPART (day, birthday) < DATEPART (day, @endDate))
0
bastos.sergio

Espero que isso ajude você de alguma forma ....

select Employeename,DOB 
from Employeemaster
where day(Dob)>day(getdate()) and month(DOB)>=month(getDate())
0
Bharathi

Próximo aniversário do funcionário - Sqlserver

DECLARE @sam TABLE
(
    EmployeeIDs     int,
    dob         datetime
)
INSERT INTO @sam (dob, EmployeeIDs)
SELECT DOBirth, EmployeeID FROM Employee

SELECT *
FROM
(
    SELECT *, bd_this_year = DATEADD(YEAR, DATEPART(YEAR, GETDATE()) - DATEPART(YEAR, dob), dob)
    FROM @sam s
) d
WHERE d.bd_this_year > DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 0)
AND d.bd_this_year <= DATEADD(DAY, DATEDIFF(DAY, 0, GETDATE()), 3)
0
samdoss