SQL優化之使用數學的方式動態的確定區間並統計02

2021-10-17 11:03:42 字數 4077 閱讀 8989

今天在群裡看到了乙個小夥伴提的乙個sql需求:

把一列分為10個區間,按最大值和最小值去分區間,然後統計區間資料量。

emmm,感覺和之前的那篇文章很像,但又有些許不同,而且他這個場景應用更頻繁,所以總結一波。

一般的分段區間統計;

指定步長的分段區間統計;

動態計算步長的分段區間統計

乙個區間的包含左邊界和右邊界,比如[0,10),[10,20),…,[90,100).

如上,是一組左閉右開的區間,步長gap為10。

一般寫法如下,可能我們會得到如下sql:

select

count

(case

when score >=

0and score <

10then

1else

0end

)as range_0_10,

count

(case

when score >=

10and score <

20then

1else

0end

)as range_10_20,

count

(case

when score >=

20and score <

30then

1else

0end

)as range_20_30,

count

(case

when score >=

30and score <

40then

1else

0end

)as range_30_40,..

.from input

以上sql從結果的正確性來說沒有任何問題,但是從維護的角度來說,問題就大了。

同樣這種sql在每一種分組區間都得去判斷一次,難以維護,新增區間或者調整區間步長又需要手動修改,時間成本增加。

我們分析一下上面那個sql,無論是case when score >=0 and score <10 then 1 else 0 end還是case when score >=10 and score <20 then 1 else 0 end,提取公有的屬性其實都是:

case

when score >= ? and score < ? then

1else

0end

我們這麼做其實就是為了給資料去分組,那我們換種思路想一下,分組。。首先想到的是不是group by?

所以我們就想一想,如何將原始資料transform為可以使用group by來解決的資料:

現有資料如下:

hive (

default

)>

select

*from math_test;

okmath_test.score

3050

101300

456768

999130

3501130

1350

1131

1150

time taken: 1.193 seconds, fetched: 13

row(s)

如果我們按照步長為100去分組,那麼 30,50 會分在[0,100)的區間,100,130會分在[100,200)的區間

這裡我們可以使用floor函式來實現,步長gap為100所以執行如下sql結果:

select floor(score/

100)

as tag,score from math_test;

tag score030

0501101

3300

4456

7768

9999

1130

3350

111130

131350

111131

111150

time taken: 0.119 seconds, fetched: 13

row(s)

結果大家已經看到了,通過floor函式和步長gap可以實現將對應區間的資料分在乙個組裡,打上了相同的tag。因為使用了floor函式向下取整,所以我們的區間是左閉有開,如果你使用的ceil函式向上取整,則是左開右閉。

sql具體實現如下:

select 

concat(

'[',cast(tag as string)

,'--'

,cast(tag +

100as string)

,')'

)as score_range,

cnt

from

(select

floor(score/

100)

*100

as tag,

count(*

)as cnt

from math_test

group

by floor(score/

100)

) tscore_range cnt[0

--100) 2

[100

--200) 2

[300

--400) 2

[400

--500) 1

[700

--800) 1

[900

--1000) 1

[1100

--1200) 3

[1300

--1400) 1

以上的**已經很好的將多行的case when轉變為了一行,但是和前言我們小夥伴提出的需求還是有差別,**把一列分為10個區間,按最大值和最小值去分區間,然後統計區間資料量。**所以這個區間是動態的,步長需要去計算得到,而不是固定的步長。

如何計算得到這個步長呢?很簡單:

gap = floor(

max(score)

-min

(score)

/ 分段數量)

;

例如在我的資料中:最大的值為1350,最小的為30 ,所以 gap = (1350-30)/10 = 120。

with v as

(select

(max

(score)

-min

(score))/

10as gap from math_test)

<===

=(1) 計算出步長gap

select

concat(

'[',cast(tag as string)

,'--'

,cast(tag + gap as string)

,')'

)as score_range,cnt

from

(select

floor(score/gap)

* gap as tag,

<===

=(2) 計算得到分組標識

gap,

count(*

)as cnt

from

(select

score,

v.gap

from math_test,v

) t1 group

by floor(score/gap)

,gap <===

= (3) 使用笛卡爾將gap關聯在math_test表

) t2

這裡使用了with as 語法先計算處理步長gap,並利用笛卡爾將gap關聯在了源表,因為with as 的結果只要一條資料,所以這裡使用笛卡爾不會造成影響

至此,我們完成了小夥伴提出的需求。

本文根據小夥伴提出的需求,經過一層一層的分析得到了最終的解決方案,這類查詢的應用範圍還是很廣的,同樣也利用了數學的方式優化了sql,讓sql更加靈活切易維護。

– by 倆只猴

SQL優化之使用數學的方式優化SQL編碼01

筆者最近工作大多寫sql為主。所以在此記錄一下工作中遇到的問題和解決方案。直接拿最近的乙個需求舉例,樓主習慣先寫自己原先的笨方法,也是大多數人一下子就能想到的方法,然後在去寫改進的方法。好了,說下具體需求 輕度彙總後的資料涉及到了標籤打分。根據某列 數值型 的範圍進行劃分並打上相應的標籤,並用該標籤...

SQL優化之索引使用

最近找工作,遇到乙個面試題 sql哪些關鍵字會忽略索引?因為之前沒這方面的經驗,當時懵逼了,隨便扯了一點。最終有沒能通過面試很遺憾。我們都知道對查詢進行優化,應盡量避免全表掃瞄,多使用索引,首先應考慮在 where 及 order by 涉及的列上建立索引。不過應該注意兩點 1 並不是所有索引對查詢...

優化SQL的幾種方式

優化的目的 1 盡量保證索引能正確使用。2 盡量避免全域性搜尋。3 索引不是越多越好。方式 1 對查詢進行優化,應盡量避免全表掃瞄,首先應考慮在 where 及 order by 涉及的列上建立索引 2 應盡量避免在 where 子句中對字段進行 null 值判斷,否則將導致引擎放棄使用索引而進行全...