一在书上看到一段sql,不知道怎么用oracle实现,请大侠们帮看看!
CREATE FUNCTION Barcode_CheckSum(IN my_barcode CHAR(12))
RETURNS INTEGER
RETURN
( SELECT ABS(SUM(CAST ( SUBSTRING( barcode Weights.seq FOR 1) AS INTEGER) * Weights.wgt))
FROM ( VALUES (CAST( 1 AS INTEGER), CAST( -1 AS INTEGER)),
( 2, +1),( 3, -1),( 4, +1),( 5, -1),( 6, +1),( 7, -1),( 8, +1),( 9, -1),( 10, +1),( 11, -1),( 12, +1)) AS Weights(seq, wgt)
WHERE barcode NOT SIMILAR TO '%^[0-9]%');
上面的sql中有两个技巧,
技巧1:similar to 双重否定, 难点,oracle正则表达式不好弄啊
技巧2:values()表达式构造了一个表常量,怎么用oracle 写? 整了好长时间没搞定,求教。
CREATE FUNCTION Barcode_CheckSum(IN my_barcode CHAR(12))
RETURNS INTEGER
RETURN
( SELECT ABS(SUM(CAST ( SUBSTRING( barcode Weights.seq FOR 1) AS INTEGER) * Weights.wgt))
FROM ( VALUES (CAST( 1 AS INTEGER), CAST( -1 AS INTEGER)),
( 2, +1),( 3, -1),( 4, +1),( 5, -1),( 6, +1),( 7, -1),( 8, +1),( 9, -1),( 10, +1),( 11, -1),( 12, +1)) AS Weights(seq, wgt)
WHERE barcode NOT SIMILAR TO '%^[0-9]%');
上面的sql中有两个技巧,
技巧1:similar to 双重否定, 难点,oracle正则表达式不好弄啊
技巧2:values()表达式构造了一个表常量,怎么用oracle 写? 整了好长时间没搞定,求教。
解决方案 »
- 改变datafile 的 SCN
- 新人求教,如何写存储过程???
- 关系SQL语句,两表连接查询的疑问
- 触发器锁表的问题
- 问一个触发器问题!
- 求SQL,查询数据库表其中3-6条的数据记录,不作排序。猛谢!
- 敏感数据存储在MSSQL或ORACLE中,大家都是怎么加密的?要求安全性能高
- 我打开oemapp console本地登陆后,然后我单击一个远程的数据库结点,弹出一个对话框,我输入用户名,密码,按确定,结果出现close connec
- 一个很菜的问题,谁帮我一下, 在线等待!
- oracle 中的位操作函数是什么?
- mysql数据库迁移到oracle数据时,表名及字段修改
- linux下 备份oracle 卡住不动
select 1 seq,-1 wgt from dual union all
select 2,1 from dual union all
select 3,-1 from dual union all
select 4,1 from dual union all
select 5,-1 from dual union all
select 6,1 from dual union all
select 7,-1 from dual union all
select 8,1 from dual union all
select 9,-1 from dual union all
select 10,1 from dual union all
select 11,-1 from dual union all
select 12,1 from dual
)
select abs(sum(seq*wgt))
from weights
where seq>=10;
values()可以像gelyon一样用with创建公共临时表,或者用字查询select * from (子查询union all )
RETURNS INTEGER
RETURN
( SELECT ABS(SUM(CAST ( SUBSTRING( barcode FROM Weights.seq FOR 1) AS INTEGER) * Weights.wgt))
FROM ( VALUES (CAST( 1 AS INTEGER), CAST( -1 AS INTEGER)),
( 2, +1),( 3, -1),( 4, +1),( 5, -1),( 6, +1),( 7, -1),( 8, +1),( 9, -1),( 10, +1),( 11, -1),( 12, +1)) AS Weights(seq, wgt)
WHERE barcode NOT SIMILAR TO '%^[0-9]%');
二楼的实现了 常量表,
补充题意:
Barcode_CheckSum 是一个条形码的检验函数,检验规则如下:
(1):计算各奇数位的和得到S1;
(2):计算各偶数位的和得到S2;
条形码的校验和位的计算公式是: ABS(MOD((s1-s2),10))
标准SQL源码 如下:CREATE FUNCTION Barcode_CheckSum(IN my_barcode CHAR(12))
RETURNS INTEGER
RETURN
( SELECT ABS(SUM(CAST ( SUBSTRING( barcode FROM Weights.seq FOR 1) AS INTEGER) * Weights.wgt))
FROM ( VALUES (CAST( 1 AS INTEGER), CAST( -1 AS INTEGER)),
( 2, +1),( 3, -1),( 4, +1),( 5, -1),( 6, +1),( 7, -1),( 8, +1),( 9, -1),( 10, +1),( 11, -1),( 12, +1)) AS Weights(seq, wgt)
WHERE barcode NOT SIMILAR TO '%[^0-9]%');--个人认为改为oracle 语句是要注意如下3点:
(1):输入参数的格式:IN my_barcode CHAR(12)
oracle中是:my_barcode in TYPE, 其中类型不能有大小,my_barcode IN CHAR(12)
会报错:PLS-00103。 不知道oracle为什么要这样,如果想限定参数的大小有什么办法吗?还是只能去函数里面判断?
(2): VALUES () 常量表,作为一个查找表,二楼和四楼已经提到解决方法。(3): 对输入参数my_barcode由数字字符组成的检验:
WHERE barcode NOT SIMILAR TO '%[^0-9]%');换句话描述:组成my_barcode 的所以字符都是数字字符。
sql里处理全称量词的方法是转换成双重否定(有更专业的术语吗?),上面描述可转换成:
my_barcode的字符没有一个是非数字字符
源码中WHERE barcode NOT SIMILAR TO '%[^0-9]%');里的 '%[^0-9]%'应该是:至少有一个是非数字字符的意思。那这个符号'^'在源码里应该是非的意识,而oracle里是表示以某字符打头的意思。四楼的关于正则表达式的代码执行如下,其结果是有误的,SQL> SELECT id
2 FROM a
3 WHERE
4 NOT REGEXP_LIKE(a.id,'%^[0-9]%')
5 ;ID
----------------------------------------------------------------
036AFCLASLAMOMYR
036AFCLASLAMONOK
大家再思考思考啊,期盼中!
2 return varchar2 as
3 v_sign number;
4 v_result number;
5 begin
6 if length(i_str) !=
7 length(translate(i_str, '0123456789' || i_str, '0123456789')) or
8 length(i_str) != 12 then
9 return null;
10 end if;
11 v_sign := 1;
12 v_result := 0;
13 for i in 1 .. length(i_str) loop
14 v_sign := -v_sign;
15 v_result := v_result + substr(i_str, i, 1) * v_sign;
16 end loop;
17 return abs(v_result);
18 end;
19 /
Function created
SQL> select func_identify_barcode('1a') result from dual;
RESULT
--------------------------------------------------------------------------------
SQL> select func_identify_barcode('1') result from dual;
RESULT
--------------------------------------------------------------------------------
SQL> select func_identify_barcode('123456789012') result from dual;
RESULT
--------------------------------------------------------------------------------
4
SQL>
--不知道对不对?
with weights as(
SELECT LEVEL seq,Power(-1,level) wgt
FROM dual
CONNECT BY LEVEL<=12
)
SELECT
abs(sum(Decode(Mod(seq,2),1,seq*wgt,0))-sum(Decode(Mod(seq,2),0,seq*wgt,0))) result
FROM weights
WHERE NOT REGEXP_LIKE(seq,'%[^[:digit:]] %') ;
SELECT LEVEL seq,Power(-1,level) wgt
FROM dual
CONNECT BY LEVEL<=12
)
SELECT abs(sum(Decode(Mod(seq,2),1,seq*wgt,0))-sum(Decode(Mod(seq,2),0,seq*wgt,0))) result
FROM weights
WHERE
NOT REGEXP_LIKE(seq,'[^[:digit:]]') ; --上面正则表达式错的,应该是这样
CREATE OR REPLACE FUNCTION Barcode_CheckSum(my_barcode IN VARCHAR2)
RETURN NUMBER
IS
result NUMBER;
BEGIN
with weights as(
SELECT LEVEL seq,Power(-1,level) wgt
FROM dual
CONNECT BY LEVEL<=my_barcode
)
SELECT abs(sum(Decode(Mod(seq,2),1,seq*wgt,0))-sum(Decode(Mod(seq,2),0,seq*wgt,0))) into result
FROM weights
WHERE
NOT REGEXP_LIKE(seq,'[^[:digit:]]') ; RETURN result;
END;--测试:
SELECT Barcode_CheckSum('12') result FROM dual;--结果:
RESULT
------
78
-- 不好意思,可能是我没把需求说明白,标准条形码有13位,其中最后一位是校验位,
-- 我们这个函数的结果是得到条形码的校验位,该函数的输入参数 my_barcode 是一个标准条形码的前12位,我们现在要得到条形码的校验位,如果输入参数有误,是得不到的结果,上面的正则表达式是对的:NOT REGEXP_LIKE(a.foracid,'[^[:digit:]]') 这个[:digit:] 是 匹配数字, '[^[:digit:]]' 这个组合能解释一下吗?
REGEXP_LIKE(seq,'[^[:digit:]]') 匹配包含了任意非数字字符,
前面加not 就是匹配全部都是数字字符的需求不清楚,只能写到这里了,sorry
八楼的结果是对的,上面做了一下简单注释,TRANSLATE 相关用法如下:/*
一、语法:
TRANSLATE(string,from_str,to_str)
二、目的
返回将(所有出现的)from_str中的每个字符替换为to_str中的相应字符以后的string。TRANSLATE 是 REPLACE 所提供的功能的一个超集。如果 from_str 比 to_str 长,那么在 from_str 中而不在 to_str 中的额外字符将从 string 中被删除,因为它们没有相应的替换字符。to_str 不能为空。Oracle 将空字符串解释为 NULL,并且如果TRANSLATE 中的任何参数为NULL,那么结果也是 NULL。
三、允许使用的位置
过程性语句和SQL语句。
四、示例
SELECT TRANSLATE( 'abcdefghij ', 'abcdef ', '123456 ') FROM dual;
TRANSLATE (
--------------
123456ghij SELECT TRANSLATE( 'abcdefghij ', 'abcdefghij ', '123456 ') FROM dual;
TRANSL
----------
123456
*/
'[^[:digit:]]' 这个组合能解释能在详细点吗? NOT REGEXP_LIKE(seq,'[^[:digit:]]') 表示:在seq中,没有一个字符是非数字字符,
对这部分分解一下:
(1) not (seq 中至少有一个非数字字符)=》
REGEXP_LIKE(seq,'[^[:digit:]]')表示:seq 中至少有一个非数字字符
又因为:这个[:digit:] 是 匹配数字,表示任意一个数字字符
所以进一步替换: REGEXP_LIKE(seq,'[^{任意一个数字字符}]')
剩下的部分:REGEXP_LIKE(seq,'[^ ]') 应是:将 任意一个数字字符 转换成 seq 中至少有一个非数字字符?
这一步是怎么转换的啊?能再解释详细点吗?
一般^在正则表达式里表示匹配字符串的开头位置,难道在[] 里 ^ 变成了 '非' ?也就是说:[] 有转意字符的功能?为什么'[^[:digit:]]' 不是表示的如下意思:任意一个非数字字符 ?没弄明白,不知道是不是问多了,还是想知道原因, 谢谢了,
http://blog.csdn.net/minitoy/archive/2010/11/02/5981729.aspx
http://blog.csdn.net/minitoy/archive/2010/11/05/5990191.aspx
http://blog.csdn.net/minitoy/archive/2010/11/05/5990220.aspx
[^[:digit:]]表示的是字段含有非数字字符,前面再加个not取反,所以整个的意思是取不含有非数字字符的串,也就是全是数字字符构成的串.
RETURN INTEGER AS
v_my_barcode VARCHAR(12):= NULL;
barcode_checkers INTEGER:=0;
idx INTEGER :=1;
sgn INTEGER:=-1;
num_flg NUMBER:=NULL;
BEGIN
-- 这个begin块可以不用,只是为了便于维护,才加了一个v_my_barcode 变量,
-- 如果 my_barcode 的名字需要修改时,只用改一处即可
BEGIN
--{
v_my_barcode := trim(my_barcode);
EXCEPTION WHEN OTHERS THEN
dbms_output.put_line('my_barcode too long');
RETURN -1;
--}
END;
---- check length
IF length(v_my_barcode) <>12 THEN
--{
RETURN -1;
dbms_output.put_line('my_barcode length is not right');
--}
END IF; ----check all char is numchar exception point
-- 利用 to_number判断是不是全部是数字字符,
-- 但单用to_number不能排除有小数点的情况,
-- 所以用like排除一下
-- 如果有小数点,num_flg就不为空, 再在下一个if块中判断
-- 值得一提的时,虽然others可以捕获所有异常,但是如果一个异常被前一个when捕获,
-- when other 就不会再进行捕获
BEGIN
--{
SELECT to_number(v_my_barcode)
INTO num_flg
FROM dual WHERE v_my_barcode LIKE '%.%';
EXCEPTION
WHEN no_data_found THEN
num_flg := NULL;
WHEN OTHERS THEN
dbms_output.put_line('my_barcode there are some NAN');
dbms_output.put_line(SQLCODE||' '|| SQLERRM);
RETURN -2;
--}
END; --check all char is numchar, exception point
IF num_flg IS NOT NULL THEN
--{
dbms_output.put_line('my_barcode there have one point');
RETURN -2;
--}
END if; --计算各奇数位的和减去各偶数位的和
WHILE idx <13 LOOP
--{ sgn := -sgn;
barcode_checkers := barcode_checkers + to_number(substr(v_my_barcode,idx,1))*sgn;
idx := idx + 1;
--}
END LOOP;
--
RETURN ABS(MOD(barcode_checkers,10));END BarcodeCheckSum;
/
select BarcodeCheckSum('111111114111') AS barcode from dual;
又用刚学到的改写了一下,SELECT ABS(SUM( SUBSTR( '121212121212', Weights.seq, 1) * Weights.wgt))
FROM (SELECT 1 seq, 1 wgt FROM dual
UNION ALL
SELECT 2 , -1 FROM dual
UNION ALL
SELECT 3 , 1 FROM dual
UNION ALL
SELECT 4 , -1 FROM dual
UNION ALL
SELECT 5 , 1 FROM dual
UNION ALL
SELECT 6 , -1 FROM dual
UNION ALL
SELECT 7 , 1 FROM dual
UNION ALL
SELECT 8 , -1 FROM dual
UNION ALL
SELECT 9 , 1 FROM dual
UNION ALL
SELECT 10 , -1 FROM dual
UNION ALL
SELECT 11 , 1 FROM dual
UNION ALL
SELECT 12 , -1 FROM dual)
Weights
WHERE NOT REGEXP_LIKE('121212121212', '[^[:digit:]]');上面的sql没问题,但是放到函数里却有问题:
函数如下:CREATE OR REPLACE FUNCTION Barcode_CheckSum( my_barcode IN VARCHAR)
RETURN INTEGER
AS
BEGIN
RETURN SELECT ABS(SUM( SUBSTR( '121212121212', Weights.seq, 1) * Weights.wgt))
FROM (SELECT 1 seq, 1 wgt FROM dual
UNION ALL
SELECT 2 , -1 FROM dual
UNION ALL
SELECT 3 , 1 FROM dual
UNION ALL
SELECT 4 , -1 FROM dual
UNION ALL
SELECT 5 , 1 FROM dual
UNION ALL
SELECT 6 , -1 FROM dual
UNION ALL
SELECT 7 , 1 FROM dual
UNION ALL
SELECT 8 , -1 FROM dual
UNION ALL
SELECT 9 , 1 FROM dual
UNION ALL
SELECT 10 , -1 FROM dual
UNION ALL
SELECT 11 , 1 FROM dual
UNION ALL
SELECT 12 , -1 FROM dual)
Weights
WHERE NOT REGEXP_LIKE('121212121212', '[^[:digit:]]')
AND ROWNUM<2;
END;
LINE/COL ERROR
-------- ---------------------------------------------------------------------5/10 PLS-00103: Encountered the symbol "SELECT" when expecting one of the following: ( - + ; case mod new not null <an identifier>
百度一个类似的答案说:高版本的语法低版本未必支持.但是下面这个也不行:
SQL> CREATE OR REPLACE FUNCTION Barcode_CheckSum( my_barcode IN VARCHAR)
2 RETURN num_test_tab
3 AS
4 BEGIN
5 RETURN SELECT 5 FROM dual WHERE ROWNUM<2;
6 END;
7 /Warning: Function created with compilation errorsSQL> show err
Errors for FUNCTION DBREAD.BARCODE_CHECKSUM:LINE/COL ERROR
-------- -------------------------------------------------------------5/9 PLS-00103: Encountered the symbol "SELECT" when exp......SELECT 5 FROM dual WHERE ROWNUM<2; 这条语句够简单为什么还会出错?
SQL> CREATE OR REPLACE FUNCTION Barcode_CheckSum( my_barcode IN VARCHAR)
2 RETURN INTEGER
3 AS
4
5 num NUMBER:=0;
6 BEGIN
7 BEGIN
8 SELECT ABS(SUM( SUBSTR( my_barcode, Weights.seq, 1) * Weights.wgt))
9 INTO num
10 FROM (SELECT 1 seq, 1 wgt FROM dual
11 UNION ALL
12 SELECT 2 , -1 FROM dual
13 UNION ALL
14 SELECT 3 , 1 FROM dual
15 UNION ALL
16 SELECT 4 , -1 FROM dual
17 UNION ALL
18 SELECT 5 , 1 FROM dual
19 UNION ALL
20 SELECT 6 , -1 FROM dual
21 UNION ALL
22 SELECT 7 , 1 FROM dual
23 UNION ALL
24 SELECT 8 , -1 FROM dual
25 UNION ALL
26 SELECT 9 , 1 FROM dual
27 UNION ALL
28 SELECT 10 , -1 FROM dual
29 UNION ALL
30 SELECT 11 , 1 FROM dual
31 UNION ALL
32 SELECT 12 , -1 FROM dual)
33 Weights
34 WHERE NOT REGEXP_LIKE(my_barcode, '[^[:digit:]]');
35
36 END;
37
38 RETURN num;
39 END;
40 /Function createdSQL>
SQL> select Barcode_CheckSum('111111111111') from dual;BARCODE_CHECKSUM('111111111111
------------------------------
0
--写那么多union all
--还不如这样直接点
SELECT LEVEL seq,Power(-1,level) wgt
FROM dual
CONNECT BY LEVEL<=12
从这里看明白你 的意思
补充题意:
Barcode_CheckSum 是一个条形码的检验函数,检验规则如下:
(1):计算各奇数位的和得到S1;
(2):计算各偶数位的和得到S2;
条形码的校验和位的计算公式是: ABS(MOD((s1-s2),10))
标准SQL源码 如下:等下
已写入 file afiedt.buf 1 create or replace function fun_dt(str varchar2) return number
2 as
3 sum1 number:=0;
4 sum2 number:=0;
5 s1 varchar2(10);
6 s2 varchar2(10);
7 num varchar2(100);
8 begin
9 select decode(sign(length(str)-length(regexp_replace(str,'[[:alpha:]]',''))),0,str,null)
10 into num from dual;
11 if num is null or length(num)<12 then
12 return null;
13 else
14 for i in 1..length(num) loop
15 if mod(i,2)=0 then
16 select substr(num,i,1) into s1 from dual;
17 sum1:=sum1+s1;
18 else
19 select substr(num,i,1) into s2 from dual;
20 sum2:=sum2+s2;
21 end if;
22 end loop;
23 return abs(mod((sum1-sum2),10)) ;
24 end if;
25 exception
26 when others then
27 dbms_output.put_line(sqlcode||' '||sqlerrm);
28* end;
SQL> /函数已创建。SQL> select fun_dt('123456789012') f1,
2 fun_dt('12s3456789012') f2,
3 fun_dt('12233') f3,
4 fun_dt('111111111111') f2
5 from dual
6 / F1 F2 F3 F2
---------- ---------- ---------- ----------
4 0
RETURN INTEGER
ASnum NUMBER:=0;
BEGIN
BEGIN
SELECT ABS(SUM( SUBSTR( my_barcode, Weights.seq, 1) * Weights.wgt))
INTO num
FROM ( SELECT LEVEL seq,Power(-1,level) wgt
FROM dual
CONNECT BY LEVEL<=12)
Weights
WHERE NOT REGEXP_LIKE(my_barcode, '[^[:digit:]]');
END;RETURN num;
END;
RETURN INTEGER
ASnum NUMBER:=0;
BEGIN
BEGIN
SELECT MOD(ABS(SUM( SUBSTR( my_barcode, Weights.seq, 1) * Weights.wgt)),10)
INTO num
FROM ( SELECT LEVEL seq,Power(-1,level) wgt
FROM dual
CONNECT BY LEVEL<=12)
Weights
WHERE NOT REGEXP_LIKE(my_barcode, '[^[:digit:]]');
END;RETURN num;
END;
RETURN INTEGER
ASnum NUMBER:=0;
BEGIN
BEGIN
SELECT ABS(MOD(SUM( SUBSTR( my_barcode, Weights.seq, 1) * Weights.wgt),10))
INTO num
FROM ( SELECT LEVEL seq,Power(-1,level-1) wgt
FROM dual
CONNECT BY LEVEL<13)
Weights
WHERE NOT REGEXP_LIKE(my_barcode, '[^[:digit:]]');
END;RETURN num;
END;
-- select Barcode_CheckSum('121212121212') from daul;
CREATE OR REPLACE FUNCTION func_identify_barcode(i_str VARCHAR2)
RETURN VARCHAR2 ASv_sign NUMBER;
v_result NUMBER;BEGIN
-- exp:i_str 为: abc1
-- 连接后的字符串:'0123456789abc1', 比'0123456789' 要长,
-- 在'0123456789abc1'而不在0123456789中的字符,由于没有替代的字符,
-- abc将要从 i_str(abc1)中删去,只剩下1,所以经过translate函数处理后返回1,其长度也为:1。
IF length(i_str) != length(translate(i_str, '0123456789' || i_str, '0123456789'))
OR length(i_str) != 12 THEN
--{
RETURN NULL;
--}
END IF;v_sign := 1;
v_result := 0;FOR i IN 1 .. length(i_str) LOOP
--{
v_sign := -v_sign;
v_result := v_result + substr(i_str, i, 1) * v_sign;
--}
END LOOP;RETURN abs(mod(v_result,10));
END;
/
--SELECT func_identify_barcode('92949a789012') result from dual;