6

我在 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() 或某些运算符在这些表达式中发挥作用时,它很难阅读和调试。

我已经解决了一些已经提出的问题,
将 mysql 值分配给变量内联
使用列的值用于另一列(SQL Server)?
如何在同一选择语句中使用条件列值?

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

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 倍以上的时间来执行。至少当我解雇它时,虽然只有一次。
如果我在输出列中 有多个级别的表达式怎么办?

有什么优雅的方法来处理这个问题,而不影响可读性?

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

4

4 回答 4

7

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

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

你可以参考这篇文章

于 2016-06-30T06:57:05.173 回答
2

我有两个选择。。

使用您的实际查询作为子查询

 select a,exp1,exp1+a as exp2
 from (SELECT a 
             ,(b + c) as exp1
       FROM very_big_table
       WHERE ...
       GROUP BY a
 )V
 ORDER BY a DESC

或为您的查询添加外部应用语句...

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

我猜第二个选项,使用外部应用,更适合阅读......

请记住,每行的外部应用运行

因此,如果 exp1 必须从表中访问大量数据,这可能是个坏主意。

无论如何,仅使用您已经在实际查询中获得的字段,添加它不会有很大的成本。

所以..你会选择什么方式?

于 2013-01-23T19:20:17.460 回答
0

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

那么你的查询看起来像

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

好处 - 这会清理您既定目标的“选择代码”,您也可以集中管理更复杂的逻辑

缺点 - 这不是将移植到其他系统的“通用 sql”,如果函数很少被重用,可能不值得花时间投资

于 2012-11-17T17:19:51.883 回答
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在生产中使用。请参阅:如何将“压缩”的 PostgreSQL 行扩展为单独的列?

如果您的函数不需要关心数据源来自哪里,只需使用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
于 2019-04-02T04:47:20.850 回答