-1

私は検索しましたが、この場合に役立つものは何も見つかりませんでした。DB は MS SQL Server 2008 R2 です。

実際に CTE を使用している複雑なクエリがあり、サブクエリの 1 つが各列に対して 1 つの行を返す必要があります。しかし、私はそれを行う方法を理解できません。

これがコードです。

select MemberFK as LMFK, ContractNr, Contractbegin as LC, ContractEnd as LCE from Contracts as V 
WHERE 
MainContract=1
AND Description not like '%Mitarbeiter%'
AND Description not like '%Kind%'
AND Description not like '%Teen%'
AND Description not like '%Kid%'
AND Contractbegin = 
(select TOP 1 MAX(V1.Contractbegin) from Contracts as V1 WHERE 
(V1.Description like '%Gold%'
OR V1.Description like '%Silber%'
OR V1.Description like '%Bronze%'
OR V1.Description like '%Executive%' )
AND V1.ContractEnd>V1.Contractbegin --to flush out some erroneous rows
AND V1.MemberFK =V.MemberFK) 

問題のある行の例:

LMFK    ContractNr  LC  LCE
649 644 2002-10-01 00:00:00 2008-04-30 00:00:00
755 646 2002-11-01 00:00:00 2002-11-01 00:00:00
755 647 2002-11-01 00:00:00 2008-07-31 00:00:00
754 648 2002-11-01 00:00:00 2008-07-31 00:00:00

LMFK ごとに 1 行のみを取得するためにやりたいことは、他の条件を満たす最大の ContractNr を取得することです。ご覧のとおり、ContractNr 646 は有効ではありませんが、647 は有効です。V1.ContractEnd>V1.Contractbeginコンディションが良くないようです。

どんな助けでも大歓迎です!

4

2 に答える 2

1

where 句で ContractNr を使用する必要があります。クエリは、一致するすべてのレコードを返します max Contractbegin

select MemberFK as LMFK, ContractNr, Contractbegin as LC, ContractEnd as LCE from Contracts as V 
WHERE 
MainContract=1
AND Description not like '%Mitarbeiter%'
AND Description not like '%Kind%'
AND Description not like '%Teen%'
AND Description not like '%Kid%'
AND ContractNr = 
(select TOP 1 MAX(V1.ContractNr) from Contracts as V1 WHERE 
(V1.Description like '%Gold%'
OR V1.Description like '%Silber%'
OR V1.Description like '%Bronze%'
OR V1.Description like '%Executive%' )
AND V1.ContractEnd>V1.Contractbegin --to flush out some erroneous rows
AND V1.MemberFK =V.MemberFK) 
于 2013-09-27T15:01:42.830 に答える
0

私はいつもおもちゃのテーブルやデータベースを作成して自分のアイデアを披露するのが好きです. 以下はそのようなスニペットの 1 つです。

あなたのソリューションと私のソリューションについてのコメントを次に示します。

1 - 先頭にワイルド カードを使用して列をパターンと比較すると、クエリ オプティマイザーはインデックスを使用できません。したがって、これは全表スキャンになります。

2 - 私は常に null のテストが好きです。合体は、null 文字列をデフォルトで空の文字列にする優れた方法です。

3 - where ロジックに永続化された計算列を使用する場合、レコードが挿入または更新されるとデータベースに格納されます。

4 - 永続化された計算列はインデックスを持つことができるため、大きなテーブルでのテーブル スキャンが不要になります。

小さなテーブルの場合、テーブル スキャンの方が高速であるため、クエリ インデックス ヒントを使用する必要がありました。

また、member_fk や begin_date を追加することもできます。IE - より多くの作業/実際の例のテスト。

5 - 最後になりましたが、ウィンドウの分割と row_number() 関数を使用して最新の行を見つけます。

WHERE 句のステートメントの SELECT 部分で計算を参照できないため、これを CTE にバンドルしました。

ここにはいくつかの良い概念があります:

  • ワイルド カード パターン検索は、全テーブル スキャンに等しい
  • null の常にテスト/アカウント
  • 速度のためのインデックスとして永続化された計算列
  • 上位の結果を選択するためのグループ化関数の使用

ご不明な点がございましたら、お気軽にお問い合わせください。

心から

ジョン

-- Drop old table
if object_id('tempdb.dbo.contracts') > 0
  drop table tempdb.dbo.contracts;
go

-- Create new table
create table tempdb.dbo.contracts
(
  id_num int identity(1,1),
  member_fk int,
  main_flag bit,
  begin_date smalldatetime,
  end_date smalldatetime,
  description_txt varchar(512),

  do_not_use_flag as
  (
    -- must have these words
    ( 
      case
        when lower(coalesce(description_txt, '')) like '%gold%' then 0
        when lower(coalesce(description_txt, '')) like '%silver%' then 0
        when lower(coalesce(description_txt, '')) like '%bronze%' then 0
        when lower(coalesce(description_txt, '')) like '%executive%' then 0
        else 1
      end
    ) 

    +

    -- must not have these words
    ( 
      case
        when lower(coalesce(description_txt, '')) like '%mitarbeiter%' then 1
        when lower(coalesce(description_txt, '')) like '%kind%' then 1
        when lower(coalesce(description_txt, '')) like '%teen%' then 1
        when lower(coalesce(description_txt, '')) like '%kid%' then 1
        else 0
      end
    ) 

    +

    -- must have begin_date <= end_date
    ( 
      case
        when begin_date is null then 1
        when end_date is null then 0
        when begin_date <= end_date then 0
        else 1
      end
    )

    + 
    (
      -- toss out non-main records
      case
        when main_flag = 1 then 0
        else 1
      end
    )
  ) persisted

);
go

-- add index on id include flag
create nonclustered index ix_contracts
    on tempdb.dbo.contracts (do_not_use_flag);
go

-- add data to table
insert into tempdb.dbo.contracts (member_fk, main_flag, begin_date, end_date, description_txt) 
values

-- shows up
(1, 1, getdate() - 5, getdate(), 'Silver - good contract for DBA'),

-- main contract <> 1
(1, 0, getdate() - 5, getdate(), 'Gold - good contract for DBA'),

-- no flag = true
(1, 1, getdate() - 5, getdate(), 'Bronze - good contract for Teen'),

-- end < begin
(1, 1, getdate(), getdate()-5, 'Bronze - good contract for DBA'),

(2, 1, getdate() - 5, getdate(), 'Executive - good contract for DBA');
go

-- wait 5 seconds
WAITFOR DELAY '00:00:02';
go

insert into tempdb.dbo.contracts (member_fk, main_flag, begin_date, end_date, description_txt) 
values
(2, 1, getdate() - 4, getdate(), 'Executive - good contract for DBA');
go


-- show the raw data
select * from tempdb.dbo.contracts as c
go

-- show the data
;
with cte_contract_by_recent_begin_dte
as
(
  select 
    ROW_NUMBER() OVER (PARTITION BY member_fk ORDER BY begin_date desc) as top_id, 
    *
  from 
    tempdb.dbo.contracts as c with(index(ix_contracts))
  where 
    c.do_not_use_flag = 0 
)
select * from cte_contract_by_recent_begin_dte as cte where cte.top_id = 1
于 2013-09-27T17:55:34.703 に答える