web-dev-qa-db-ja.com

サブクエリを再利用することは可能ですか?

クエリを実行しようとして問題が発生しました。 2つのテーブルがあります。1つは要素情報を持ち、もう1つは最初のテーブルの要素に関連するレコードを持ちます。アイデアは、同じ行に要素情報といくつかのレコード情報を取得することです。

構造は次のように説明できます。

 table [ id, name ]
 [1, '1'], [2, '2']

 table2 [ id, type, value ]
 [1, 1, '2009-12-02']
 [1, 2, '2010-01-03']
 [1, 4, '2010-01-03']
 [2, 1, '2010-01-02']
 [2, 2, '2010-01-02']
 [2, 2, '2010-01-03']
 [2, 3, '2010-01-07']
 [2, 4, '2010-01-07']

そして、これは私が達成したいことです:

 result [id, name, Column1, Column2, Column3, Column4]

 [1, '1', '2009-12-02', '2010-01-03', , '2010-01-03']
 [2, '2', '2010-01-02', '2010-01-02', '2010-01-07', '2010-01-07']

次のクエリは適切な結果を取得しますが、列ごとにtable2を繰り返す必要があるため、非常に非効率的です。とにかくサブクエリを実行して再利用することは可能でしょうか?

SELECT
      a.id,
      a.name,
      (select min(value) from table2 t where t.id = subquery.id and t.type = 1 group by t.type) as Column1,
      (select min(value) from table2 t where t.id = subquery.id and t.type = 2 group by t.type) as Column2,
      (select min(value) from table2 t where t.id = subquery.id and t.type = 3 group by t.type) as Column3,
      (select min(value) from table2 t where t.id = subquery.id and t.type = 4 group by t.type) as Column4
FROM
      (SELECT distinct id
       FROM table2 t
       WHERE (t.type in (1, 2, 3, 4))
             AND t.value between '2010-01-01' and '2010-01-07') as subquery
       LEFT JOIN table a ON a.id = subquery.id
22
Gothmog

集計をCTE(共通テーブル式)に取り出すことができます。

with minima as (select t.id, t.type, min(value) min_value
                from table2 t
                where t.type in (1,2,3,4)
                group by t.id, t.type)
select a.id, a.name,
       (select min_value from minima where minima.id = subquery.id and minima.type = 1) as column1,
       (select min_value from minima where minima.id = subquery.id and minima.type = 2) as column2,
       (select min_value from minima where minima.id = subquery.id and minima.type = 3) as column3,
       (select min_value from minima where minima.id = subquery.id and minima.type = 4) as column4
from (select distinct id from table2 t where t.type in (1,2,3,4) and t.value between '2010-01-01' and '2010-01-07') as subquery
     left join a on a.id = subquery.id

もちろん、これが実際に何らかのメリットがある(またはサポートされている)かどうかは、環境とデータセットによって異なります。

別のアプローチ:

select xx.id, a.name, xx.column1, xx.column2, xx.column3, xx.column4
from (
      select id,
             max(case type when 1 then min_value end) as column1,
             max(case type when 2 then min_value end) as column2,
             max(case type when 3 then min_value end) as column3,
             max(case type when 4 then min_value end) as column4
      from (select t.id, t.type, min(value) min_value
            from table2 t
            where t.type in (1,2,3,4)
            group by t.id, t.type) minima
      group by id
) xx left join a on a.id = xx.id
order by 1
28
araqnid

後のデータベース製品の一部(Oracle、SQL Server 2005、SQL Server 2008など)は、共通テーブル式(略してCTE)を作成する機能を提供します。これで、次のようにサブクエリを再利用できます。

With Subquery As
    (
    Select Id
        , Min( Case When T.TypeId = 1 Then Value End ) As MinType1
        , Min( Case When T.TypeId = 2 Then Value End ) As MinType2
        , Min( Case When T.TypeId = 3 Then Value End ) As MinType3
        , Min( Case When T.TypeId = 4 Then Value End ) As MinType4
    From Table2 As T
    Where T.Type In(1,2,3,4)
        And T.Value Between '2010-01-01' And '2010-01-07'
    Group By Id
    )
Select A.Id, A.Name, S.MinType1, S.MinType2, S.MinType3, S.MinType4
From Subquery As S
    Left Join Table As A
        On A.Id = S.Id

確かに、これは次のようなものと大差ありません。

Select  A.Id, A.Name, S.MinType1, S.MinType2, S.MinType3, S.MinType4
From    (
        Select Id
            , Min( Case When T.TypeId = 1 Then Value End ) As MinType1
            , Min( Case When T.TypeId = 2 Then Value End ) As MinType2
            , Min( Case When T.TypeId = 3 Then Value End ) As MinType3
            , Min( Case When T.TypeId = 4 Then Value End ) As MinType4
        From Table2 As T
        Where T.Type In(1,2,3,4)
            And T.Value Between '2010-01-01' And '2010-01-07'
        Group By Id
        ) As S
    Left Join Table As A
        On A.Id = S.Id
7
Thomas