在使用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 │
└────┴────────────────────────────────────────────┴───────┴────────────┴──────┴───────┴────────────┴────────┘请注意,有四个项目的价格12,但其中两个在有十分之一,其中有两个十分之二。我想保持这些项目在一起,我不担心哪个十分之一。
我还包括了其他窗口函数来进行比较。
ntile()似乎只使用row_number(),并在此基础上进行裁剪。如果它使用rank()或count(*)函数,就更公平了,因为相同价格的商品最终会被放在同一个垃圾桶里。
这是PostgreSQL和Server的行为,也可能是其余的行为。
问题是,有没有办法做到这一点?
发布于 2019-08-22 07:04:52
您可以使用rank()对每个bin进行整数除法和行数除法。
declare @T table(id int, title varchar(100), price int);
insert into @T(id, title, price) values
(19, 'Deux fillettes, fond jaune et rouge ', 11),
(17, 'Flowers in a Pitcher ', 12),
(5 , 'Composition with Red, Yellow and Blue ', 12),
(18, 'La lecon de musique (The Music Lesson) ', 12),
(9 , 'The Adoration of the Magi ', 12),
(29, 'Self-Portrait ', 14),
(25, 'Symphony in White, No. 1: The White Girl ', 14),
(30, 'The Anatomy Lecture of Dr. Nicolaes Tulp ', 14),
(20, 'Les repasseuses (Women Ironing) ', 14),
(1 , 'The Birth of Venus ', 15),
(12, 'Femme se promenant dans une foret exotique ', 15),
(24, 'Portrait of the Painter’s Mother ', 15),
(28, 'Jeunes filles au piano ', 15),
(7 , 'Portrait de l artiste (Self-portrait) ', 16),
(3 , 'The Last Supper ', 16),
(13, 'Combat of a Tiger and a Buffalo ', 16),
(4 , 'The Creation of Man ', 17),
(22, 'Le Chemin de Fer ', 17),
(6 , 'Femmes de Tahiti [Sur la plage] ', 18),
(21, 'Le Bar aux Folies-Berg ', 18),
(26, 'Lady at the Piano ', 18),
(15, 'Remembrance of a Garden ', 18),
(16, '1914 ', 18),
(14, 'Ancient Sound, Abstract on Black ', 19),
(8 , 'The Large Turf ', 19),
(23, 'On the Beach ', 19),
(2 , 'Portrait of Mona Lisa ', 19),
(27, 'On the Terrace ', 20),
(10, 'The She-Wolf ', 20);
declare @BinCount int = 10;
declare @BinSize int;
select @BinSize = 1 + count(*) / @BinCount from @T;
select T.id,
T.title,
T.price,
1 + rank() over(order by T.price) / @BinSize as decile
from @T as T;结果:
id title price decile
--- ------------------------------------------- ------ --------------------
19 Deux fillettes, fond jaune et rouge 11 1
17 Flowers in a Pitcher 12 1
5 Composition with Red, Yellow and Blue 12 1
18 La lecon de musique (The Music Lesson) 12 1
9 The Adoration of the Magi 12 1
29 Self-Portrait 14 3
25 Symphony in White, No. 1: The White Girl 14 3
30 The Anatomy Lecture of Dr. Nicolaes Tulp 14 3
20 Les repasseuses (Women Ironing) 14 3
1 The Birth of Venus 15 4
12 Femme se promenant dans une foret exotique 15 4
24 Portrait of the Painter’s Mother 15 4
28 Jeunes filles au piano 15 4
7 Portrait de l artiste (Self-portrait) 16 5
3 The Last Supper 16 5
13 Combat of a Tiger and a Buffalo 16 5
4 The Creation of Man 17 6
22 Le Chemin de Fer 17 6
6 Femmes de Tahiti [Sur la plage] 18 7
21 Le Bar aux Folies-Berg 18 7
26 Lady at the Piano 18 7
15 Remembrance of a Garden 18 7
16 1914 18 7
14 Ancient Sound, Abstract on Black 19 9
8 The Large Turf 19 9
23 On the Beach 19 9
2 Portrait of Mona Lisa 19 9
27 On the Terrace 20 10
10 The She-Wolf 20 10我也不担心哪个十进制
请注意,使用示例数据的bin 2和8最终为空。
发布于 2019-08-23 06:55:59
我已经接受了一个答案,但我想说明一下我是如何将答案付诸实践的。
WITH data AS (SELECT count(*)/10.0 AS bin FROM prints)
SELECT
id,title,price,
row_number() OVER(ORDER BY price) AS row_number,
ntile(10) OVER(ORDER BY price) AS decile,
floor((row_number() OVER(ORDER BY price)-1)/bin)+1 AS row_decile,
floor((rank() OVER(ORDER BY price)-1)/bin)+1 AS rank_decile,
floor((count(*) OVER(ORDER BY price)-1)/bin)+1 AS count_decile,
bin
FROM prints,data
ORDER BY price;兴趣点:
WHERE子句只是为了强制一个尴尬的垃圾箱大小。10.0将生成一个十进制整数,而不是一个截断整数。row_number()/bin+1计算以复制本机ntile函数。当然,当行数相对较少时,会有丢失十进制的风险,但至少它会将相同的值放在一起。
现在的问题是决定哪种选择更符合自己的口味。
小提琴:http://sqlfiddle.com/#!17/8bb42/15
https://dba.stackexchange.com/questions/245962
复制相似问题