日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 运维知识 > 数据库 >内容正文

数据库

一条看似不合理SQL和10个合理的解释

發布時間:2025/3/16 数据库 23 豆豆
生活随笔 收集整理的這篇文章主要介紹了 一条看似不合理SQL和10个合理的解释 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

有一天看到了一個開發同學提的問題,感覺蠻有意思,就稍花了些時間總結了下,問題描述如下:

開發的時候碰到類似這么一個問題:

mysql中有表:

create table dept(id int auto_increment primary key comment 'id',level int comment '權重,1--普通員工,2--經理',name varchar(15) comment '員工姓名' )

有一天這個系統用了很長時間了,需要增加一個“副經理”的角色,但是因為1和2已經在很多地方寫死了,只能在原來的基礎上,將3設為“副經理”,并且設計表的人表示以后絕對不會修改這個表了

本人小白,做外包,所以沒辦法

差不多就是這樣,遇到一個需求,是將表中數據按照職位大小進行排序

然后我是這么寫的:

select * from dept order by (4-level)%3 desc

然后問題雖然是解決了,思路也是我自己想出來的,但是我想不通為什么這樣可以運行

有沒有前輩能指點一下,這種解決思路是一個算法,還是一個什么思路?有沒有通用的可以抽取的地方可以學習?這個問題疑惑了我兩個月了,希望可以得到解答

看到回復里面 絕大多數都在說明這個需求的不合理,我想試試其他的方法。

這個問題如果換成一個更有意思的問題,那就是:

那就是如何讓MySQL識別出2比3大?

在此我給出10種解法,大家可以根據自己的情況進行補充,在總結中也得到了

多位同學的支持,他們分別是以下4位。

小葉,開發DBA,我的得力助手

湯勇,運維方向,通信行業,從事MySQL數據庫開發+數據庫維護

小蒙,開發DBA,光電通信,從事MySQL數據庫開發

齊宇軒,北京,運維方向

方法1

? ?

在這里說是方法1是想說明這可能不是最好的方案,但是從規范角度來說是最好的。

我們可以新增一個字段,然后修改數據,按照新的字段排序。

alter table dept add new_level int default 1 ; update dept set new_level=1 where level=1; update dept set new_level=3 where level=2; update dept set new_level=2 where level=3;

使用如下的SQL查詢即可。

select id,level,name from dept order by new_level desc;

方法2

? ?

可以使用filed函數來進行排序

mysql> SELECT * FROM deptORDER BY FIELD(`level`, 2, 3, 1); +----+-------+------+ | id | level | name | +----+-------+------+ | 6 | 2 | ff | | 7 | 2 | gg | | 3 | 3 | cc | | 1 | 1 | aa | | 2 | 1 | bb | | 4 | 1 | dd | | 5 | 1 | ee | +----+-------+------+ 7 rows in set (0.00 sec)

方法3

? ?

可以使用find_in_set函數來進行排序

思路就是把2,3,1組合成一個字符串,然后在字符串中取字符的下標來變相排序。

SELECT * FROM `dept` order by find_in_set(`level`,'2,3,1'); +----+-------+------+ | id | level | name | +----+-------+------+ | 6 | 2 | ff | | 7 | 2 | gg | | 3 | 3 | cc | | 1 | 1 | aa | | 2 | 1 | bb | | 4 | 1 | dd | | 5 | 1 | ee | +----+-------+------+

方法4

? ?

可以使用case when函數來進行排序

select * fromdeptorder by (case when level=1 then 1 when level=2 then 3 when level=3 then 2 end ) desc;

方法5

? ?

可以構造子查詢來進行排序,

里面使用的還是case when,當然還可以使用其他的邏輯

select a.* from dept a,(select id,name,(CASE WHEN level = '1'THEN 'a'WHEN '2'THEN 'c'WHEN '3'THEN 'b' ELSE '0' end) as t from dept ) b where a.id =b.id order by b.t desc;

方法6

? ?

可以使用union/union all函數來進行排序

這種思路看起來有些取巧,但是不失為一種可擴展的方法

select * from dept where level=2union select * from dept where level=3union select *from dept where level=1

接下來我們做幾種略微復雜的方法

方法7

? ?

在這里使用Hash的方法來解決,在問題中已經給出了:

(4-level)%3

我嘗試了如下的方式,對于(x+N)%5,對于x為(1,2,3)可以得到如下的表格。可以看到順序都是按照012012這樣的頻率的,所以在這種模式下是得不到所需的結果的。

x?mod?3x+1x+2x+3x+4x+5
120120
201201
312012

我們把0,1,2當做手環上面的3個數,既然它們可以正向旋轉,也可以反向旋轉,所謂的反向旋轉,輸出的頻率即為:210210

于是我得到了如下的表格:

x?mod?33-x4-x5-x6-x7-x8-x9-x10-x
120120120
212012012
301201201

觀察列表,我們可以看到對于4-x,7-x,10-x都是按照021的順序來排列的。

所以我們可以使用4-x,7-x,10-x得到如下的同樣結果:

mysql> select (4-level)%3,(7-level)%3,level,id from-> dept; +-------------+-------------+-------+----+ | (4-level)%3 | (7-level)%3 | level | id | +-------------+-------------+-------+----+ | 0 | 0 | 1 | 1 | | 0 | 0 | 1 | 2 | | 1 | 1 | 3 | 3 | | 0 | 0 | 1 | 4 | | 0 | 0 | 1 | 5 | | 2 | 2 | 2 | 6 | | 2 | 2 | 2 | 7 | +-------------+-------------+-------+----+ 7 rows in set (0.00 sec)

方法8

? ?

我們可以得到一個大膽的結果,那就是對于3*N+1,這種方式都是可行的。

先輸出level和id的結果:

mysql> select (4-level)%3,level,id fromdept; +-------------+-------+----+ | (4-level)%3 | level | id | +-------------+-------+----+ | 0 | 1 | 1 | | 0 | 1 | 2 | | 1 | 3 | 3 | | 0 | 1 | 4 | | 0 | 1 | 5 | | 2 | 2 | 6 | | 2 | 2 | 7 | +-------------+-------+----+

按照我們剛總結的方式,我們可以得到3N+1的模式都可以輸出結果,我們給出一個自負的測試結果,那就是對于任何大于0的數,測試結果冪等,所以我們構造了一個隨機數,當然這種方式需要注意的是,還需要對id進行升序排列,否則輸出結果還是有些差異。

mysql> select * from-> dept-> order by (3*(FLOOR(RAND()*100)+1)+1-level)%3 desc,id; +----+-------+------+ | id | level | name | +----+-------+------+ | 6 | 2 | ff | | 7 | 2 | gg | | 3 | 3 | cc | | 1 | 1 | aa | | 2 | 1 | bb | | 4 | 1 | dd | | 5 | 1 | ee | +----+-------+------+

方法9

? ?

我們可以根據函數的思想來進行分析,我們設想得到一個圖形。

對于1,2,3分別對應f(1)<f(2)>f(3),這顯然是一個絕對值函數。

如果我們想得到一個精確的值,可以設置函數為y=a|x-2|+bx+c

帶入1,2,3我們會得到如下的三個等式:

  • 2b+c=3

  • a+b+c=1

  • a+3b+c=2

驗算得到,a=-3/2, b=1/2, c=2

我們可以使用如下的三個函數來進行驗證。

mysql> select -3*abs(-1)/2+1/2+2; +--------------------+ | -3*abs(-1)/2+1/2+2 | +--------------------+ | 1.0000 | +--------------------+ 1 row in set (0.00 sec)mysql> select -3*abs(2-2)/2+2/2+2; +---------------------+ | -3*abs(2-2)/2+2/2+2 | +---------------------+ | 3.0000 | +---------------------+ 1 row in set (0.00 sec)mysql> select -3*abs(1)/2+3/2+2; +-------------------+ | -3*abs(1)/2+3/2+2 | +-------------------+ | 2.0000 | +-------------------+ 1 row in set (0.00 sec)

所以這個SQL就呼之欲出了。

select * fromdeptorder by -3*abs(level-2)/2+(level)/2+2 desc;

方法10

? ?

方法8的演進版本,我們可以考慮擬合,即不用精確值。

我們可以這樣設想如果x=2為中位值,那么f(1)=f(3),

在f(1)<f(2)>f(3)的情況下,f(1)到f(2)單調遞增,而f(2)>f(3)使得單調遞減。

這個圖形我們不需要關注它的函數值到底是多少,而是著重于考慮中位值,而要單調遞減,則x需要在2~2.5之間。

mysql> select * from-> dept-> order by -abs(level-2.1) desc; +----+-------+------+ | id | level | name | +----+-------+------+ | 6 | 2 | ff | | 7 | 2 | gg | | 3 | 3 | cc | | 1 | 1 | aa | | 2 | 1 | bb | | 4 | 1 | dd | | 5 | 1 | ee | +----+-------+------+ 7 rows in set (0.00 sec)

? ?

有道無術,術可成;有術無道,止于術

歡迎大家關注Java之道公眾號

好文章,我在看??

總結

以上是生活随笔為你收集整理的一条看似不合理SQL和10个合理的解释的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。