數據庫----如何將oracle語句轉換成mysql語句

前言

  最近由於公司業務上需要,需要將原項目的數據庫由oracle轉換成mysql,轉換的時候我們通常需要先將數據庫轉成mysql,然後再去爲項目添加mysql語句,至於如何將oracle數據庫轉換成mysql數據庫且保證數據庫數據的正確性,可以參考我的上一篇文章,這篇文章的主要作用就是來講一下oracle語句中用到的那些函數,在mysql中應該怎麼使用。


文章地址:

  將Oracle數據庫轉換成mysql數據庫


正文

  首先我們可以先來說一下oracle和mysql的區別,他們的區別就是我們改動的sql的原因。至於其他的區別我就不在這裏多說了,主要是講函數的區別,以及對應情況的處理.
  Oracle最大的特點就是sql的靈活性,它具有強大的分組查詢功能,而對應的函數在mysql中是沒有的,那麼我們如果想要實現同樣的功能,就需要通過其他的方法來實現同樣的功能。

簡單函數之間的轉換

  快速查詢:
  column表示字段名

函數作用 oracle mysql
日期轉字符串 to_char date_format
字符串轉日期 to_date str_to_date
判斷空值 nvl IFNULL
轉換數字 to_number cast
條件判斷 decode case then或if else
時間串拼接 numtodsinterval contact
decimal的轉換 to_char cast(column as char)
當前時間 sysdate now()
substring區別 開始位置可以爲0 開始位置不能爲0
時間的計算 可以直接相減(默認單位:天) 使用 TIMESTAMPDIFF(需指定默認單位)
分組排序 row number over 使用變量來實現

具體實例:

一、當前時間–(sysdate)

  oracle語句:

		select sysdate from dual

  mysql語句:

		select now()
二、日期轉字符串–(to_char)

  oracle語句:

		select  to_char(sysdate,'yyyy-mm-dd hh24:mi:ss') from dual

  mysql語句:

		select DATE_FORMAT(NOW(),'%Y-%m-%d %H:%i:%s')
三、字符串轉日期–(to_date)

  oracle語句:

		select to_date('2019-01-01 08:00:00', 'yyyy-mm-dd hh24:mi:ss') from dual

  mysql語句:

		select STR_TO_DATE('2019-01-01 08:00:00',"%Y-%m-%d %H:%i:%s")
四、判斷空值–(nvl)

  oracle語句:

		NVL(a,b)

  mysql語句:

		IFNULL(a,b)
五、轉換數字–(to_number)

  oracle語句:

		to_number(column)

  mysql語句:

	轉換成整形:
		cast(column as unsigned int)
	轉換成浮點型:
		cast(column as decimal(10,2))
六、條件判斷–(decode)

  oracle語句:

		select decode(mod(quantity,7),0,'A',1,'B','C') as qtype from biz_order

  mysql語句:

	使用case whenSelect case mod(quantity,7) when 0 then 'A'
		 	when 1 then 'B'
			else 'C'
			end as qtype
		from biz_order

	使用if else:
		不推薦使用if-else,因爲我寫不出來,建議使用case when,咳咳咳咳咳-----,
七、時間串拼接–(numtodsinterval)

  oracle語句:

		select a.*,
    		case when length(ltrim(substr(numtodsinterval(ceil(a.reportperiod), 'second'),2,15),'0')) = 6
   			then '0天' || substr(numtodsinterval(ceil(a.reportperiod), 'second'),12,8)
   			when a.reportperiod is null
    		then ''
   			else ltrim(substr(numtodsinterval(ceil(a.reportperiod), 'second'),2,9),'0') || '天' ||
    		substr(numtodsinterval(ceil(a.reportperiod), 'second'),12,8)
    		end as ReportWaitTime 
   		from biz_dev_fault_repair_result a

  mysql語句:

	SELECT
        a.*,
        CONCAT(
        FLOOR( a.reportperiod / 86400 ),
        '天',
        LPAD( FLOOR( a.reportperiod % 86400 / 3600 ), 2, 0 ),
        ':',
        LPAD( FLOOR( a.reportperiod % 86400 % 3600 / 60 ), 2, 0 ),
        ':',
        LPAD( CEIL( a.reportperiod % 86400 % 3600 % 60 ), 2, 0 )
        ) AS ReportWaitTime 
     FROM biz_dev_fault_repair_result a
八、decimal的轉換

  oracle語句:

		select to_char(quantity) from biz_order

  mysql語句:

	需要注意的是,在mysql中轉換時不能使用as varchar//SELECT cast(quantity as VARCHAR) from biz_order  ----這是錯誤的語法
		SELECT cast(quantity as CHAR) from biz_order ----這是正確的語法
九、substring的區別

  oracle語句:

		select substr('1234567890',0,2) from dual	//返回結果12

  mysql語句:

		select substr('1234567890',1,2)		//返回結果12,即相同返回結果
十、時間的計算

  oracle語句:

		select maintaintime,createtime,maintaintime-createtime t from biz_order
		//返回結果以天爲單位

  mysql語句:

		select maintaintime,createtime,TIMESTAMPDIFF(second,maintaintime,createtime) t from biz_order 
		//返回結果以秒爲單位,直接相減默認結果說實話我沒看懂是以什麼爲單位,它是每分鐘爲100,秒數作爲十分位和百分位,逢60就加100

  返回結果以秒爲單位,直接相減默認結果說實話我沒看懂是以什麼爲單位,它是每分鐘爲100,秒數作爲十分位和百分位,逢60就加100

十一、分組排序–(row_number() over)

  oracle語句:

		SELECT a.*,
		ROW_NUMBER() OVER(partition by a.orderchildId order by a.CheckEndTime desc) as rum_num
		FROM biz_qa_check_first a

  mysql語句:

		select @rownum:=@rownum+1 rownum,a.*, 
          if(@orderchildId=a.orderchildId,@rank:=@rank+1,@rank:=1) as rum_num,
           @orderchildId:=a.orderchildId
 		  from(SELECT * from biz_qa_check_first order by orderchildId,CheckEndTimedesc)a,
 		(select @rownum:=0,@orderchildId:=null,@rank:=0)b

補充知識說明:


  1.在mysql中如何獲取當前周

  oracle語句:

		select to_char(sysdate,'iw') from dual

  返回結果:
在這裏插入圖片描述
這表示2019-08-18是該年的第33個周
  mysql語句:

		SELECT DATE_FORMAT(now(),'%u')
		//大寫U與小寫u不一樣

  返回結果:
在這裏插入圖片描述
   下面附上mysql中日期標識符的作用,

		%M 月名字(January……December) 
		%W 星期名字(Sunday……Saturday) 
		%D 有英語前綴的月份的日期(1st, 2nd, 3rd, 等等。) 
		%Y 年, 數字, 4 位   www.2cto.com  
		%y 年, 數字, 2 位 
		%a 縮寫的星期名字(Sun……Sat) 
		%d 月份中的天數, 數字(00……31) 
		%e 月份中的天數, 數字(0……31) 
		%m 月, 數字(01……12) 
		%c 月, 數字(1……12) 
		%b 縮寫的月份名字(Jan……Dec) 
		%j 一年中的天數(001……366) 
		%H 小時(00……23) 
		%k 小時(0……23) 
		%h 小時(01……12) 
		%I 小時(01……12) 
		%l 小時(1……12) 
		%i 分鐘, 數字(00……59) 
		%r 時間,12 小時(hh:mm:ss [AP]M) 
		%T 時間,24 小時(hh:mm:ss) 
		%S 秒(00……59) 
		%s 秒(00……59) 
		%p AM或PM 
		%w 一個星期中的天數(0=Sunday ……6=Saturday ) 
		%U 星期(0……52), 這裏星期天是星期的第一天 
		%u 星期(0……52), 這裏星期一是星期的第一天 
		%% 一個文字“%”。

  2.mysql實現row number over的原理

  在oracle中,ROW_NUMBER() OVER(partition by col1 order by col2) 表示根據col1分組,在分組內部根據col2排序,而此函數計算的值就表示每組內部排序後的順序編號(組內是連續且唯一的)。
  然後在mysql中沒有對應的函數,所以我們選擇使用變量來實現,那麼具體的實現過程是什麼?下面一起來看一下
  oracl語句:

		SELECT a.*,
		ROW_NUMBER() OVER(partition by a.orderchildId order by a.CheckEndTime desc) as rum_num
		FROM biz_qa_check_first a

  mysql語句:

		select @rownum:=@rownum+1 rownum,a.*, 
          if(@orderchildId=a.orderchildId,@rank:=@rank+1,@rank:=1) as rum_num,
           @orderchildId:=a.orderchildId
 		  from(SELECT * from biz_qa_check_first order by orderchildId,CheckEndTimedesc)a,
 		(select @rownum:=0,@orderchildId:=null,@rank:=0)b

假如我們的數據庫對應的數據是下表

orderchildid
10
10
10
11
12

  mysqlsql運行步驟:
    查詢第一行,此時的變量值

@rownum @orderchildId @rank
0 null 0

    判斷條件執行此時orderchilidid不相等,將@rank=1,將@orderchilidid=10
    查詢第二行,此時的變量值:

@rownum @orderchildId @rank
1 10 1

    判斷條件執行此時orderchilidid相等,將@rank=@rank+1,將@orderchilidid=10
    查詢第三行,此時的變量值:

@rownum @orderchildId @rank
2 10 2

    判斷條件執行此時orderchilidid相等,將@rank=@rank+1,將@orderchilidid=10
    查詢第四行,此時的變量值:

@rownum @orderchildId @rank
3 10 3

    判斷條件執行此時orderchilidid不相等,將@rank=1,將@orderchilidid=11
    查詢第五行,此時的變量值:

@rownum @orderchildId @rank
4 11 1

    判斷條件執行此時orderchilidid不相等,將@rank=1,將@orderchilidid=12
    對於以上步驟最終的結果對應的表如下

orderchildid @rank
10 1
10 2
10 3
11 1
12 1

  那麼在我們做完查詢之後,只需要指定查詢出@rank=1的就是實現了以orderchildid分組,由於我們設置了別名,所以我們的篩選條件可以寫爲 where rum_num=1,這樣我們就實現了分組,而在分組之前,我們的嵌套查詢已經實現了排序功能。


  3.說一下mysql中表的別名問題

  比如說這條sql吧

		select *from (
            select distinct a.*,e.name workshopname,f.name rolename,h.name shiftname, m.row_num 
            from  biz_base_person_group a 
            left join biz_base_person_group_asso b on a.id=b.GroupId
            left join biz_base_person_group_dev d on a.id = d.groupid
            left join biz_base_workshop e on a.workshopid = e.id
            left join sys_role f on a.roleid = f.id
            left join biz_base_shift g on a.classcode = g.code
            left join v_sys_dict h on g.code = h.code and h.pCode = 'ShiftType' and h.type = 'zh_CN' 
			left join (select a.id ,if(@id=a.id,@rank:=@rank+1,@rank:=1) as row_num,
            @id:=a.id
			from (SELECT *from biz_base_person_group order by id) a) m on a.id = m.id
			) a
       		where a.row_num = 1
       order by maintaintime desc

  首先我們看到,我們將內部的查詢結果作爲一個表,但是其實我們可以看一下,通常我們寫sql的時候,寫
select *from table where column=’’
  這裏我不爲表名別名也應該可以,但是在mysql中,不爲表名別名會報這個錯誤,但是在oracle中就可以,這個希望大家以後注意
在這裏插入圖片描述


  4.使用mysql實現rownum

   首先看一下oracle的語句:

   	select rownum rn ,a.* from
  		   (
  		   select a.*,b.name typename,c.value from sys_res_i18n a
   			left join sys_dict b on a.type = b.code and b.pcode = 'I18nType'
  			left join sys_res_i18n_type c on a.id = c.pid and c.type = 'zh_CN'
  			order by a.id desc
  			)  a

  返回的結果:
在這裏插入圖片描述
  我第一次寫出的mysql語句,是這樣的

	select @rownum:=@rownum+1 rn,a.* from 
	(
		select a.*,b.name typename,c.value from sys_res_i18n a
		left join sys_dict b on a.type = b.code and b.pcode = 'I18nType'
		left join sys_res_i18n_type c on a.id = c.pid and c.type = 'zh_CN'
		order by a.id desc
	) a,
	(SELECT @rownum:=0) b

  返回的結果相同,但是使用我們公司框架的時候會出現轉換問題,在頁面的數據會顯示爲
在這裏插入圖片描述
  這對客戶來說就很尷尬,不知道出現這問題的原因是什麼,debug之後發現通過反射調用方法之後,返回的就帶有.0,最後我將sql語句改了一下,寫成下面的形式,完美解決問題.

		select cast(@rownum:=@rownum+1 as char) rn,a.* from 
		(
		select a.*,b.name typename,c.value from sys_res_i18n a
		left join sys_dict b on a.type = b.code and b.pcode = 'I18nType'
		left join sys_res_i18n_type c on a.id = c.pid and c.type = 'zh_CN'
		order by a.id desc
		) a,
		(SELECT @rownum:=0) b

  補充: 並不是所有的rownum都需要這麼來實現,假如前臺不需要rownum的值,你可以看一下具體的業務,很可能他的sql只是想取出第一條數據,在mysql中你可以直接使用limit關鍵字就能拿到數據。


  5.使用mysql執行語句塊

  在Oracle中執行語句塊應該以下面的形式:

  begin
      insert into sys_sequence(ObjectName, Id, CreateTime)
              select '${tableobjname}', 1, sysdate from dual where not exists (
                   select 1 from sys_sequence where ObjectName = '${tableobjname}') ;
      update sys_sequence set id = (select nvl(max(id), 1) from ${tableobjname}) where ObjectName = '${tableobjname}';
  end;       

在mysql中執行語句塊,不需要添加begin和end

        insert into sys_sequence(ObjectName, Id, CreateTime)
                select '${tableobjname}', 1, sysdate from dual where not exists (
                     select 1 from sys_sequence where ObjectName = '${tableobjname}') ;
        update sys_sequence set id = (select nvl(max(id), 1) from ${tableobjname}) where ObjectName = '${tableobjname}';    

  6.自增ID

  MySQL不存在sequence,所以在進行數據插入時,需要自增的列需要特殊處理。
  oracle:

   select seq_id.nextval from dual

MySQL:更改表中需要自增的列,如id,更改語句如下:

    alter table biz_order modify id int auto_increment primary key

  在mysql中執行插入時,就不用列出id。

  7.使用mysql實現merge into

oracle語句:

    merge into biz_url_request_para a
		    using (select #{clientid} as clientid, #{url} as url, #{para} as para from dual) b
		    on (a.clientid = b.clientid and a.url = b.url and a.clientid = #{clientid}) 
		    when matched then
		       update set a.para = b.para, timestamp = to_char(sysdate,'yyyy-mm-dd hh24:mi:ss')
		    when not matched then
		       insert values(#{clientid}, #{url}, #{para}, to_char(sysdate,'yyyy-mm-dd hh24:mi:ss'))

mysql

    insert into biz_url_request_para
        (
            clientid,
            url,
            para,
            timestamp
        )
        values (
            #{clientid},
            #{url},
            #{para},
            date_format(now(),'%Y-%m-%d %H:%i:%s')
        )
        on duplicate key update para = values(para), timestamp = values(timestamp)
  8.mysql實現connect by

  首先,關於connect by這個用法我也不是很清楚,具體的大家可以參考這篇文章,但是我這裏舉的例子,因爲功能業務的需求,不是像下面文章一樣的例子。在mysql中,這裏這處業務的實現通過substring_index函數來實現,同樣也有一篇推薦文章。下面我會解釋我所要講的內容。


文章地址:
  (一)connect by 和level 的用法
  (一)字符串截取之substring_index


  首先我們先看一下數據。
  我們數據庫中的數據是這個樣子
在這裏插入圖片描述
  我需要的結果就是以’,’爲分割線,將所有的結果都查詢出來,結果就是這個樣子的。
在這裏插入圖片描述
  我們這裏就要使用substring_index函數,但是我們在使用這個函數的時候,需要指定count的值,但是我們不知道數據庫中有多少個數據,所以我們不能寫一個明確的值,這個時候,我們需要另外一張表來協助,這張表的值是下面的形式
在這裏插入圖片描述
  就是從0開始增長,那麼我們寫出來的sql語句爲

SELECT a.id,SUBSTRING_INDEX(SUBSTRING_INDEX(a.exceptiontype,',',b.Id+1),',',-1) as name  
	from biz_mold_maintain_result a 
	left join v_biz_seq_id b 
	on b.Id < (LENGTH(a.exceptiontype)-LENGTH(REPLACE(a.exceptiontype,',',''))+1) 
where a.id=1 

  首先我們來看on條件的意思,前面的length結果是10,後面的length裏面嵌套了一個replace,是將字段中的‘,’替換爲空,結果是8,那麼這是什麼意思?通過這個結果,我們可以知道的是該字段中有幾個需要切割的值,前面我已經將字段中的值貼出了,我們可以看到,一共有三個值。那麼我們這裏計算出來的結果也是3,但是由於我們協助表從0開始,所以我們條件選擇<3。
  這裏使用了兩次substring_index來切割字符串,第一次的時候,b.id=0,那麼內部的substring_index切出來的字符串爲‘HMFQ’,外部的從右往左切割拿到的字符串同樣是它本身,因爲結果只有一個。
  第二次執行的時候,內部的substring_index切出來的字符串切出的結果是’HMFQ,XC’,外部的從右往左切,得一個,那麼它拿到的就是’XC’。
  第二次執行的時候,內部的substring_index切出來的字符串切出的結果是’HMFQ,XC,AS’,外部的從右往左切,得一個,那麼它拿到的就是’AS’。
  我們就是通過這種方式來實現了遞歸查詢。

  9.mysql實現sum over(partition by column)

還是先拿出oracle語句以及查詢結果:

  		select distinct a.scrapreason,
        sum(a.scrapcount) over(partition by a.itemid) as scrapcount,
        sum(a.scrapcount) over(partition by a.factoryid) as count 
        from da_qa_scrap_daily a
        left join biz_base_workshop b on a.workshop = b.code and a.factoryid = b.factoryid
        where 1=1
        and a.checktype = 'QaCheckFirst'

  查詢出的結果:
在這裏插入圖片描述
  解釋一下oracle語句的作用,他是將數據以項目id即itemid分組計算出每個項目的報廢產品數量,又以工廠id,即factoryid分組計算出總項目的報廢數量,但是卻是以itemid分組後的結果來顯示數據。在mysql中,我沒有想到很好的實現方法,就把兩個sum over拆分成了兩張表,第一張以itemid分組計算出報廢產品數量,第二張以工廠id分組計算出全部產品報廢數量,然後通過其中一個字段來進行左連接,因爲我們都是從同一張表中拿的數據,一定可以找到這個字段。最後寫成的mysql語句如下:

			select distinct
            a.*,b.count count
            from
            (
                select a.*,b.name workshopname,b.id workshopid ,sum(a.scrapcount) itemscrapcount from
                da_qa_scrap_daily a
                left join biz_base_workshop b on a.workshop = b.code and a.factoryid = b.factoryid
                where 1=1 and a.checktype = 'qacheckfirst'
                group by a.itemid
            ) a
            left join
            (
                select a.*,sum(a.scrapcount) count from da_qa_scrap_daily a
                left join biz_base_workshop b on a.workshop = b.code and a.factoryid = b.factoryid
                where 1=1 and a.checktype = 'qacheckfirst'
                group by a.factoryid
            ) b on a.factoryid=b.factoryid

  如果你有更好的實現方法,歡迎在下面評論留言或者私信我。


  由於寫的都是在轉換過程中遇到的一些函數問題,所以整理了一下,當然也有未解決的問題,比如oracle中的connect by函數的實現,我在網上看的教程就是通過自己創建存儲過程來實現同樣的效果,這個就涉及到我的知識盲區了,如果我弄懂了之後會在這裏補充.

總結

  在這裏和大家說一下轉換之中的感想吧,其實在轉換的時候我們大可不必擔心,只要自己慢慢來,先明白原先sql的作用的,包括從哪張表的查詢,查詢了什麼字段。在你轉換的過程中,一步步的分解,先解決嵌套查詢中的轉換工作,在看外部查詢有沒有需要轉換的內容,大部分的函數mysql和oracle中還是相同的,比如一些聚合函數的使用,這都不需要你更改。轉換時要細緻。

  如果文章哪裏有寫的不對的地方,歡迎指出!!!


  鹹魚IT技術交流羣:89248062,在這裏有一羣和你一樣有愛、有追求、會生活的朋友! 大家在一起互相支持,共同陪伴,讓自己每天都活在豐盛和喜樂中!同時還有龐大的小夥伴團體,在你遇到困擾時給予你及時的幫助,讓你從自己的坑洞中快速爬出來,元氣滿滿地重新投入到生活中!

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章