通过php调用存储过程时出错。 Literal与格式字符串不匹配

时间:2017-02-25 11:23:27

标签: php oracle codeigniter stored-procedures

我正在使用带有PHP codeigniter的Oracle 10g Express Edition。

我有一张桌子tbl_movie: -

CREATE TABLE tbl_movie (movie_id NUMBER(11) PRIMARY KEY,
  movie_title VARCHAR2(255) NOT NULL,
  movie_image VARCHAR2(255) NOT NULL,
  language_id NUMBER(11) NOT NULL REFERENCES tbl_language(language_id) ON DELETE CASCADE,
  showtime_id NUMBER(11) NOT NULL REFERENCES tbl_showtime(showtime_id) ON DELETE CASCADE,
  movie_sdate DATE NOT NULL,
  movie_edate DATE NOT NULL,
  movie_add_date DATE NOT NULL
);

现在我通过Oracle命令行运行它来创建一个过程: -

CREATE OR REPLACE PROCEDURE 
        insertMovie (id IN NUMBER, title IN VARCHAR2, image IN VARCHAR2, language IN NUMBER, showtime IN NUMBER, sdate IN DATE, edate IN DATE, adate IN DATE, message OUT NUMBER) 
    IS
        BEGIN
            INSERT INTO tbl_movie (movie_id, movie_title, movie_image, language_id, showtime_id, movie_sdate, movie_edate, movie_add_date) 
            VALUES ( id, title, image, language, showtime, TO_DATE(sdate, 'YYYY-MM-DD hh24:mi:ss'), TO_DATE(edate, 'YYYY-MM-DD hh24:mi:ss'), TO_DATE(adate, 'YYYY-MM-DD hh24:mi:ss'));
            message := 1;
        EXCEPTION
          WHEN OTHERS THEN
            message := 0;
        END;
/

这是我的codeigniter控制器的代码: -

public function testMovie()
{
    $insertData['movie_id'] = 1;
    $insertData['movie_title']= 'test';
    $insertData['movie_image']= 'rfgt';
    $insertData['language_id']= 1;
    $insertData['showtime_id']= 1;
    $insertData['movie_sdate']= '2017-02-24 00:00:00';
    $insertData['movie_edate']= '2017-02-27 00:00:00';
    $insertData['movie_add_date']= '2017-02-20 00:00:00';
    $this->load->model('oracle_model');
    $return = $this->oracle_model->add_movie('movie',$insertData,'');   
}

这是用于调用过程的模型函数: -

function add_movie($entity,$insertData,$time)
{
    print_r($insertData);
    $conn   = oci_connect("xxxxxx", "xxxxxx","xxxxxxxxx");
    if(!$conn)
    {
        echo "connect failed".oci_error();
    }   
    else
    {   
        echo "connect done at".date('d-m-y');

        // calling stored procedure insertMovie
        $insertMovieProcedureSQL = "BEGIN insertMovie(:id, :title, :image, :language, :showtime, :sdate, :edate, :adate, :message); END;";
        $stmt = oci_parse($conn, $insertMovieProcedureSQL);

        //  Bind the input parameter
        oci_bind_by_name($stmt,':id',$insertData['movie_id']);
        oci_bind_by_name($stmt,':title',$insertData['movie_title']);
        oci_bind_by_name($stmt,':image',$insertData['movie_image']);
        oci_bind_by_name($stmt,':language',$insertData['language_id']);
        oci_bind_by_name($stmt,':showtime',$insertData['showtime_id']);
        oci_bind_by_name($stmt,':sdate',$insertData['movie_sdate']);
        oci_bind_by_name($stmt,':edate',$insertData['movie_edate']);
        oci_bind_by_name($stmt,':adate',$insertData['movie_add_date']);

        // Bind the output parameter
        oci_bind_by_name($stmt,':message',$message);

        oci_execute($stmt);
        // $message is now populated with the output value
        echo "$message\n";
    }   
}

当我运行该功能时,我收到以下错误: -

oci_execute(): ORA-01861: literal does not match format string ORA-06512: at line 1

当我尝试通过删除所有date数据类型列来运行该过程时,代码运行正常。我认为主要问题是Date列。

但是当我从命令行运行此查询时,数据已成功插入: -

INSERT INTO tbl_movie (movie_id, movie_title, movie_image, language_id, showtime_id, movie_sdate, movie_edate, movie_add_date) 
            VALUES ( 2, 'title', 'image', 2, 3, TO_DATE('2017-02-24 00:00:00', 'YYYY-MM-DD hh24:mi:ss'), TO_DATE('2017-02-27 00:00:00', 'YYYY-MM-DD hh24:mi:ss'), TO_DATE('2017-02-20 00:00:00', 'YYYY-MM-DD hh24:mi:ss'));

我做错了什么?我被困了将近4个小时。

2 个答案:

答案 0 :(得分:1)

过程参数sdate等属于DATE数据类型。在您的程序中,您将TO_DATE( ...., 'yyyy-mm-dd hh24:mi:ss')应用于他们。

TO_DATE用于将字符串转换为日期,而非日期转换为日期。如果将TO_DATE应用于某个日期,Oracle会首先将日期转换为字符串,以便将其传递给TO_DATE。不幸的是,当Oracle进行隐式转换时,它不会使用TO_DATE中的相同格式模型;相反,它使用调用过程的会话的nls_date_format。这与TO_DATE中的模型不同 - 导致您看到的错误。

有两种方法可以解决这个问题。一种是不使用TO_DATE(sdate, ...),而是在sdate语句中仅使用INSERTsdate和其他日期已经是日期(如果它们首先作为日期正确传递到过程中)。

第二个是,如果实际上你的意思是将参数作为字符串传递,则不要在过程声明中将它们声明为DATE;声明他们VARCHAR2。 (并确保这些字符串完全的格式正确 - 您应该能够从前端控制它。)

每当您看到TO_DATE(....)应用于DATE输入时,都会遇到类似您所看到的错误。相反,只要您看到类似的错误,就会将TO_DATE(....)等可疑代码应用于某个日期。

已添加:还有另一种可能性。如果实际上是将字符串传递给过程,Oracle必须将它们转换为日期(因为它们被声明为这样)。如果字符串不完全采用调用过程的会话的nls_date_format格式,则参数从字符串到日期的初始转换将失败,甚至不会达到INSERT语句

答案 1 :(得分:1)

将输入参数的数据类型从date更改为varchar。在当前场景中,您基本上在to_date数据类型上应用date。将date替换为varchar后,其余逻辑应该可以正常工作。

CREATE OR REPLACE PROCEDURE 
        insertMovie (id IN NUMBER, title IN VARCHAR2, image IN VARCHAR2, language IN NUMBER, showtime IN NUMBER, sdate IN VARCHAR2, edate IN VARCHAR2, adate IN VARCHAR2, message OUT NUMBER) 
    IS
        BEGIN
            INSERT INTO tbl_movie (movie_id, movie_title, movie_image, language_id, showtime_id, movie_sdate, movie_edate, movie_add_date) 
            VALUES ( id, title, image, language, showtime, TO_DATE(sdate, 'YYYY-MM-DD hh24:mi:ss'), TO_DATE(edate, 'YYYY-MM-DD hh24:mi:ss'), TO_DATE(adate, 'YYYY-MM-DD hh24:mi:ss'));
            message := 1;
        EXCEPTION
          WHEN OTHERS THEN
            message := 0;
        END;
/