Oracle Pro * C - 在嵌套循环中使用游标

时间:2014-02-20 00:21:38

标签: c oracle oracle-pro-c embedded-sql

我正在教自己Pro * C并且有一个程序通过光标在数据库中记录(据说),并编译并运行。问题是,当变量打印时(使用光标读入的那些),我得到内存中的垃圾。

我尝试用几种不同的方式拆分 sql exec 语句,但这没有帮助。还尝试在不同的地方打开和关闭sql,也无济于事。我真的在一个漫长的调试过程结束时,我很确定在这一点上我犯了一个非常新手的错误。如果这里的任何Oracle程序员都不介意花一点时间,我真的想要一些关于如何回到正轨的反馈。

它应该打印:

Enter a Guest_ID(type 0 to terminate)>>

1

Charge Summary for: Firstname Lastname Guest-ID: 1

Sales_Item: 1 – Room (Taxable)

Hotel-Id Hotel-Name Trans-Date Quantity Unit-Price Extended-Price

Hotel-Id Hotel-Name Trans-Date Quantity Unit-Price Extended-Price

Hotel-Id Hotel-Name Trans-Date Quantity Unit-Price Extended-Price

Sales Item Total Quantity Extended-Price

它实际打印:

Enter a Guest_ID(type 0 to terminate)>>

3

Charge Summary for: l▒   Guest_ID: 3

我觉得我完全弄乱了光标,但我无法准确指出问题所在,因为我仍然习惯于如何在Pro * C中声明和使用变量。此外,C程序通常是经过调试的,但这是在远程服务器上运行的,调试非常有限,甚至没有dbx命令。

代码:

#include<stdio.h>
#include<string.h>
#include<stdlib.h>
exec sql include sqlca;

// OK - Here we GO
void main()
{
    // First, create all the variables that we will need to communicate between 
    // the "C" program and the database
    exec sql begin declare section;
        //VARCHAR sLastName[51], sFirstName[51], sHotelName[51], sCheckInDate[12], sRoom[11];
        VARCHAR sLastName[51], sFirstName[51], sHotelName[51], sTransDate[11];
        //int nDays, nGuest_ID, nCount;
        int nGuest_ID, nQuantity, nUnitPrice, nCount, nHotelID, nItemID;
        //VARCHAR sInCity[11];
        VARCHAR sItemName[31], sTaxable[11];
        VARCHAR sUserID[21], sPassword[21];
    exec sql end declare section;

        // Now define the cursor we will use to get all of the charges that the guest incurred at all hotels
    exec sql declare dbGuest cursor for
        Select G.Guest_ID, G.Last_Name, G.First_Name, C.Item_ID, C.Item_Name, C.Quantity, C.Unit_Price, C.Trans_Date, H.Hotel_Name, H.Hotel_ID, SI.Taxable
        From Hotel H, Charge C, Stay S, Guest G, Sales_Item SI Where
        C.Stay_ID=S.Stay_ID And H.Hotel_ID=S.Hotel_ID And G.Guest_ID=S.Guest_ID
            And SI.Item_ID=C.Item_ID
        Group By S.Guest_ID;

    // Set up the user-id and password to access my database
    // Because we are using the local database on this server
    // we don't need to use any database location or SID
    strcpy(sUserID.arr,"myuserid"); 
    strcpy(sPassword.arr,"mypassword");  
    sUserID.len=strlen(sUserID.arr);
    sPassword.len=strlen(sPassword.arr);
    exec sql connect :sUserID identified by :sPassword;

    // sqlca.sqlcode is a variable that is set based on the last command sent in to the database
    // a value anything other than zero for what we just did (connect to the database) indicates
    // a error.
    if(sqlca.sqlcode !=0)
       {
        //printf("Sorry, cannot connect to server, pgm aborted %s\n",sqlca.sqlcode); //correction 2/5/14
        printf("Sorry, cannot connect to server, pgm aborted %d\n",sqlca.sqlcode); //change to %d
        exit(1);
       }
    //we made it here, so we were able to open the database correctly
    exec sql SELECT COUNT(*) INTO :nCount FROM Guest;
    printf ("There are %d Guests.\n",nCount);
    for(;;){
        // Read in through stdio the Guest we want to query, then set it up do we can use it
        printf("Enter a Guest_ID(type 0 to terminate)>>\n");
        scanf("%d",&nGuest_ID);
        //Guest_ID.len= strlen(Guest_ID.arr);
        if(nGuest_ID==0)
        {
            printf("BYE\n");
            exit(0);
        }
        printf("%s %s %s %s %d\n","Charge Summary for:", sFirstName.arr, sLastName.arr, " Guest_ID:", nGuest_ID);
        //printf("I do not work yet (type exit to terminate)>>\n");
                // Open our cursor and begin reading records
        exec sql open dbGuest;
        for(;;)
        {
            //exec sql fetch dbGuest into :nGuest_ID, :sLastName, :sFirstName, :sHotelName, :sCheckInDate, :nDays, :sRoom;
            exec sql fetch dbGuest into :sLastName, :sFirstName, :nItemID, :sItemName, :nQuantity, :nUnitPrice, :sTransDate, :sHotelName, :nHotelID;
            if(sqlca.sqlcode !=0)  // If anything went wrong or we read past eof, stop the loop
            {
                break;
            }
            printf("%s %s %s %s %d\n","Charge Summary for:", sFirstName.arr, sLastName.arr, " Guest_ID:", nGuest_ID);
            // Do the crazy stuff to end the C-Strings
            sLastName.arr[sLastName.len] = 0;
            sFirstName.arr[sFirstName.len] = 0;
            sItemName.arr[sItemName.len] = 0;
            sTransDate.arr[sTransDate.len] = 0;
            sHotelName.arr[sHotelName.len] = 0;

            // Print out the information for this guest
            printf("%s %d %s %s \n", "Sales_Item: ", nItemID, " - ", sItemName.arr);

            printf("%d %s %s %d %d \n", nHotelID, " ", sHotelName.arr, " ",sTransDate.arr, " ", nQuantity, " ", nUnitPrice);
        }
        // close the cursor and end the program
        exec sql close dbGuest ;
    }
    exit(0);
}

通常情况下,C程序会在调试器中运行,但这是Pro C而且我对整个Oracle Pro C调试事件感到很遗憾(因为它在远程数据库上运行)。

通过这些但没有帮助:

Strange behaviours with oracle nested cursors

Oracle ProC INSERT INTO VALUES ( (select ...) )

Oracle Pro*C updating table with cursor failed

我被告知应该以不同方式声明VARCHAR变量,但其他方式似乎会抛出错误。

2 个答案:

答案 0 :(得分:1)

即使在将值提取到sFirstName之前,也要打印它们。首先,因为您没有初始化它,它会打印 garbage 值。此外,如果您觉得游标提取被中断,在* break * ing LOOP之前,请使用sqlca的sqlerrm来打印错误消息。与sqlca.sqlerrm.sqlerrmc

一样

然后,您的OPEN CURSOR调用将失败,因为查询存在语法错误。 因此,您需要修改光标如下,或者正确修改查询。

我们必须在继续之前检查OPEN cursor的状态,否则FETCH将再次失败,结果可能无法预测。因此,请在每次sqlca.sqlcode来电后查看EXEC SQL

此外,我们需要处理NULL,如果未使用Indicator variables,我们可以在查询中使用NVL()

   exec sql declare dbGuest cursor for
        Select G.Guest_ID,
               G.Last_Name,
               G.First_Name,
               C.Item_ID,
               C.Item_Name,
               C.Quantity,
               C.Unit_Price,
               C.Trans_Date,
               H.Hotel_Name,
               H.Hotel_ID,
               SI.Taxable
        From Hotel H, Charge C, Stay S, Guest G, Sales_Item SI
        Where C.Stay_ID=S.Stay_ID 
          And H.Hotel_ID=S.Hotel_ID 
          And G.Guest_ID=S.Guest_ID
          And SI.Item_ID=C.Item_ID;

非聚合列只能用于聚合函数。因此,请删除分组或将MAX()添加到其他列。

将以下内容添加到声明中

int temp_sales_id = -999;
int first_iter = 1;
int total_nQuantity = 0;
float total_nUnitPrice = 0.0;

然后,

exec sql open dbGuest;

    /* Lets check the status of the OPEN statement before proceeding , else exceptions would be suppressed */
    if(sqlca.sqlcode !=0)  // If anything went wrong or we read past eof, stop the loop
     {
        printf("Error while opening Cursor <%d><%s>\n",sqlca.sqlcode,sqlca.sqlerrm.sqlerrmc);
        break;
     }

for(;;)
    {
        //exec sql fetch dbGuest into :nGuest_ID, :sLastName, :sFirstName, :sHotelName, :sCheckInDate, :nDays, :sRoom;
        exec sql fetch dbGuest into :sLastName, :sFirstName, :nItemID, :sItemName, :nQuantity, :nUnitPrice, :sTransDate, :sHotelName, :nHotelID;

        /* Check for No DATA FOUND */
        if(sqlca.sqlcode == 100 || sqlca.sqlcode == 1403)  // If anything went wrong or we read past eof, stop the loop
        {
            printf("CURSOR is empty after all fetch");
            break;
        }
        /* Check for other errors */
        else if(sqlca.sqlcode != 0)
        {
             printf("Error while fetching from Cursor <%d><%s>\n",sqlca.sqlcode,sqlca.sqlerrm.sqlerrmc);
             break;
        }

        if(first_iter) {
           printf("%s %s %s %s %d\n","Charge Summary for:", sFirstName.arr, sLastName.arr, " Guest_ID:", nGuest_ID);
           first_iter = 0;
        }
        // Do the crazy stuff to end the C-Strings
        sLastName.arr[sLastName.len] = 0;
        sFirstName.arr[sFirstName.len] = 0;
        sItemName.arr[sItemName.len] = 0;
        sTransDate.arr[sTransDate.len] = 0;
        sHotelName.arr[sHotelName.len] = 0;

        if(temp_sales_id == -999 || temp_sales_id != nItemID)
        {
          /* First Item or Sales Item has Changed (next sales id)*/
          temp_sales_id = nItemID;
          // Print out the information for this guest
          printf("%s %d %s %s \n", "Sales_Item: ", nItemID, " - ", sItemName.arr);

          printf("%d %s %s %d %d \n", nHotelID, " ", sHotelName.arr, " ",sTransDate.arr, " ", nQuantity, " ", nUnitPrice);

          total_nQuantity += nQuantity;
          total_nUnitPrice += nUnitPrice;
        }
        if (temp_sales_id != nItemID) {
            /* Printing total for Current Sale id */
            /* If you want to Sum all the sale id together take this finally */
            printf("Total Quantity <%d> Total Extended Price <%g>\n",total_nQuantity,total_nUnitPrice);
            total_nUnitPrice = 0;
            total_nQuantity = 0;
        }

        if(temp_sales_id == -999 || temp_sales_id == nItemID) {
          printf("%d %s %s %d %d \n", nHotelID, " ", sHotelName.arr, " ",sTransDate.arr, " ", nQuantity, " ", nUnitPrice);
        }
    }
    // close the cursor and end the program
    exec sql close dbGuest ;

答案 1 :(得分:0)

如果您有垃圾值,则可能意味着所获取的数据在数据库中是NULL值。您将需要使用指示器数组来查看什么是NULL。另一种方法是将DB列设置为NOT NULL,以确保您将数据提取到变量中。

我想要检查的第一件事是在SQLPLUS中执行SQL查询,看看你得到了什么结果。如果结果中没有NULL值,则获取的变量不应包含垃圾。

如果结果中有NULL值,请在ProC代码中引入指标数组来处理这种情况。