将表中的每个字段与同一表中的每个其他字段进行比较
Posted
技术标签:
【中文标题】将表中的每个字段与同一表中的每个其他字段进行比较【英文标题】:Compare every field in table to every other field in same table 【发布时间】:2017-06-29 17:44:03 【问题描述】:想象一个只有一列的表格。
+------+
| v |
+------+
|0.1234|
|0.8923|
|0.5221|
+------+
我想对 K 行执行以下操作:
-
取行 K=1 值:0.1234
计算表格其余部分中有多少值小于或等于第 1 行中的值。
遍历所有行 输出应该是:
+------+-------+
| v |output |
+------+-------+
|0.1234| 0 |
|0.8923| 2 |
|0.5221| 1 |
+------+-------+
快速更新我使用这种方法来计算上表中每个 v 值的统计量。对于我正在处理的数据大小,交叉连接方法太慢了。因此,我改为计算 v 值网格的统计数据,然后将它们与原始数据中的 vs 相匹配。 v_table 是之前的数据表,stat_comp 是统计表。
AS SELECT t1.*
,CASE WHEN v<=1.000000 THEN pr_1
WHEN v<=2.000000 AND v>1.000000 THEN pr_2
FROM v_table AS t1
LEFT OUTER JOIN stat_comp AS t2
【问题讨论】:
欢迎您!我对输出有点困惑。0.5221 > 0.1234
那么为什么output = 1
在那一行呢?
抱歉错字。现在应该是正确的。
看起来你想要的几乎是计算有多少值小于或等于当前行,而不是小于或等于第 1 行。是这样吗?
是的,对不起。我在工作,总是被打扰。问题应该反映新的变化。这是我要运行此比较的每一行。
数据样本不能很好地反映您正在处理的问题,尽管您给出了很多解释,但所需的结果仍然不完全清楚。如果您要求“小于或等于”,则数据样本中的值应至少重复一次。
【参考方案1】:
Windows 函数于 1999 年添加到 ANSI/ISO SQL 中,并在 2013 年 5 月 15 日发布的 0.11 版中添加到 Hive。 您正在寻找的是 rank with ties high 的变体,在 ANSI/ISO SQL:2011 中看起来像这样-
rank () over (order by v with ties high) - 1
Hive 目前不支持with ties ...
,但可以使用count(*) over (...)
实现逻辑
select v
,count(*) over (order by v) - 1 as rank_with_ties_high_implicit
from mytable
;
或
select v
,count(*) over
(
order by v
range between unbounded preceding and current row
) - 1 as rank_with_ties_high_explicit
from mytable
;
【讨论】:
据我所知,我的答案仍然正确,尽管您的答案可能更快。为什么投反对票? @McGlothlin - 你可以用锤子杀死站在窗户上的蚊子,尽管我不建议这样做,也不接受它作为合法的解决方案。 @McGlothlin - 正如你所看到的,我有点被其他事情分心了 :-) 谢谢【参考方案2】:生成样本数据
select 0.1234 as v into #t
union all
select 0.8923
union all
select 0.5221
这是查询
;with ct as (
select ROW_NUMBER() over (order by v) rn
, v
from #t ot
)
select distinct v, a.cnt
from ct ot
outer apply (select count(*) cnt from ct where ct.rn <> ot.rn and v <= ot.v) a
【讨论】:
帖子标记为hive
,但即使对于 SQL Server,这也是一个糟糕的解决方案。 (1) 不用outer apply
就可以用一个windows 函数来回答这个问题(2) row_number
是完全没有必要的。你需要做的就是从count(*)
中减去1【参考方案3】:
看到您的编辑后,看起来您确实可以使用笛卡尔积,即这里的CROSS JOIN
。我打电话给你的桌子foo
,并以bar
交叉加入它自己:
SELECT foo.v, COUNT(foo.v) - 1 AS output
FROM foo
CROSS JOIN foo bar
WHERE foo.v >= bar.v
GROUP BY foo.v;
Here's a fiddle.
此查询交叉连接列,从而返回列元素的每个排列(您可以通过删除SUM
和GROUP BY
子句并将bar.v
添加到SELECT
来自己看到这一点)。然后在foo.v >= bar.v
时加一个计数,产生最终结果。
【讨论】:
由于 windows 函数成为 SQL 标准的一部分大约 18(!) 年,所以没有理由使用cross join
实现 rank
。仅此一项就证明了否决票的合理性。此外 - (1) 您忽略了“小于 或等于”的 OP 请求 (2) 这是错误的过滤放置。将过滤放在聚合函数而不是 where 子句中,生成 n^2 行而不是 ((1+n)*n) 的行集/2
感谢您的反馈。我已经进行了相应的更新,但保留了CROSS JOIN
,因为我认为它提供了一个示例,说明如何在 OP 不知道的情况下使用它。【参考方案4】:
您可以将表的完整笛卡尔积与它本身相加并总结一个案例语句:
select a.x
, sum(case when b.x < a.x then 1 else 0 end) as count_less_than_x
from (select distinct x from T) a
, T b
group by a.x
这将为表中的每个唯一值提供一行,其中包含值小于此值的非唯一行的计数。
请注意,既没有连接也没有 where 子句。在这种情况下,我们实际上想要那个。对于a
的每一行,我们得到一个别名为b
的完整副本。然后我们可以检查每一个,看看它是否小于a.x
。如果是,我们将计数加 1。如果没有,我们就加 0。
【讨论】:
虽然“笛卡尔积”是基于这个问题想到的第一件事,但这实际上并不是这样做的。如果我没记错的话,本专栏的笛卡尔积将显示v
的每个元素的每个排列。无论如何,对总和/案例方法 +1。
如果我们在表中有重复的值,这将返回不正确的结果。
OP 没有明确指定不同的值。这是否“不正确”取决于OP想要什么。我可以很容易地想象这样一种情况,您希望计算重复行的每个实例,就像只计算一次一样。如果你想要不同,很容易修改。只需将别名为 b 的表替换为子查询(从 T 中选择不同的 x)。
这似乎有效。但是,这似乎在一张大桌子上会显着减慢。
@mymarkers 没错,我要统计重复值。以上是关于将表中的每个字段与同一表中的每个其他字段进行比较的主要内容,如果未能解决你的问题,请参考以下文章