背景
問題
oracle寫法:
SELECT ? AS id,
REGEXP_SUBSTR (?, '[^,]+', 1,rownum) n0,
REGEXP_SUBSTR (?, '[^,]+', 1,rownum) n1,
... 中間省略一堆
REGEXP_SUBSTR (?, '[^,]+', 1,rownum) nn
from dual connect by rownum<=LENGTH (?) - LENGTH (regexp_replace(?, ',', ''))+1
說明:
先對用到的函數一一釋義,
1. REGEXP_SUBSTR: 官方文檔的截圖
REGEXP_SUBSTR通過允許在字符串中搜索正則表達式模式來擴展SUBSTR函數的功能,不過它返回的不是子字符串的位置,而是子字符串本身。
source_char是用作搜索值的字符表達式
pattern是正則表達式
position是一個正整數,指示Oracle應該在其中開始搜索的source_char字符。默認值爲1,表示Oracle從source_char的第一個字符開始搜索
occurrence是一個正整數,指示Oracle應該在source_char中搜索哪個模式出現。缺省值爲1,表示Oracle搜索第一次出現的模式
match_parameter是文本字面值,可讓您更改函數的默認匹配行爲
對於具有子表達式的模式,subexpr是從0到9的非負整數,指示該函數將返回模式中的哪個子表達式
先忽略connect by的部分,假定rownum是1:Select REGEXP_SUBSTR (?, '[^,]+', 1,rownum) from dual,也假定?變量的值是字符串aa,bb,cc.
可以看做, Select REGEXP_SUBSTR (‘aa,bb,cc’, '[^,]+', 1,1) from dual的意思是將’aa,bb,cc’,以逗號分隔取第一個,即aa.
實際我的業務場景遇到一個問題:
這裏用的?變量,這個?在這裏出現一次,意味’aa,bb,cc’,做爲參數只能出現一次,我轉換爲mysql時也只能出現一次。
所以,需要避免’aa,bb,cc’會作爲2次參數使用的實現方式。
不要問爲什麼,當前這個?號不能用兩次。所以如果是連續逗號去重也很麻煩,做爲參數只能出現一次,那就不好處理。oracle好歹有這個正則拆分,一次處理好。
根據我推測當前上下文場景,排除掉了出現連續逗號的情況,所以轉mysql實際看做一個逗號處理。
mysql替代方案:
使用SUBSTRING_INDEX(
str
,
delim
,
count
)
在出現定界符delim之前,從字符串str返回子字符串。count是我要的那個1。
2. oralce : length(str),返回字符串長度
此處理論上不能使用mysql的length,oracle的length使用字符來定義長度,而mysql的length是以字節爲單位的,
比如字符串’abcde‘,兩個都返回5,但如果是字符串’abcde中國人’,有3個漢字,看效果:
Oracle還是8:
Mysql是14:
mysql替代方案:CHAR_LENGTH(
str
)
/ CHARACTER_LENGTH(str)
:返回字符串str的長度,以字符爲單位
3. oracle: regexp_replace(src, pat, dst), 將字符串src中的pat,替換爲dst
把逗號去掉
mysql替代方案:
當前場景使用:REPLACE(
str
,
from_str
,
to_str
)
即可。返回字符串str,其中所有出現的字符串from_str都替換爲字符串to_str。
4. 最後一部分:connect by rownum<=
不多說,看結果 吧,只查dual,rownum是1:
但是這裏要查一個數字序列,想遞歸查詢:
所以上面整個sql的目的就是:將字符串以逗號進行拆分,轉換爲行。如下:
mysql最終解決方案:
SELECT
SUBSTRING_INDEX(
SUBSTRING_INDEX('aa,bb,cc', ',', rownum),
',',
- 1
)
FROM
(
SELECT
@rownum :=@rownum + 1 AS rownum,
TABLE_NAME
FROM
information_schema. PARTITIONS,
(SELECT @rownum := 0) t
WHERE
DATABASE () = TABLE_SCHEMA
) t
WHERE
rownum <= CHAR_LENGTH('aa,bb,cc') - CHAR_LENGTH(REPLACE('aa,bb,cc', ',', '')) + 1;
說明下:
對於其中用到的幾個函數,前面都已經說明了。這個@rownum是我定義的變量。對於:
SUBSTRING_INDEX(
SUBSTRING_INDEX('aa,bb,cc', ',', rownum),
',',
- 1
)
這部分,是通過rownum遞增,分別取出aa,bb,cc。內層的: SUBSTRING_INDEX('aa,bb,cc', ',', rownum),隨着rownum遞增,分別返回aa\aa,bb\,aa,bb,cc,外層的參數-1,就是分別返回aa,bb,cc了。
rownum <= CHAR_LENGTH('aa,bb,cc') - CHAR_LENGTH(REPLACE('aa,bb,cc', ',', '')) + 1;這部分返回rownum的限制大小,就是aa,bb,cc能分成幾部分。
from-where中間的嵌套子查詢和變量定義,其實返回的是當前數據庫中所有表的表名和它的行號,它的行號就是rownum的值。這種實現只對我當前場景百分百準確有用。因爲aa,bb,cc實際在當前場景是數據庫的表名組成的,所以它拆分的個數一定不會超過當前數據庫表的總量。如果不是我這種場景,無法確定拆分字符串的時候能拆出來多少,這個rownum可能就要足夠大,至少要比其中的分隔符要多吧,就需要考慮其它實現方案,比較藉助輔助表及輔助字段等手段。
oracle寫法:哎,這場景。 一條一二百行的sql,要改動的點不少,還有一堆變量,不說了,只補充點前面沒提示到的吧。
1)Case when不用改,oracle基本和mysql差不多。
2)substr(str, instr(str,?), length(?)) = ?,,,用來判斷str裏是否包子字符串
substr: SUBSTR函數返回char的一部分,從char位置開始,substring_length個字符長。 SUBSTR使用輸入字符集定義的字符來計算長度。 SUBSTRB使用字節而不是字符。 SUBSTRC使用Unicode完整字符。 SUBSTR2使用UCS2代碼點。 SUBSTR4使用UCS4代碼點.
mysql替代方案:
substr可以使用Mysql的:SUBSTR()
/SUBSTRING()
.
所以不用改了
3)to_char(to_date(to_char(begintime,'yyyy-mm-dd'),'yyyy-mm-dd')+7,'yyyy-mm-dd') >= to_char(to_date(to_char(process_time,'yyyy-mm-dd'),'yyyy-mm-dd'),'yyyy-mm-dd')
不就判斷兩個日期是否相差7天,最後還用字符串字典排序比較,真tm費勁.
Mysql替代方案:
DATE_ADD/ ADDDATE
DATE_FORMAT(DATE_ADD(begintime,INTERVAL 7 DAY),'%Y-%m-%d') >= DATE_FORMAT(process_time,'%Y-%m-%d ')
oracle寫法:add_months
ADD_MONTHS返回一個加上加上整數月的日期
mysql替代方案:DATE_ADD/ ADDDATE
這裏的sysdate就用now()代替了,畢竟當前場景需要使用now()了。
oracle寫法:MOD
求餘
Mysql: MOD(
N
,
M
)
, N
%
M
, N
MOD
M
都可以,這裏就不用改了
oracle寫法:最後一個
oracle創建表的時候:其中有個字段
create table t_demo(
day NUMBER(2) default to_char(SYSDATE, 'dd')
)
默認值 是當月的第幾天,需要調用函數 。Mysql默認值 是不能調用函數的。
mysql替代方案:創建觸發器
-- 創建 表t_demo 的觸發器
CREATE TRIGGER t_demo BEFORE INSERT ON t_demo FOR EACH ROW
BEGIN
IF NEW.day IS NULL THEN
SET NEW.day = DATE_FORMAT(now(), '%d');
END
IF;
END;