SQL SELECT语句表达式值重用于其他表达式

时间:2012-08-23 21:23:38

标签: mysql sql sql-server

我有一个在mysql中有大量行数的表(虽然我正在寻找通用的SQL解决方案)

very_big_table(INT a, INT b, INT c, ...)

我想要SELECT语句

SELECT a, 
    (b + c) as expression1,
    (b + c + a) AS expression2 -- basically (expression1 + a)
FROM very_big_table
WHERE ...
GROUP BY a
ORDER BY a DESC

只要表达式1很简单,这看起来很好并且易于阅读 但是当CASE-WHEN / IFNULL()/ SUM()/ MIN()/ STRCAT()或某些运算符在这些表达式中起作用时,它很难读取和调试。

我已经完成了一些已经问过的问题 assigning mysql value to variable inline
Use value of a column for another column (SQL Server)?
How to use conditional columns values in the same select statement?

但是,如果我使用描述的方法

SELECT a, 
    expression1,
    (expression1 + a) AS expression2
FROM 
    (SELECT a,
        (b + c) AS expression1
    FROM very_big_table
    WHERE ...
    GROUP BY a) as inner_table
ORDER BY a DESC

这样可以正常工作,但是这个查询需要花费大约70倍的时间来执行。至少只有一次,我就解雇了它 如果我在输出列中有多个级别的表达式,该怎么办?

在不影响可读性的情况下,有没有优雅的方法来解决这个问题?

BTW为什么SQL标准或供应商不支持select语句中的表达式重用别名引用? (假设在单个SELECT语句表达式中没有循环评估。在这种情况下编译器失败)

3 个答案:

答案 0 :(得分:4)

您可以使用用户定义的变量来解决您的问题。您的SQL可以重写为:

SELECT a, 
    @expr1 := (b + c) as expression1,
    (@expr1 + a) AS expression2
FROM very_big_table
WHERE ...
GROUP BY a
ORDER BY a DESC

您可以参考this post

答案 1 :(得分:0)

解决方法假设表达式不是sql select子子句,而是嵌套的运算符/函数纯粹作用于原始“select from”子句中检索的数据,因此手头的任务纯粹而且简单地使代码“看起来”漂亮”: 创建用户定义的函数

然后你的查询看起来像

select a
, myfunction( b, c)
, myfunction( b, c) + a
...

上行 - 这清理了“选择代码”,这是您声明的目标,也可以集中管理更复杂的逻辑

缺点 - 这不是“通用sql”,它将移植到其他系统,如果功能很少被重复使用,可能不值得投资

答案 2 :(得分:0)

对于Postgresql用户

如果要最大程度地提高复杂计算和/或逻辑的可读性,请将计算封装在其自己的函数中

create table x
(
    a int not null,
    b int not null,
    c int not null
);

create table y
(
    a int not null,
    z int not null
);


select * from x;

insert into x VALUES
(1,2,3), (4,5,6);

insert into y values
(1, 100);

具有复杂计算和/或逻辑的功能:

create or replace function computer
(
    rx x, -- this emphasizes that the source of data come from table x
    ry y, -- this emphasizes that the source of data come from table x

    out expression1 int, out expression2 int, out expression3 int
)
as $$
begin
    expression1 := rx.b + rx.c;
    expression2 := expression1 + rx.a;
    expression3 := expression2 + ry.z;
end;
$$ language 'plpgsql';

测试查询:

select x.a, x.b, x.c, y.z, answer.*
from x
join y on x.a = y.a
cross join lateral computer(x,y) answer 

输出:

enter image description here

如果您只需要快速测试该函数的返回值,则可以使用括号-星号语法对其进行扩展:

select x.a, x.b, x.c, y.z, (computer(x, y)).*
from x
join y on x.a = y.a

但是不要在生产环境中使用该函数,如果该函数具有50个返回的列,则该函数将被调用50次。在生产中使用cross join lateral。参见:How can you expand a "condensed" PostgreSQL row into separate columns?

如果您的函数不需要担心数据源来自何处,只需在函数的参数上使用record类型

create or replace function computer_b
(
    anon record,
    out expression1 int, out expression2 int, out expression3 int
)
as $$
begin
    expression1 := anon.b + anon.c;
    expression2 := expression1 + anon.a;
    expression3 := expression2 + anon.z;
end;
$$ language 'plpgsql';  

测试查询:

with data_source as
(
    select x.*, y.z
    from x
    join y on x.a = y.a
)
select ds.*, answer.*
from data_source ds
cross join lateral computer_b(ds) answer