Oracle之to_char()小碰撞

在使用Oracle的to_char()函数时,遇到因变量长度不足导致的错误。通过调整变量空间大小解决了报错,但发现转换后的数值前多了空格。经研究发现,to_char()在特定格式下,会根据数值位数添加空格和0。最终通过TRIM函数去除空格,得到正确结果。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

2014/11/6,今天在练习存储过程的时候,to_char()函数和我小小的闹腾了一把。

/* PROCEDURE RaceInfo
**   Join tables: BoatCrew,Boat and Person,
**   to get the information:
**     BoatCategory, BowNumber, Position, LastName, FirstName
**   Note:
**     the posible value of the column Position is between 0 and 9, if
**     the position is 0, print 'Cox';
**     others, print the string of three characters of the number.
**     for an instance, if position is 1, print '001'
**   Input: BoatCat -- the category of the Boat
**                     if BoatCat is 'ALL', fetch all the records,
**                     otherwise just print the records of the special
**                     boat category
**          BowNo   -- the bow number
**   Output: just print the information
*/
CREATE OR REPLACE PROCEDURE RaceInfo
(BoatCat IN VARCHAR2, BowNo IN VARCHAR2) AS
  v_Count INTEGER := 0;
  v_Position VARCHAR(3);
  CURSOR cv_allPerson IS
    SELECT BoatCategory, BowNumber, Position, 
           LastName, FirstName
    FROM BoatCrew   JOIN Boat 
                   USING (BoatID)
                    JOIN Person 
                   USING (PersonID)
    ORDER BY BoatCategory, BowNumber, Position;

  CURSOR cv_onePerson IS
    SELECT BoatCategory, BowNumber, Position, 
           LastName, FirstName
    FROM BoatCrew   JOIN Boat 
                   USING (BoatID)
                    JOIN Person 
                   USING (PersonID)
    WHERE UPPER(BoatCategory) = UPPER(BoatCat)
      AND UPPER(BowNumber) = UPPER(BowNo)
    ORDER BY BoatCategory, BowNumber, Position;

BEGIN
  SELECT COUNT(*)
  INTO v_Count
  FROM Boat
  WHERE UPPER(BoatCategory) = UPPER(BoatCat)
    AND UPPER(BowNumber) = UPPER(BowNo);

  IF UPPER(BoatCat) = 'ALL' THEN
    FOR X IN cv_allPerson LOOP
      IF X.Position = 0 THEN
        v_Position := 'Cox';
      ELSE
        v_Position := TO_CHAR(X.Position, '009');
      END IF;

      DBMS_OUTPUT.PUT_LINE(
        ' ' || X.BoatCategory ||
        ' ' || X.BowNumber ||
        ' ' || v_Position ||
        ' ' || X.LastName ||
        ' ' || X.FirstName
      );
    END LOOP;
  ELSIF v_Count > 0 THEN
    FOR X IN cv_onePerson LOOP
      IF X.Position = 0 THEN
        v_Position := 'Cox';
      ELSE
       v_Position := TO_CHAR(X.Position, '009');
      END IF;

      DBMS_OUTPUT.PUT_LINE(
        ' ' || X.BoatCategory ||
        ' ' || X.BowNumber ||
        ' ' || v_Position ||
        ' ' || X.LastName ||
        ' ' || X.FirstName
      );
    END LOOP;
  ELSE
    DBMS_OUTPUT.PUT_LINE('Bat parameters');
  END IF;

EXCEPTION
  WHEN OTHERS THEN
    DBMS_OUTPUT.PUT_LINE(SQLERRM);
END RaceInfo;
/

当我把,存储过程放到SQL * PLUS进行编译的时候,没有报错。但是我运行如下测试案例的时候,问题就出来了。

CALL RaceInfo('ALL', 'X');

CALL RaceInfo('Crew-8', '3');

报出的错误如下:

从出现的错误提示,比较容易判断可能是我定义的变量大小不太合适。我一开始就把注意力集中在了v_Positionv_Position VARCHAR2(3))这个变量。但是,从存储过程的执行情况来看,它输出了一行数据,输出数据的Position列是‘Cox’,说明该行的值是0。说明v_Position := 'Cox';这个分支运行是没有问题的。然后问题就缩小到了v_Position := TO_CHAR(X.Position,'009');这个分支上。但是,老实说,以我目前掌握的情况,我真心没有看出这儿有什么问题。

从报出的问题来看,是我定义变量空间太小的缘故。所以,我抱着试一试的心态把我的变量空间调大一些。将v_PositionVARCHAR2(3)调成v_PositionVARCHAR2(4),重新编译和调用之后,确实没有报错了。但是从输出情况来看,又引发了新的问题。以下是输出结果:

Position列的值不为0的时候,走的是v_Position := TO_CHAR(X.Position,'009');这个分支,从输出情况来看,前面多出了一个空格。这次,情况很明显,问题应该出在TO_CHAR()函数身上。

然后就是,果断的的使用搜索引擎。

终于,在某个地方翻到了如下解释:

 

To_char(var, [format]);

[format]为‘099’这种模式的时候,假设N代表[format]09的总个数。

var的位数小于或等于N,则转换后的字符串前面有一个空字符和(N – var的位数 – 1)0;

var的位数大于N时,则无法正确的转换。

 

到现在为止,一切都已经水落实出了。我一开始使用v_Position :=TO_CHAR(X.Position, '009');转化的时候,因为PositionNUMBER(1),转化出来的结果就是空字符 + 001’这种模式,我调用存储过程的时候,就报出了“字符串缓冲区太小”(那个时候我是VARCHAR2(3),显然不够)的错。当我将v_Position换成VARCHAR2(4)的时候,就刚好够了,但是,由于前面有一个空字符的原因,所有,打印出来的效果,就不太美观。

 

所以,最后的解决方式就是,用TRIM将空格给去掉:

v_Position := TRIM(TO_CHAR(X.Position, ‘009’));

输出结果如下:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值