2017-12-22 28 views
-4

私は好奇心を持っています。ここにベストプラクティスを教えてください。自己結合表とcteの再帰

empnameにはempnameとmgr_nameの列が含まれ、empnameにはempnameが含まれ、mgr_nameには特定の従業員のマネージャ名が含まれていることがわかります。私の要件は、少なくとも1人のマネージャー+マネージャーである従業員に報告する従業員を見つけることです。

私はクエリを書き、正しいレコードを返しています。

create table test(empname varchar(10),mgr_name varchar(10)) 

insert into test 
values('a','b'),('b',null),('c',null),('d','f'),('f',null),('g',null),('h',null) 

クエリ:私はこのクエリを最適化し、代わりに自己のテーブルを使用してのCTEを使用しようとした

select a.empname 
from test a , test b 
where a.mgr_name =b.empname 
union 
select b.empname 
from test a , test b 
where a.mgr_name =b.empname 

;with cte as (
select * 
from test 

) 
select a.empname 
from cte a , cte b 
where a.mgr_name =b.empname 
union 
select b.empname 
from cte a , cte b 
where a.mgr_name =b.empname 

のように複数回参加しかし、私は何の改善がないことがわかりました実行計画に表示されます。

この場合、クエリを最適化することを決定するために助けが必要です。他のアプローチがこの場合最適化されるかどうかをご提案ください。あなたはまだ2回の走査を行うが、のを取得する必要があります

SELECT empname 
FROM test 
WHERE mgr_name IS NOT NULL 
UNION 
SELECT mgr_name 
FROM test 
WHERE mgr_name IS NOT NULL; 

+4

**たいクエリの[悪い習慣を蹴るためには:古いスタイルを使用してJOINを](http://sqlblog.com/blogs/aaron_bertrand/archive/2009/10/08/bad-habitsどのように私たちが手助けすることができますこれらのアプローチの計画、STATSのIOとDDLを共有しないで - 蹴って - 古いスタイルのjoins.aspxを使用していますか? – Sami

+0

? –

+1

*なぜCTEを導入することがここで助けになると思いましたか? –

答えて

-1

だけmgr_nameNULLの価値を持っているかどうかをチェックし...テーブルを4回を照会する必要はありませんJOINの暗黙の削除と2回のスキャン(照会の場合は、UNIONの両側で表が2回スキャンされました)。

+0

これと同じではありませんFKは存在しません – Paparazzi

+0

パパラッチは "同じではない"と何ですか?どのような外部キーですか? OPはDDL上のキーを指定しません。上記のクエリは、OPと同じ結果セットを返します。あなたがコメントしたときに詳しく述べてください:「うまくいかない」というのは決して役に立たない。 – Larnu

+0

正確には、OPはキーを指定していません。 mgr_nameが無効であっても削除されます。 – Paparazzi

0

すべての人々のリストを提供するために、以下、それらは(1)誰かまたは(b)の管理者によって管理されているかどうかの指定されたメトリック(なし)の結果への最適化:

declare @Employees as Table (EmployeeName VarChar(10), ManagerName VarChar(10)); 

insert into @Employees (EmployeeName, ManagerName) values 
    ('a', 'b'), ('b', null), ('c', null), ('d', 'f'), ('f', null), ('g', null), ('h', null); 

select distinct(Name), 
    case when exists 
    (select 42 from @Employees where EmployeeName = Name and ManagerName is not NULL) then 1 else 0 end as IsManaged, 
    case when exists 
    (select 42 from @Employees where ManagerName = Name) then 1 else 0 end as IsManager 
    from (
    select EmployeeName as Name 
     from @Employees 
    union all 
    select ManagerName 
     from @Employees 
     where ManagerName is not NULL) as PH; 

-- Alternatively: 
select Name, Max(IsManaged) as IsManaged, Max(IsManager) as IsManager 
    from (
    select EmployeeName as Name, case when ManagerName is NULL then 0 else 1 end as IsManaged, 0 as IsManager 
     from @Employees 
    union all 
    select ManagerName, 0, 1 
     from @Employees 
     where ManagerName is not NULL) as PH 
    group by Name; 

自己管理にサポートされています。

0

実際には、CTEは何もしません。あなたはちょうど1つのテーブルを正確にコピーしているので、cteまたはtestの使用との違いはありません。

; with cte as (
    select * -- This won't work because duplicate naming, but you can change that 
    from test a, test b -- my soul weeps at this terrible syntax 
) 

どのような場合でも、これを行う必要はありません。ここでは、

select empname 
from test 
where mgr_name is not null 
    or empname in (
     select mgr_name 
      from test 
    )