【问题标题】:Possible to store value of one select column and use it for the next one?可以存储一个选择列的值并将其用于下一个吗?
【发布时间】:2021-04-25 08:14:02
【问题描述】:

是否可以存储或缓存属于一个选择列的一部分的值,然后用于下一个?例如,

select 
       FirstColumn = (complex query returns a value based on ThirdColumn),
       SecondColumn = (uses the same value returned from above + does some additional calculations.)
from SomeTable

是否可以这样做,这样我就不必编写两次相同的复杂查询?

【问题讨论】:

  • 以下答案之一是否回答了您的问题?如果是这样,请考虑接受它(并支持所有帮助)。如果不是,请提供更多信息。

标签: sql-server tsql


【解决方案1】:

我认为在这种情况下,您应该使用 CTE(公用表表达式)。例如

;WITH CTE1 AS
(
 select col1 from table ---- your query 
),
CTE2 as 
(
 select col2 from table --- your query
)
select col1, Col2
from CTE1 join CTE2 on --- do anything you want

【讨论】:

    【解决方案2】:

    子查询是最简单的形式。您可以将子查询嵌套到您喜欢执行计算的任何级别:

    select 
        ComplexValue1 as FirstColumn
        , ComplexValue1 + ComplexValue2 as SecondColumn
    from (
      select 
        {complex query returns a value} ComplexValue1
        , {complex query returns a value} ComplexValue2
        , {any other required columns}
      from SomeTable
    ) X;
    

    注意:CTE 本质上是一样的,只是更漂亮(也许吧)。

    横向连接是另一种选择,因为它允许您引用基表中的列并对其执行计算,然后可在结果集中使用:

    select
        X.ComplexValue1 as FirstColumn
        , X.ComplexValue1 + ST.ComplexValue2 as SecondColumn
    from SomeTable ST
    cross apply (select {complex query returns a value referencing ST}) as X (ComplexValue1);
    

    【讨论】:

    • 也许只有我一个人,但我一直觉得(来自程序)横向比复杂表达式的子查询更有意义。我知道有些感觉不同,我想这只是口味问题。
    • @Charlieface 这取决于你需要它做什么。我的规则是,如果您可以通过连接解决它,那么请使用连接,因为它通常更快。但有时子查询可以更好地证明,这取决于你在其中做什么
    • @GuidoG Obvs 是的,这是真的。我倾向于将它用于以下方面:分组子表;每组前1名;以前表格的复杂计算。如果您想将行加倍(unpivot)也非常有用,您甚至可以有条件地执行此操作(union allwhere 在应用内)并获得不同数量的行。
    • @GuidoG 和 Dale K。请参阅我的答案的进一步编辑
    • @Charlieface 非常聪明 :) 只是希望你没有超载 OP。
    【解决方案3】:

    一种方法是选择结果集并从那里开始工作。

    这通过选择第一列及其所有复杂内容并将其返回到名为t 的结果集中。

    select t.FirstColumn,
    from   ( select FirstColumn --(complex query returns a value based on ThirdColumn)
             from SomeTable
           ) t
    

    然后您可以非常简单地从t 中进行选择,它会返回FirstColumn,就像它是一个普通的简单列一样,因此您可以添加到它而无需重复复杂的内容。

    select t.FirstColumn,
           secondcolum (that uses t.FirstColum as it where a normal field)
    from   ( select FirstColumn
             from SomeTable
           ) t
    

    一个例子:

    select t.FirstColumn,
           (t.FirstColum / 123.0) * 50 as SecondColumn
    from   ( select (s.A + s.B) * s.C as FirstColumn
             from   sometable s
           ) t
    

    如您所见,我不需要重复计算 t.Firstcolumn 来为我的 SecondColumn 添加东西。

    另一种选择是使用cross apply(这里的另一个答案中有解释,所以我不会重复)。
    但是请测试哪种解​​决方案最适合您,交叉应用在某些情况下可能会降低性能,我的解决方案也是如此。因此,只需测试最适合您的方法。

    【讨论】:

      【解决方案4】:

      这里需要CROSS APPLY,它可以引用外部引用,不需要烦人的子查询或CTE:

      select col1, col2
      from table1 as outer_table
      
      -- can also have multi-row values
      cross apply (values (complex_expression_1) ) as v1 (col1)
      cross apply (values (expression_referring_to_col1) ) as v2 (col2)
      
      -- alternate syntax, select without from returns a single row
      cross apply (select complex_expression_1 as col1 ) AS v1
      cross apply (select expression_referring_to_col1 as col2 ) as v2
      
      -- you can also do anything you like in there, can be one or multiple rows
      cross apply (
          select complex_expression_1 as col1 
          from othercomplexjoin as o
          where o.join_column = outer_table.join_column
      ) AS v1
      

      你可以用APPLY做更多的技巧:

      1.每组子表前 1 名:

      “每组前 1 名”的经典解决方案是使用 row_number()。这通常会导致大量扫描,尤其是当不同外部值的数量相对于子表而言较少时。

      select
          o.id,
          lastPayment.Date
      from order_header as o
      join
      ( select *, row_number() over (partition by order_id order by date desc) as rn
       from payments
      ) as lastPayment on ...
      where lastPayment.rn = 1
      

      我们可以这样做:

      select
          o.id,
          lastPayment.Date
      from order_header as o
      cross apply
      ( select top (1) *
       from payments as p
       where p.order_id = o.id
       order by date desc
      ) as lastPayment
      

      注意:OUTER APPLY 在概念上替换左连接,即返回空值而不是无行。


      2。不旋转

      select
          o.id,
          customer.*
      from order_header as o
      cross apply ( values    -- This returns two rows for every order_header
          ( 'DeliveryCustomer', o.deliveryCustomer ),
          ( 'billingCustomer', o.billingCustomer )
      ) as customer (type, name)
      

      3.以可变次数分解一行:

      假设我们要取一个金额,并将其分成不同的行。如果是amount <= 50,则为一排amount,如果为> 50,则为两排,50 之一,其余各之一:

      select t.id, v.amount
      from table as t
      cross apply (
          select case when amount > 50 then 50 else amount end as amount
          union all
          select amount - 50   -- note this row will not appear if amount < 50
          where amount > 50
      ) v
      

      【讨论】:

      • cross apply 绝对是正确的选择。但是,既然 OP 已经有了复杂的查询,为什么还要使用values?只需直接使用select
      • 我认为 OP 的意思是“复杂计算”而不是“复杂查询”
      • 我的意思是执行复杂计算但只返回单个值的复杂查询。我将如何直接使用查询?
      • 增加了更多可能的语法
      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2020-04-27
      • 1970-01-01
      • 2020-08-09
      • 2015-12-03
      • 2020-07-07
      相关资源
      最近更新 更多