Как правильно заполнить атрибут возраста? - PullRequest
0 голосов
/ 23 марта 2009

У меня есть MS-SQL Server, который отслеживает количество клиентов. Если день рождения известен, он сохраняется как атрибут datetime, называемый dayOfBirth. Теперь я хотел бы иметь еще один атрибут age, который отслеживает текущий возраст клиента. Поскольку возраст может измениться в любой день, я подумал, что сценарий может быть лучшей идеей.

Первое, что я сделал, было создание хранимой процедуры, которая вычисляет возраст с учетом дня рождения как datetime. Вот что я придумал:

USE [MyDB]
SET ANSI_NULLS ON
GO
SET QUOTED_IDENTIFIER ON
GO

CREATE PROCEDURE CalculateAge
@dayOfBirth datetime,
@age INT OUTPUT
AS

DECLARE @today datetime, @thisYearBirthDay datetime
DECLARE @years int

SET @today = GETDATE()
SET @thisYearOfBirth = DATEADD(year, @dayOfBirth, @today), @dayOfBirth)
SET @years = DATEDIFF(year, @dayOfBirth, @today) - (CASE WHEN @thisYearBirthDay > @today THEN 1 ELSE 0 END)
SET @age = @years

Впоследствии я создал еще один скрипт, который запускает все записи с ненулевым атрибутом dayOfBirth и обновляет возраст, заполненный соответствующим образом.

USE [MyDB]
GO

DECLARE @age int;
DECLARE @birth datetime;
DECLARE @id intl
DECLARE cursorQuery CURSOR FOR SELECT clientId FROM Clients WHERE dayOfBirth IS NOT NULL;

OPEN cursorQuery

FETCH NEXT FROM cursorQuery INTO @id

WHILE @@FETCH_STATUS = 0
BEGIN
    SET @birth = (SELECT dayOfBirth from Kunden where clientId=@id);
    EXEC dbo.CalculateAge @birth, @age OUTPUT;
    UPDATE Clients SET Age = @age WHERE clientId = @id;
    FETCH NEXT FROM cursorQuery INTO @id
END

CLOSE cursorQuery
DEALLOCATE cursorQuery

Я бы запустил приведенный выше скрипт один раз в день, чтобы заполнить атрибут age. Это то, что я имею до сих пор, но я чувствую, что есть много возможностей для улучшения.

** Спасибо Сунг Мейстер **

У меня получилось что-то вроде этого:

CREATE TABLE Client (

ID int identity(1,1) primary key,
DOB datetime null,
AGE as (case
    when DOB is null then null
    else DATEDIFF(YY,DOB,GETDATE()) - CASE WHEN (MONTH(GETDATE()) = MONTH(DOB) AND DAY(DOB) > DAY(GETDATE()) OR MONTH(GETDATE()) > MONTH(DOB)) THEN 1 ELSE 0 END
    end
    )
)

Ответы [ 3 ]

2 голосов
/ 23 марта 2009

Вместо создания триггера и выполнения каких-либо ручных обновлений, Самый простой способ обойти это - создать вычисляемое поле с именем Age.

Таким образом, вам не нужно беспокоиться о несинхронизации данных возраста (данные остаются согласованными) и больше не требуется триггер или обновление вручную.

Расчет возраста человека может быть немного сложным, как вы можете видеть на картинке ниже.

alt text

Вот используемый текст

create table #Person (
ID      int identity(1, 1) primary key,
DOB     datetime null,
Age     as 
        (case 
                when DOB is null then null
                --; Born today!
                when datediff(d, DOB, getdate()) = 0 then 0
                --; Person is not even born yet!
                when datediff(d, DOB, getdate()) < 0 then null
                --; Before the person's B-day month so calculate year difference only
                when cast(datepart(m, getdate()) as int) > cast(datepart(m, DOB) as int) 
                    then datediff(year, DOB, getdate())
                --; Before Person's b-day
                else datediff(year, DOB, getdate()) - 1
            end)
)
insert  #Person select GetDate()
insert  #Person select null
insert  #Person select '12/31/1980'

select  * from  #Person

update  #Person
set     DOB = '01/01/1980'
where   ID = 2

select  * from  #Person
2 голосов
/ 23 марта 2009

Вы действительно не должны хранить возраст в базе данных, так как он легко рассчитывается и меняется ежедневно.

Я бы посоветовал вам сохранить поле даты рождения и просто подсчитать возраст по мере необходимости. Если вы хотите, чтобы возраст выбирался вместе с другими атрибутами, рассмотрите представление, возможно, с определенной пользователем функцией для вычисления возраста.

Ниже приведен пример (непроверенный) UDF, который вы можете использовать

CREATE FUNCTION age 
(
    @userId int
)
RETURNS int
AS
BEGIN

    DECLARE @Result int

    SELECT @Result = DATEDIFF(year, dateofBirth, getDate())
    FROM    person
    WHERE   userId = @userId

    RETURN @Result

END
GO

Тогда в ваших запросах вы можете сделать что-то похожее на следующее,

SELECT *,
       dbo.age(userId) as age
FROM   person

Отвечая на ваш вопрос о сортировке и т. Д., Вы можете создать представление данных и использовать его для отображения данных примерно так (не проверено)

CREATE VIEW personview(firstname, surname, dateOfBirth,age) AS 

SELECT firstname,
       surname,
       dateOfbirth,
       dbo.age(userid)
FROM   person

Затем вы можете использовать это представление для выполнения ваших запросов, может произойти снижение производительности для фильтрации и сортировки по возрасту, и если вы регулярно сортируете / фильтруете по полю возраста, то вы можете создать индексированное представление.

0 голосов
/ 23 марта 2009

Используйте вид и / или функцию. Никогда не храните два поля, которые основаны на одних и тех же данных, если вы можете помочь, так как в конечном итоге они не синхронизируются.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...