我有一个设置的表,以便一列(属性)包含诸如名字,姓氏,帐号和与数据库中的事物相关的任何其他信息之类的信息。另一列(attributeType)包含一个数字,表示属性是什么,例如1可能是名字,2个姓氏和3个帐号等。还有另一列(enddate)通过在那里设置日期来指示记录是否是最新的。通常它将被设置为当前的9999年,否则将设置为过去的某个日期。描述同一事物的所有数据在另一列(实体)中也具有唯一值,因此实体列中具有相同编号的每个记录将描述一个人。 E.g。
entity attribute attributetype enddate
------ --------- ------------- --------
1 ben 1 9999-1-1
1 alt 2 9999-1-1
1 12345 3 9999-1-1
2 sam 1 9999-1-1
2 smith 2 9999-1-1
2 98765 3 1981-1-1
我想从上表中选择一个具有特定名字和姓氏的人,其名称将是最新的,但如果不是,则不输出帐号。假设该表名为tblAccount,我将对名称部分执行以下操作:
select ta1.attribute '1st Name', ta2.attribute 'last name'
from tblAccount ta1
inner join tblAccount ta2 on ta1.entity = ta2.entity
where ta1.attribute = 'sam' and ta2.attribute = 'smith'
and ta1.attributetype = 1 and ta2. attributetype = 2
and ta1.enddate > getdate() and ta2.enddate > getdate()
并按预期输出名字和姓氏,但是当我想要包含帐号列时,我什么都得不输出:
select ta1.attribute '1st Name', ta2.attribute 'last name', ta3.attribute 'account#'
from tblAccount ta1
inner join tblAccount ta2 on ta1.entity = ta2.entity
left join tblAccount ta3 on ta1.entity = ta3.entity
where ta1.attribute = 'sam' and ta2.attribute = 'smith'
and ta1.attributetype = 1 and ta2. attributetype = 2
and ta1.enddate > getdate() and ta2.enddate > getdate()
and ta3.attributetype = 3 and ta3.enddate > getdate()
我希望看到的是在上面的情况下,帐号#列中没有输出的名字和姓氏,它不是最新的。我做错了什么以及如何更正此查询?
答案 0 :(得分:4)
您必须将日期比较移至连接条件:
select ta1.attribute '1st Name'
, ta2.attribute 'last name'
, ta3.attribute 'account#'
from tblAccount ta1
inner join tblAccount ta2
on ta1.entity = ta2.entity
and ta1.attributetype = 1 and ta2. attributetype = 2
and ta1.enddate > getdate() and ta2.enddate > getdate()
left join tblAccount ta3 on ta1.entity = ta3.entity
and ta3.attributetype = 3 and ta3.enddate > getdate()
where ta1.attribute = 'sam' and ta2.attribute = 'smith'
当它在where子句中时,如果没有帐户,则将getdate()与NULL进行比较,返回NULL。所以没有记录。
编辑:
响应对多个有效记录的有效关注,并使代码更加便于维护:
DECLARE @FNAME VARCHAR(50) = 'sam'
, @LNAME VARCHAR(50) = 'smith'
, @now DATETIME2(7) = GETDATE();
SELECT
name.[1st Name]
, name.[last name]
, name.entity
,
(
select
top 1
ta3.attribute
FROM tblAccount ta3
WHERE
ta3.entity = name.entity
and
ta3.attributetype = 3
and
ta3.enddate > @now
ORDER BY
ta3.enddate
)
FROM
(
select
ta1.attribute '1st Name'
, ta2.attribute 'last name'
, ta.entity
, ROW_NUMBER()
OVER(
PARTITION BY
ta1.entity
ORDER BY
ta1.enddate
) r
from
tblAccount ta1
inner join tblAccount ta2
on
ta1.entity = ta2.entity
and
ta2. attributetype = 2
and
ta2.enddate > @now
and
ta2.attribute = @LNAME
where
ta1.attributetype = 1
and
ta1.attribute = @fname
and
ta1.enddate > @now
) name
WHERE
NAME.r = 1
此代码围绕每个名/姓的一个实体的隐含假设以及执行时间之后的一个完整日期。变量存储过程友好,并允许您更改“截至日期”。如果你坚持使用EAV,你可能会想要存储过程。我假设任何后来的记录只有在该记录到期后才有效,我将在有关日期之后结束第一个记录。也许这太过分了,因为它超出了OP问题的范围,但这是一个有效的观点。
我说“坚持使用EAV”。虽然EAV并不总是坏事;也没有在后面射击某人。在任何一种情况下,如果你希望通过陪审团,你最好有充分的理由。在NoSQL存储模式中它很好,但EAV通常是RDBMS范例的不良实现模式。
虽然从OP后来的评论来看,看起来他受到了更好的原因之一。
答案 1 :(得分:1)
每个属性实际上是此模型中的一个独特实体,但它们都在同一个物理表中共享相同的存储(为什么?)。这会产生:
with data as (
select entity = 1, attribute = 'ben', attributeType=1, enddate = convert(datetime,'99990101') union all
select entity = 1, attribute = 'alt', attributeType=2, enddate = convert(datetime,'99990101') union all
select entity = 1, attribute = '12345', attributeType=3, enddate = convert(datetime,'99990101') union all
select entity = 2, attribute = 'sam', attributeType=1, enddate = convert(datetime,'99990101') union all
select entity = 2, attribute = 'smith', attributeType=2, enddate = convert(datetime,'99990101') union all
select entity = 2, attribute = '67890', attributeType=3, enddate = convert(datetime,'99990101') union all
select entity = 2, attribute = '68790', attributeType=3, enddate = convert(datetime,'20130331') union all
select entity = 2, attribute = '876', attributeType=3, enddate = convert(datetime,'19810101')
)
select top 1
FirstName, LastName, AccountNum
from (
select top 1
a1.entity, FirstName, LastName
from (
select entity, enddate, attribute as FirstName
from data d
where d.enddate >= getdate()
and attributeType = 1
) a1
join (
select entity, enddate, attribute as LastName
from data
where enddate >= getdate()
and attributeType = 2
) a2 on a1.entity = a2.entity
and a1.enddate = a2.enddate
where FirstName = 'sam' and LastName = 'smith'
and a1.enddate >= getdate() and a2.enddate >= getdate()
order by a1.enddate
) E
left join (
select entity, enddate, attribute as AccountNum
from data
where enddate >= getdate()
and attributeType = 3
) a3 on a3.entity = E.entity
order by a3.enddate
返回:
FirstName LastName AccountNum
--------- -------- ----------
sam smith 68790
请注意,至少,会计部门在月份的安静时段输入未来的交易是很常见的,特别是如果这些交易将在月份的繁忙时段(即月末)生效。年度交易也是如此。人们不应该假设只有一个记录可以存在且有效期> GETDATE()。