【问题标题】:How to properly populate the age attribute?如何正确填充年龄属性?
【发布时间】:2009-03-23 10:41:52
【问题描述】:

我有一个 MS-SQL 服务器,它跟踪许多客户端。如果生日已知,则将其存储为名为@9​​87654321@ 的日期时间属性。现在我想要另一个属性年龄,它跟踪客户的当前年龄。由于年龄随时可能发生变化,我认为脚本可能是最好的主意。

我做的第一件事是创建一个存储过程,它计算给定生日的年龄作为日期时间。这是我想出的:

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

我会每天触发一次上面的脚本来填充年龄属性。到目前为止,这就是我所拥有的,但我觉得还有很大的改进空间。

** 谢谢宋老师 **

我最终得到了这样的结果:

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
    )
)

【问题讨论】:

    标签: database tsql


    【解决方案1】:

    与其创建触发器并进行任何手动更新, 解决这个问题的最简单方法是创建一个名为Age计算字段

    这样,您就不必担心 Age 数据不同步(数据保持一致),并且不再需要触发器或手动更新。

    如下图所示,计算人的年龄可能有点麻烦。

    这里是使用的文本

    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
    

    【讨论】:

    • +1 这很酷,但我认为您的计算有问题。仍然是一件很整洁的事情。我正在努力。
    • @nooomi 好吧,脚本的全部内容仅用于演示,如何设置 Age 的默认值完全取决于您想要的方式,而不是 null
    • @Sung 我知道并非常感谢 :)
    • @nooomi:不客气;我最近才发现我可以使用“case”进行条件检查,并且有很多地方可以对其他计算字段使用相同的技巧
    【解决方案2】:

    您真的不应该将年龄存储在数据库中,因为它很容易计算并且每天都在变化。

    我建议您保留出生日期字段并根据需要计算年龄。如果您希望将年龄与其他属性一起选择,请考虑使用视图,也许使用用户定义的函数来计算年龄。

    以下是您可以使用的示例(未经测试)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
    

    然后您可以使用此视图执行查询,根据年龄进行过滤和排序可能会影响性能,如果您定期根据年龄字段进行排序/过滤,那么您可能需要创建索引视图。

    【讨论】:

    • 但是如何实现排序和过滤呢?如果我有一个属性,我可以做例如'select * from Clients where age = 30 and ...' 这样的事情
    • 谢谢+1,我会试试这个。就像我说的,我根本不是 DBA。我只需要通过 Hiberante 调用这些东西。我不知道这是否有效。我们会看到的。
    【解决方案3】:

    使用视图和/或函数。如果您能提供帮助,切勿存储基于完全相同数据的两个字段,因为它们最终会不同步。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2021-05-27
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多