使用 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
上还是在两者的组合上UPDATED
和 PARTY_ID
。如果有机会,我建议您针对两组索引尝试更新后的查询,并查看它们的比较情况。
实现所需的更复杂功能的一种方法可能是对条件表达式求和 - 例如,如果您希望 PARTY_ID
包含在 TABLE1
和 TABLE2
和 TABLE3
中或 TABLE4
和 TABLE5
:
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,intersection或difference运算符组合查询(来自http://www.postgresql.org/docs/9.4/static/queries-union