代码之家  ›  专栏  ›  技术社区  ›  Gothmog

是否可以重用子查询?

  •  18
  • Gothmog  · 技术社区  · 16 年前

    我在执行查询时遇到一些问题。我有两个表,一个包含元素信息,另一个包含与第一个表的元素相关的记录。其思想是在同一行中获取元素信息和多个记录信息。

    结构可以这样解释:

     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']
    

    下面的查询得到了正确的结果,但在我看来效率极低,必须为每列迭代表2。无论如何都可以执行子查询并重用它吗?

    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
    
    2 回复  |  直到 10 年前
        1
  •  26
  •   Chris    13 年前

    可以将聚合提取到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
    
        2
  •  5
  •   Thomas    16 年前

    一些较新的数据库产品(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