3

1つの列(属性)に、名、姓、アカウント番号、およびデータベース内の物に関連するその他の情報が含まれるように設定されたテーブルがあります。別の列(attributeType)には、属性が何であるかを示す番号が含まれます。たとえば、1は名、2は姓、3はアカウント番号などです。別の列(enddate)には、日付を指定してレコードが最新かどうかを示します。通常、現在の場合は9999年に設定され、それ以外の場合は過去の日付に設定されます。同じことを説明するすべてのデータは、別の列(エンティティ)でも一意の値を持つため、エンティティ列の同じ番号の各レコードは1人の人物を説明します。例えば

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

私が見たいのは、現在ではない上記の場合、account#列に何も表示されない名前と名前が出力されることです。何が間違っているのでしょうか。また、このクエリを修正するにはどうすればよいですか。

4

2 に答える 2

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

;

このコードは、姓名ごとに1つのエンティティ、実行時間後の1つの終了日という暗黙の仮定を回避します。変数はもう少しストアドプロシージャに対応しており、「現在」の日付を変更できます。また、EAVに固執している場合は、ストアドプロシージャが必要になる可能性があります。問題の日付の後に終了する最初のレコードを取得しています。それ以降のレコードは、そのレコードの有効期限が切れた後にのみ有効になると想定しています。OPの質問の範囲を超えているので、多分それはやり過ぎですが、それは有効なポイントです。

私は「EAVで立ち往生している」と言います。EAVは必ずしも悪いわけではありませんが、どちらも後ろで誰かを撃っていません。どちらの場合でも、陪審員を通過することを期待するのであれば、確固たる正当性を持っている方がよいでしょう。NoSQLストレージパターンでは問題ありませんが、EAVは通常RDBMSパラダイムの実装パターンとしては不十分です。

OPのその後のコメントから、彼はより良い理由の1つにぶつかったようです。

于 2013-03-07T15:29:02.010 に答える
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

少なくとも、会計部門がその月の静かな時間帯に将来のトランザクションを入力することは非常に一般的であることに注意してください。特に、それらのトランザクションがその月の忙しい時間帯(つまり月末)に有効になる場合はそうです。年次取引についても同様です。expiry> getdate()で存在できるレコードは1つだけであると想定するべきではありません。

于 2013-03-07T15:45:44.137 に答える