使用 sql union 子查询组合来处理客户过滤的 AND/OR 条件组合

Posted

技术标签:

【中文标题】使用 sql union 子查询组合来处理客户过滤的 AND/OR 条件组合【英文标题】:Use combination of sql union sub-queries to handle combinations of AND/OR conditions for customer filtering 【发布时间】:2015-09-18 13:53:24 【问题描述】:

这是一项与市场细分分析相关的工作,我认为它每天运行几次。

我将解释的查询类型需要在 10 分钟内完成,最多 5 个表,每个表中有 1000 万条记录。

我是一个 sql 菜鸟。我将其作为春季批处理作业来实现,并且需要确定要使用的最有效的 sql 查询技术。因此,我可以为任何 AND/OR 条件组合编写动态查询生成代码。

目标是根据存在于多个表中的存在来选择partyId、groupId,这些表更新太频繁以至于索引非常有用。相同的表本身本质上是由某些现有进程创建的箱。使用日期范围条件,以便选择仅考虑自上次作业运行以来的更改。 (假设日期范围条件有助于查询优化)

所以对于我的测试用例,我有 5 个表都具有以下结构

CREATE TABLE `TABLE1` (
  `UPDATED` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP,
  `PARTY_ID` varchar(20) NOT NULL,
  `GROUP_ID` varchar(20) NOT NULL,
  `SEQUENCE_ID` int(11) NOT NULL AUTO_INCREMENT,
  PRIMARY KEY (`SEQUENCE_ID`)
) ENGINE=InnoDB AUTO_INCREMENT=2225551 DEFAULT CHARSET=latin1;

来自Partition a very large INNER JOIN SQL query上的答案和cmets

我拼凑了 2 个可能的查询,一个用于“所有 AND”类型条件,一个用于“所有 OR”类型条件。

select PARTY_ID from
(select distinct PARTY_ID from TABLE1 WHERE (UPDATED >= '2015-09-18 14:43:44' AND UPDATED <= '2015-09-18 15:00:00')  union all
 select distinct PARTY_ID from TABLE2 WHERE (UPDATED >= '2015-09-18 14:43:44' AND UPDATED <= '2015-09-18 15:00:00')  union all
 select distinct PARTY_ID from TABLE3 WHERE (UPDATED >= '2015-09-18 14:43:44' AND UPDATED <= '2015-09-18 15:00:00')  union all
 select distinct PARTY_ID from TABLE4 WHERE (UPDATED >= '2015-09-18 14:43:44' AND UPDATED <= '2015-09-18 15:00:00')  union all
 select distinct PARTY_ID from TABLE5 WHERE (UPDATED >= '2015-09-18 14:43:44' AND UPDATED <= '2015-09-18 15:00:00')) as ilv
group by PARTY_ID 
having count(*) = 5;

这适用于返回所有表 1-5 中存在的那些 partyId 的结果集。用户将以 AND/OR 条件的形式提供条件要求,因此这等同于纯粹的 AND 条件集)

select PARTY_ID from
(select distinct PARTY_ID from TABLE1 WHERE (UPDATED >= '2015-09-18 14:43:44' AND UPDATED <= '2015-09-18 15:00:00')  union all
 select distinct PARTY_ID from TABLE2 WHERE (UPDATED >= '2015-09-18 14:43:44' AND UPDATED <= '2015-09-18 15:00:00')  union all
 select distinct PARTY_ID from TABLE3 WHERE (UPDATED >= '2015-09-18 14:43:44' AND UPDATED <= '2015-09-18 15:00:00')  union all
 select distinct PARTY_ID from TABLE4 WHERE (UPDATED >= '2015-09-18 14:43:44' AND UPDATED <= '2015-09-18 15:00:00')  union all
 select distinct PARTY_ID from TABLE5 WHERE (UPDATED >= '2015-09-18 14:43:44' AND UPDATED <= '2015-09-18 15:00:00')) as ilv
group by PARTY_ID;

这适用于返回存在于表 1-5 中的任何 partyId 的结果集。用户将以 AND/OR 条件的形式提供条件要求,因此这等同于纯 OR 条件集)

我需要一个示例,说明 AND/OR 条件的组合如何转换为这种类型的查询语法(因为它比标准连接快得多)

例如,正确使用子查询以在 TABLE1 AND TABLE2 AND TABLE3 OR TABLE4 AND TABLE5 中返回party_id 列表,然后我可以看到如何为任何组合编写动态查询生成代码。

我的另一个问题是日期范围实际上是否有助于提高效率? 我还可以有效地使用日期范围对查询进行分区,以便可以并行运行吗?

我不确定的原因是我猜测 sql 引擎必须遍历每个表的所有行,而不管条件是否存在。所以对查询进行分区可能会导致更多的总循环......这样的推理是否有意义?

【问题讨论】:

既然你在做UNION,你可以去掉DISTINCT关键字。 (UNION 确实已经删除了所有重复项。) 如果 Updated 没有索引,那么必须对每个联合查询执行完整的表扫描。即使已对更新进行了索引,您的查询也会被写入,以便仍然必须执行索引扫描。有一种方法可以编写查询以生成索引搜索,但我必须首先知道是否已更新或者可以索引。另外,表之间到底有什么区别?它们在物理上是相同的,但来自一个的数据显然意味着不同于另一个的数据...... @TommCatt:我想更新可以被索引,但它必须在主查询之前完成......不确定这是否有帮助。由于表的频繁更新,每次都需要重建索引。关于数据,结果实际上只是partyId(查询在这些(最多60个表)中询问哪些partyId存在于......无论指定表名的条件集),所以你是正确的groupId不需要在那里......查询结果中的 groupId 将没有任何意义,因为 groupId 来自任何带有“group by”子句的 partyId 组。 你能提供一些数据吗,从几个表中筛选出几行?当您说“频繁更新”时,这些是实际的 Update 语句还是 Insert 语句?频繁的频率是多少?看起来您可能对每种类型的行都有一个单独的表,而不是一个带有类型指定字段的表。我希望你所有的问题都会通过一些仔细的数据建模而消失。 【参考方案1】:

如果您只打算查询当天的行,那么有一个通宵的工作来为当天的每个表设置一个新分区(并将前一天的行移动到主分区)是有意义的每个表)。这样,您应该只查询数千条记录,而不是数百万条记录。

如果日期/时间范围可能从任何天开始,那么在每个表上设置一个新索引会更有意义,无论是单独在 UPDATED 上还是在两者的组合上UPDATEDPARTY_ID。如果有机会,我建议您针对两组索引尝试更新后的查询,并查看它们的比较情况。

实现所需的更复杂功能的一种方法可能是对条件表达式求和 - 例如,如果您希望 PARTY_ID 包含在 TABLE1TABLE2TABLE3 TABLE4TABLE5:

select PARTY_ID from
(select distinct PARTY_ID, 'TABLE1' TABLENAME from TABLE1 
 WHERE UPDATED >= '2015-09-18 14:43:44' AND UPDATED <= '2015-09-18 15:00:00'  union all
 select distinct PARTY_ID, 'TABLE2' TABLENAME from TABLE2 
 WHERE UPDATED >= '2015-09-18 14:43:44' AND UPDATED <= '2015-09-18 15:00:00'  union all
 select distinct PARTY_ID, 'TABLE3' TABLENAME from TABLE3 
 WHERE UPDATED >= '2015-09-18 14:43:44' AND UPDATED <= '2015-09-18 15:00:00'  union all
 select distinct PARTY_ID, 'TABLE4' TABLENAME from TABLE4 
 WHERE UPDATED >= '2015-09-18 14:43:44' AND UPDATED <= '2015-09-18 15:00:00'  union all
 select distinct PARTY_ID, 'TABLE5' TABLENAME from TABLE5 
 WHERE UPDATED >= '2015-09-18 14:43:44' AND UPDATED <= '2015-09-18 15:00:00') as ilv
GROUP BY PARTY_ID
HAVING SUM(CASE WHEN TABLENAME IN ('TABLE1','TABLE2','TABLE3') THEN 1 END)=3
    OR SUM(CASE WHEN TABLENAME IN ('TABLE4','TABLE5') THEN 1 END)=2;

请注意,等式表达式(在HAVING 子句中)中的数字需要与CASE 表达式中检查的表总数相匹配 - 因此查询需要检查 SUMmed CASE 表达式是否等于 3当检查PARTY_ID在所有前三个表中时,需要在检查后两个表时检查第二个表达式是否等于2

【讨论】:

以上是关于使用 sql union 子查询组合来处理客户过滤的 AND/OR 条件组合的主要内容,如果未能解决你的问题,请参考以下文章

SQL中Union与Union All的区别

SQL学习之组合查询(UNION)

组合查询

SQL-子查询;union;limit

sql 使用union,intersection或difference运算符组合查询(来自http://www.postgresql.org/docs/9.4/static/queries-union

MySQL必知必会:组合查询(Union)