我有一个在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语句表达式中没有循环评估。在这种情况下编译器失败)
答案 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
输出:
如果您只需要快速测试该函数的返回值,则可以使用括号-星号语法对其进行扩展:
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