Possible to store value of one select column and use it for the next one?

孤街浪徒 提交于 2021-02-19 08:01:10

问题


Is it possible to store or cache values that are part of one select column and then be used for the next one? For example,

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

Is it possible to do something like that so I don't have to write the same complex query twice?


回答1:


You need CROSS APPLY here, it can refer to outer references, no annoying subqueries or CTEs needed:

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

Some more tricks you can do with APPLY:

1. Top 1 per group of child table:

A classic solution to the "top 1 per group" is to use row_number(). This can often result in huge scans, especially when the number of distinct outer values is small relative to the child table.

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

Instead we can do:

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

Note: OUTER APPLY conceptually replaces a left join, i.e. returns nulls instead of no rows.


2. Unpivoting

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. Exploding out a row a variable number of times:

Say we want to take an amount, and split it into different rows. If the amount <= 50 then one row of amount, if > 50 then two rows, one of 50 and one of the rest:

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



回答2:


I think in that case, you should use CTE (common table expression). for example

;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



回答3:


One way to do it is select the resultset and work from there.

This works by selecting the firstcolumn with all its complex stuff and return it in a resultset called t.

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

Then you can very simply select from t and it will return FirstColumn as if it was a normal simple column, so you can add to it without having to repeat the complex stuff.

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

An example:

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

As you can see, I don't need to repeat the calculation for t.Firstcolumn to add stuff to it for my SecondColumn.

Another option is to use cross apply (there is an explanation in another answer here so I won't repeat that).
But do test which solution performs best for you, cross apply could have a performance penalty in some cases, and so can my solution. So just test what works best for you.




回答4:


A sub-query is the simplest form. You can nest sub-queries to any level you like performing calculations along the way:

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;

Note: a CTE is essentially the same thing just prettier (maybe).

A lateral join is another option as it allows you to reference columns in the base table and perform calculations on them which are then available for use in the resultset:

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);


来源:https://stackoverflow.com/questions/65818438/possible-to-store-value-of-one-select-column-and-use-it-for-the-next-one

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!