使用ntile()
窗口函数时,主要问题是它任意分组为大致相等的部分,而不管实际值如何。
例如,使用以下查询:
select
id,title,price,
row_number() over(order by price) as row_number,
rank() over(order by price) as rank,
count(*) over(order by price) as count,
dense_rank() over(order by price) as dense_rank,
ntile(10) over(order by price) as decile
from paintings
order by price;
我会得到 10 组大小大致相同的画作,价格相同的画很有可能最终会被放入不同的垃圾箱。
例如:
┌────┬────────────────────────────────────────────┬───────┬────────────┬──────┬───────┬────────────┬────────┐
│ id │ title │ price │ row_number │ rank │ count │ dense_rank │ decile │
╞════╪════════════════════════════════════════════╪═══════╪════════════╪══════╪═══════╪════════════╪════════╡
│ 11 │ Eyes in the Heat │ 10 │ 1 │ 1 │ 1 │ 1 │ 1 │
│ 19 │ Deux fillettes, fond jaune et rouge │ 11 │ 2 │ 2 │ 2 │ 2 │ 1 │
│ 17 │ Flowers in a Pitcher │ 12 │ 3 │ 3 │ 6 │ 3 │ 1 │
│ 5 │ Composition with Red, Yellow and Blue │ 12 │ 4 │ 3 │ 6 │ 3 │ 2 │
│ 18 │ La lecon de musique (The Music Lesson) │ 12 │ 5 │ 3 │ 6 │ 3 │ 2 │
│ 9 │ The Adoration of the Magi │ 12 │ 6 │ 3 │ 6 │ 3 │ 2 │
│ 29 │ Self-Portrait │ 14 │ 7 │ 7 │ 10 │ 4 │ 3 │
│ 25 │ Symphony in White, No. 1: The White Girl │ 14 │ 8 │ 7 │ 10 │ 4 │ 3 │
│ 30 │ The Anatomy Lecture of Dr. Nicolaes Tulp │ 14 │ 9 │ 7 │ 10 │ 4 │ 3 │
│ 20 │ Les repasseuses (Women Ironing) │ 14 │ 10 │ 7 │ 10 │ 4 │ 4 │
│ 1 │ The Birth of Venus │ 15 │ 11 │ 11 │ 14 │ 5 │ 4 │
│ 12 │ Femme se promenant dans une foret exotique │ 15 │ 12 │ 11 │ 14 │ 5 │ 4 │
│ 24 │ Portrait of the Painter’s Mother │ 15 │ 13 │ 11 │ 14 │ 5 │ 5 │
│ 28 │ Jeunes filles au piano │ 15 │ 14 │ 11 │ 14 │ 5 │ 5 │
│ 7 │ Portrait de l artiste (Self-portrait) │ 16 │ 15 │ 15 │ 17 │ 6 │ 5 │
│ 3 │ The Last Supper │ 16 │ 16 │ 15 │ 17 │ 6 │ 6 │
│ 13 │ Combat of a Tiger and a Buffalo │ 16 │ 17 │ 15 │ 17 │ 6 │ 6 │
│ 4 │ The Creation of Man │ 17 │ 18 │ 18 │ 19 │ 7 │ 6 │
│ 22 │ Le Chemin de Fer │ 17 │ 19 │ 18 │ 19 │ 7 │ 7 │
│ 6 │ Femmes de Tahiti [Sur la plage] │ 18 │ 20 │ 20 │ 24 │ 8 │ 7 │
│ 21 │ Le Bar aux Folies-Berg │ 18 │ 21 │ 20 │ 24 │ 8 │ 7 │
│ 26 │ Lady at the Piano │ 18 │ 22 │ 20 │ 24 │ 8 │ 8 │
│ 15 │ Remembrance of a Garden │ 18 │ 23 │ 20 │ 24 │ 8 │ 8 │
│ 16 │ 1914 │ 18 │ 24 │ 20 │ 24 │ 8 │ 8 │
│ 14 │ Ancient Sound, Abstract on Black │ 19 │ 25 │ 25 │ 28 │ 9 │ 9 │
│ 8 │ The Large Turf │ 19 │ 26 │ 25 │ 28 │ 9 │ 9 │
│ 23 │ On the Beach │ 19 │ 27 │ 25 │ 28 │ 9 │ 9 │
│ 2 │ Portrait of Mona Lisa │ 19 │ 28 │ 25 │ 28 │ 9 │ 10 │
│ 27 │ On the Terrace │ 20 │ 29 │ 29 │ 30 │ 10 │ 10 │
│ 10 │ The She-Wolf │ 20 │ 30 │ 29 │ 30 │ 10 │ 10 │
└────┴────────────────────────────────────────────┴───────┴────────────┴──────┴───────┴────────────┴────────┘
请注意,有四个项目 price 12
,但其中两个在有十分位数 1 中,其中两个在十分位数 2 中。我想把这些项目放在一起,我不关心哪个十分位数。
我已经包含了其他窗口函数来进行比较。
似乎ntile()
使用row_number()
唯一的并以此为基础。如果它使用rank()
orcount(*)
函数会更公平,因为价格相同的物品最终会放在同一个 bin 中。
这是 PostgreSQL 和 SQL Server 的行为,并且可能是其余的行为。
问题是,有没有办法做到这一点?
您可以使用
rank()
并使用每个 bin 的行数进行整数除法。结果:
请注意,带有样本数据的 bin 2 和 8 最终为空。
您可以模拟 WIDTH_BUCKET 函数:
或者没有函数:
注意我已经将价格类型从 int 更改为 decimal。
db<>在这里摆弄
我已经接受了一个答案,但我想说明我是如何将答案付诸实践的。
兴趣点:
WHERE
子句只是强制一个尴尬的 bin 大小。10.0
是生成小数而不是截断的整数。row_number()/bin+1
计算以复制本机ntile
功能。当然,当行数相对较少时,存在丢失十分位数的风险,但至少它将相同的值保持在一起。
现在的问题是决定哪种选择更符合自己的口味。
小提琴:http ://sqlfiddle.com/#!17/8bb42/1