Oracle:如何在获取结果之前按某些列对记录进行分组

Posted

技术标签:

【中文标题】Oracle:如何在获取结果之前按某些列对记录进行分组【英文标题】:Oracle: How to group records by certain columns before fetching results 【发布时间】:2016-08-18 23:01:42 【问题描述】:

我在 Redshift 中有一个如下所示的表:

col1 | col2 | col3 | col4 | col5 | col6
=======================================
123  | AB   | SSSS | TTTT | PQR  | XYZ
---------------------------------------
123  | AB   | SSTT | TSTS | PQR  | XYZ
---------------------------------------
123  | AB   | PQRS | WXYZ | PQR  | XYZ
---------------------------------------
123  | CD   | SSTT | TSTS | PQR  | XYZ
---------------------------------------
123  | CD   | PQRS | WXYZ | PQR  | XYZ
---------------------------------------
456  | AB   | GGGG | RRRR | OPQ  | RST
---------------------------------------
456  | AB   | SSTT | TSTS | PQR  | XYZ
---------------------------------------
456  | AB   | PQRS | WXYZ | PQR  | XYZ

我有另一个表也有类似的结构和数据。

从这些表中,我需要在 (edited) 任何一个表中选择 col3 中没有“SSSS”和 col4 中没有“TTTT”的值 .我还需要按 col1 和 col2 中的值对结果进行分组。

在这里,我希望我的查询返回:

123,CD
456,AB

我不希望 123, AB 出现在我的结果中,因为对应于 123, AB 的行之一分别在 col3 和 col4 中具有 SSSS 和 TTTT。即,我想在我正在查找的两个表中的任何一个中省略 col3 和 col4 中具有 SSSSTTTT 的项目。

我对编写查询以从数据库中提取信息非常陌生,所以请原谅我的无知。我被告知要探索 GROUP BYORDER BY,但我不确定我是否足够了解它们的用法。

我的查询看起来像:

SELECT * from table1 join table2 on
table1.col1 = table2.col1 AND
table1.col2 = table2.col2
WHERE
col3 NOT LIKE 'SSSS' AND
col4 NOT LIKE 'TTTT'
GROUP BY col1,col2

但是,此查询会引发错误:col5 must appear in the GROUP BY clause or be used in an aggregate function;

我不确定如何继续。我会很感激任何帮助。谢谢!

【问题讨论】:

你写的不是真的/不完整。如果 col5 不在查询的 SELECT 列表中,您将不会收到有关 col5 的错误消息。当您发布并展示您尝试过的内容时,请准确展示您尝试过的内容;否则我们都只是浪费时间。 你是对的 - 我的查询中有一个 SELECT *。修改问题。 这没有意义:您说您只想获得前两列(这就是您在示例中显示的内容)。请确保您准确地解释了您的需求;如果示例不正确,请也修复它。 这样我们就清楚了。如果在该组中找到至少一个 SSSSTTTT 的组合,是否要删除整个组(在这种情况下为 123 | AB)? @Nicholas Krasnov 是的,没错。如果123, AB 的行之一具有SSSSTTTT,我不希望考虑任何123, AB【参考方案1】:

您似乎还想要 DISTINCT 结果。在这种情况下,使用 MINUS 的解决方案可能与任何其他解决方案一样有效(请记住,MINUS 自动也意味着 DISTINCT):

select col1, col2 from table_name             -- enter your column and table names here
minus
select col1, col2 from table_name where col3 = 'SSSS' and col4 = 'TTTT'
;

无需分组!

话虽如此,这里有一个使用 GROUP BY 的解决方案。请注意,HAVING 条件使用了一个重要的聚合函数 - 它是一个 COUNT() 但计算的是一个 CASE 来处理所需的内容。请注意,不必/不需要将 HAVING 子句/条件中的聚合函数包含在 SELECT 列表中!

select   col1, col2
from     table_name
group by col1, col2
having count(case when col3 = 'SSSS' and col4 = 'TTTT' then 1 else null end) = 0
;

【讨论】:

注意:优化器成本(如 EXPLAIN PLAN 中所示)不应仅用于判断查询效率,尤其是当计划针对两种截然不同的方法时。话虽如此,在提供的样本数据上,MINUS 解决方案的优化器成本为 6(包括生成测试数据的 WITH 子句);使用 HAVING 的 GROUP BY 解决方案的优化器成本是 17,即使没有连接也是如此。 MINUS 解决方案似乎更有效。 是的,出于某种原因,join 是第一个想到消除这些行的事情。您的解决方案更整洁。另外,我们只查询一次表,这应该会提高性能。 使用minus 似乎是一个好的开始。但是,我的表非常大,我要查询的表实际上是两个不同表的连接:select col1, col2 from table1 join table2... 所以它现在运行真的很慢。关于如何加快速度的任何建议?我在问题中包含更多细节。 您可以在实际数据上尝试使用 MINUS 和 GROUP BY(带有 HAVING 条件),看看哪个更快。建议...您的列上有索引吗?如果您的表很大,并且您进行了联接,并且您需要这种额外的处理......可能没有很多好的解决方案! 我现在要试试GROUP BYminus 已经运行了几个小时还没有结果。我不确定索引。我是一个总数据库n00b,所以我首先要查找如何查找是否有索引。我刚刚编辑了我的问题以包含更多详细信息。【参考方案2】:

您应该使用EXCEPT 运算符。 EXCEPT and MINUS 是同一运算符的两个不同版本。

这是您的查询的语法

SELECT col1, col2 FROM table1           
EXCEPT
SELECT col1, col2 FROM table1 WHERE col3 = 'SSSS' AND col4 = 'TTTT';

一个重要的考虑因素是要知道您想要的答案是否需要 and 或 OR 运算符。您想查看 col3 = 'SSSS' 和 col4 的值不同于 col4 = 'TTTT' 的记录吗?

如果答案是否定的,您应该使用以下版本:

SELECT col1, col2 FROM table1           
EXCEPT
SELECT col1, col2 FROM table1 WHERE col3 = 'SSSS' OR col4 = 'TTTT';

您可以了解更多关于MINUS or EXCEPT operator on the Amazon Redshift documentation here.

【讨论】:

以上是关于Oracle:如何在获取结果之前按某些列对记录进行分组的主要内容,如果未能解决你的问题,请参考以下文章

如何按 Cassandra 中的二级索引或列对结果进行排序?

SQL:按选定列对记录进行分组

如何在android中按数值列对listview项目进行排序

Druid -> 按本机查询的时间戳以外的另一列对数据进行排序

如何按列对多维数组进行排序?

如何按列对数据框进行分组?