【问题标题】:Oracle Pro*C -- using a cursor in nested loopOracle Pro*C——在嵌套循环中使用游标
【发布时间】:2014-02-20 00:21:38
【问题描述】:

我正在自学 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 程序会在调试器中运行,但这是 ProC,我对整个 Oracle ProC 调试的东西有点迷失(因为它在远程数据库上运行)。 p>

经历了这些但没有帮助:

Strange behaviours with oracle nested cursors

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

Oracle Pro*C updating table with cursor failed

有人告诉我应该以不同的方式声明 VARCHAR 变量,但其他方式似乎会引发错误。

【问题讨论】:

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


    【解决方案1】:

    即使在将值提取到sFirstName 之前,您也可以打印它们。首先,由于您没有初始化它,它会打印 garbage 值。此外,如果您觉得游标获取被中断,在 *break* 循环之前,请使用 sqlca 的 sqlerrm 打印错误消息。赞sqlca.sqlerrm.sqlerrmc

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

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

    另外,我们需要处理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 ;
    

    【讨论】:

    • 只是想知道您为什么使用 MAX() 聚合?这似乎会在每次迭代时返回最大值。我只是想打印数据库中的每条记录。我确实尝试了您的解决方案,似乎抛出了这个错误Charge Summary for: ▒ Guest_ID: 1 Error while opening Cursor &lt;(null)&gt; 。不确定是否需要将其他部分添加到光标,但我真的不知道指标变量是否在这里必不可少,或者如果没有这可以工作。
    • 如果您想要每条记录,只需在您的查询版本中删除GROUP BY
    • 抱歉,错误消息字符串是sqlca.sqlerrm.sqlerrmc...在printf 中也添加sqlca.sqlcode。现在 OPEN 语句本身失败了,这有助于您理解这一点,Pro*C 会抑制错误,直到我们明确处理它们
    • 好的,进行了一些额外的调试,但至少可以编译。正如在我的帖子中一样,我想要获得的格式是客人收取的所有费用。我认为这就像另一个嵌套的 for 循环,但我不确定是否需要另一个游标以及 C-String 结尾将被捆绑在哪里(循环的嵌套)?或者这会在没有另一个循环的情况下完成吗?
    • AND G.Guest_ID = :nGuest_ID 添加到游标中的查询中。它应该工作。对于每个 循环迭代,nGuest_ID 的值会发生变化并相应地获取记录。
    【解决方案2】:

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

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

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

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2012-06-15
      • 1970-01-01
      • 1970-01-01
      • 2015-04-25
      • 1970-01-01
      • 2023-03-25
      • 2023-04-01
      • 1970-01-01
      相关资源
      最近更新 更多