如何在结果集中获得 WHERE 条件评估的结果?

Posted

技术标签:

【中文标题】如何在结果集中获得 WHERE 条件评估的结果?【英文标题】:How to get the results of WHERE conditional evaluations in the result set? 【发布时间】:2009-06-26 21:23:47 【问题描述】:

问题

我有一个这样的查询:

select a.id from a join b on ( a.id = b.my_a ) join ....
where 
    ( /* complex and expensive conditional */ )
    AND 
       (( /* conditional #1 */ ) 
        OR ( /* conditional #2 */ )
        OR ( /* conditional #3 */))

我希望查询返回如下内容:

select a.id, conditional_1_eval_value, conditional_2_eval_value, conditional_3_eval_value from a join b on ( a.id = b.my_a ) join ....
where 
    ( /* complex and expensive conditional */ )
    AND 
       (( /* conditional #1 */ ) 
        OR ( /* conditional #2 */ ) 
        OR ( /* conditional #3 */))

其中conditional_1_eval_valueconditional_2_eval_valueconditional_3_eval_value 设置为 TRUE、FALSE、NULL。 NULL 表示未评估条件。

所以结果集可能是:

1, FALSE, NULL, TRUE ( condition_1, condition_3 were evaluate, condition_2 was not)
2, NULL, TRUE, TRUE ( condition_2, condition_3 were evaluate, condition_1 was not)
3, TRUE, FALSE, FALSE (all were evaluated)

condition_1condition_2condition_3 本身就是复杂的,涉及相关的子查询和分组。

编辑:

我想要完成什么?

我们需要记录哪个条件导致该行被返回。我们不需要知道返回该行的所有原因。所以在结果示例的第二行中,知道conditional_2conditional_3 都是正确的就足够了。不知道conditional_1 的值是什么并不重要。

知道至少满足一个条件以及那个条件是什么就足够了。

非最优解

显然我可以用这样的 UNION 做到这一点:

select a.id, TRUE, NULL, NULL from a join b on ( a.id = b.my_a ) join ....
where 
    ( /* complex and expensive conditional */ )
    AND 
       ( /* conditional #1 */ )
UNION
select a.id, NULL, TRUE, NULL from a join b on ( a.id = b.my_a ) join ....
where 
    ( /* complex and expensive conditional */ )
    AND 
        ( /* conditional #2 */ )
UNION
select a.id, NULL, NULL, TRUE from a join b on ( a.id = b.my_a ) join ....
where 
    ( /* complex and expensive conditional */ )
    AND 
        ( /* conditional #3 */)

但这意味着:

    常见的“复杂且昂贵的条件”被评估 3 次。 即使在另一个条件已经满足 OR 的情况下,也会评估所有条件。 会有一个维护噩梦,以确保常见的复杂查询的 3 个副本是相同的(可以通过在代码中构造 sql 并复制公共字符串来解决 - 但这意味着我会违反所有 sql 不存在的另一个内部标准嵌入在 java 中,但在 DBA 可见的 xml 文件中)

在选择中使用复制每个条件 1 到 3 的 CASE 可避免对常见条件进行 3 次评估。但是,条件 1-3 的复杂性是不可能的。

在 FROM 子句中使用 select 会很尴尬,并且可能无法实现,因为 FROM SELECT 不能是相关查询。我不确定我能否构造一个有用的非相关查询。

存储过程可以工作。但是,这将首先是这样的存储过程,并且会显着增加我们的部署复杂性。

在 java 代码中进行conditional_1conditional_2conditional_3 评估。这是我们目前正在做的事情,它运行缓慢。当数据库被设计为过滤结果集时传输的大量数据——不应该在 java 中这样做!

解决方案建议?

有人吗?

我还应该补充一点,我欢迎那些说这个问题无法解决的答案。知道问题无法解决将节省我尝试使用严格的 SQL 解决问题的时间。

如果必须选择,我会倾向于学习 mysql 存储过程的外观。

因此,如果您想自愿提供 mysql 存储过程的外观,那就太好了。

【问题讨论】:

基于此处的(半预期的、非超人救援的)答案 - 我将尝试进一步减少查询并看看有什么变化。当我知道更多时会添加更多......谢谢。 【参考方案1】:

为了实现您的目标,为什么不使用存储函数来处理复杂的条件?

这将导致如下选择语句:


select func1(arg1, arg2, ...), func2(arg1, arg2, ...), rest_of_select_columns 
from table1, table2
where (complex1 logic) 
OR func1(arg1, arg2, ....) = 1 /* return to give true */ 
OR func2(arg1, arg2, ....) = 1 

注意事项:

    SQL 不支持 Boolean 数据类型,因此函数的结果为 true、false 是不可能的。因此,0,1 返回值。 根据您的 mySQL 版本,您可以将函数设为 DETERMINISTIC,这可能会带来一些性能改进。

【讨论】:

那么这些函数在概念上的作用与 CASE 相同,但性能更好?它将通过将条件封装在函数中来避免复制条件的问题。 [存储过程被列在问题的非最佳解决方案下] 在某种程度上它会像这种情况一样。使用 DETERMINISTIC (如果 mySQL 做得“正确”)并且条件是“确定性的”,将导致查询运行得更快。在 Oracle 中(我假设 mySQL 是相同的),当查询引擎“看到”以前“完成”的参数列表时,它会替换之前的结果(当相同的参数列表发生在多行时会节省很多) . RE:不想使用存储过程。我很抱歉,但我能看到的唯一方法是使用使用数字返回类型的函数来“滑过薄冰”的 SQL 没有真正的布尔列类型(1=1,因为不支持选择列) . 嗯 TRUE FALSE NULL 只是示例值。 “foo”、“bar”和“baz”也可以。重要的是信号,而不是信号的形式。【参考方案2】:

你是对的,将所有数据拉回 java 并执行你的条件将是一条狗。

但是,您唯一真正的选择是 UNION 3 个不同的查询。由于关系引擎的工作方式,无法提取记录中“命中”的内容。

【讨论】:

【参考方案3】:

弗兰克,我不知道如何在 MySQL 中(或者实际上在任何 SQL 中)表达这个问题。然而,几年前我遇到了同样复杂的大型数据集评估问题1

根据收集到的经验,我可以就如何加快评估提出一些想法:

我会考虑切换到另一个数据库引擎(替换当前的引擎或只是将数据复制到其中)- 我会使用 Oracle,因为我知道它在查询优化方面的能力。其他选择是使用嵌入式 sql 引擎使数据更接近计算位置。 我会再看一下当前基于 Java 的评估。也许调整查询批量大小,对问题中涉及的各种表进行并行纠缠查询,并使用基于流的方法。 如果我的 Java 代码有相当多的可用内存,如果需要频繁执行计算,我会考虑始终将部分/所有数据缓存在内存中。 或者会寻找一种方法来取出条件的公共部分,通过在条件之间共享来获得一些速度。

1 实际上,约束是在约 1M 的记录集上进行快速的按需计算。

【讨论】:

没有。条件不可变。但是每一行的处理会根据三个条件中的哪一个而改变。将编辑问题。 我们是一家初创公司。解决方案必须在几个小时内实现。没几天。现在看起来存储函数正在引领潮流。自然,我会尝试减少/合并查询,但我必须假设我无法在分配的时间内进行操作。回复:基于 Java 的评估;这就是我们想要取代的。【参考方案4】:

您能否只选择与结果相关的所有数据,然后在您的客户端代码中执行该条件逻辑以处理结果集?

【讨论】:

没有。 Conditional_1、Conditional_2 和 conditional_3 是重要的。在代码中这样做很昂贵,并且会导致大量数据被传输。【参考方案5】:

如果您希望减少复杂表达式的重新键入,请将表达式放在子查询派生表中:

SELECT t.*
FROM (
    SELECT a.*, b.*, ...
        /* conditional #1 */ AS c1,
        /* conditional #2 */ AS c2,
        /* conditional #3 */ AS c3
    FROM a JOIN b ON (a.id = b.my_a)
    ...) AS t
WHERE /* ...other conditions... */
    AND ((c1) OR (c2) OR (c3));

这也是另一个问题的解决方案,“如何在 WHERE 条件下使用列别名?”

【讨论】:

你有 FROM (SELECT /* conditional #1 / AS c1, ) ... 那么我应该用什么替换 / conditional #1 */ 呢? 也如问题 FROM SELECTs 中所引用的,必须是不相关的查询。如果我能想出一个 FROM SELECT 查询,那将是我的首选解决方案。

以上是关于如何在结果集中获得 WHERE 条件评估的结果?的主要内容,如果未能解决你的问题,请参考以下文章

数据库mysql查询

having和where的区别

SQL - 在 WHERE 子句中使用日期范围的结果集中未显示正确日期

条件查询

mysql基本查询

页面项目的布尔评估不正确