Oracle Pro*C с использованием курсора во вложенном цикле

Я изучаю 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 (поскольку она выполняется в удаленной базе данных).

Прошел через это, но не помогло:

Странное поведение с вложенными курсорами оракула

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

Ошибка обновления таблицы Oracle Pro*C с курсором

Мне сказали, что переменные VARCHAR должны быть объявлены по-другому, но другие способы, похоже, вызывают ошибки.


person stackuser    schedule 20.02.2014    source источник


Ответы (2)


Еще до извлечения значений в sFirstName вы их печатаете. Для начала, поскольку вы его не инициализировали, он печатает значение мусор. Кроме того, если вы чувствуете, что выборка курсора прервана, прежде чем прерывать цикл, используйте команду sqlca sqlerrm для вывода сообщения об ошибке. Как 2_

Тогда ваш вызов OPEN CURSOR завершится ошибкой, так как в запросе есть синтаксические ошибки. Итак, вам нужно либо изменить курсор, как показано ниже, либо правильно изменить запрос.

Мы должны проверить состояние OPEN cursor, прежде чем продолжить, иначе FETCH снова не удастся, и результаты могут быть непредсказуемыми. Поэтому, пожалуйста, проверяйте sqlca.sqlcode после каждого EXEC SQL звонка.

Также нам нужно обрабатывать NULL, мы можем использовать 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 ;
person Maheswaran Ravisankar    schedule 20.02.2014
comment
Просто интересно, почему вы использовали агрегат MAX()? Кажется, что он будет возвращать максимум на каждой итерации. Я просто пытаюсь распечатать каждую запись в базе данных. Я попробовал ваше решение, кажется, выдает эту ошибку Charge Summary for: ▒ Guest_ID: 1 Error while opening Cursor <(null)> . Не уверен, что есть какая-то другая часть, которую нужно добавить к курсору, но я действительно не понимаю, необходимы ли здесь переменные индикатора или может ли это работать без этого. - person stackuser; 20.02.2014
comment
если вам нужна каждая запись, просто удалите GROUP BY в вашей версии запроса - person Maheswaran Ravisankar; 20.02.2014
comment
И извините, это sqlca.sqlerrm.sqlerrmc для строки сообщения об ошибке... добавьте также sqlca.sqlcode в printf. Теперь, когда сам оператор OPEN терпит неудачу, и это поможет вам понять, что Pro*C подавляет ошибки до тех пор, пока мы не обработаем их явным образом. - person Maheswaran Ravisankar; 20.02.2014
comment
Хорошо, потребовалась дополнительная отладка, но она по крайней мере скомпилировалась. Как и в моем посте, формат, который я пытаюсь получить, - это оплата гостем. Я думаю, что это похоже на еще один вложенный цикл for, но я не уверен, нужен ли еще один курсор и где будут связаны окончания C-String (в каком вложении циклов)? Или это будет сделано без другого цикла? - person stackuser; 20.02.2014
comment
добавьте AND G.Guest_ID = :nGuest_ID к вашему запросу в курсоре. Он должен работать. Для каждой итерации внешнего цикла значение nGuest_ID изменяется и извлекается записи соответственно. - person Maheswaran Ravisankar; 20.02.2014
comment
Извините, вы имеете в виду все товары вместе? Уже этот запрос выводит все товары для этого гостевого идентификатора. - person Maheswaran Ravisankar; 20.02.2014
comment
Это, по крайней мере, приносит результаты только тому гостю, которого опрашивают. Но, как и в моем посте, я пытаюсь собрать все товары для продажи вместе (формат, который я использую, указан в моем посте). Теперь это возможно только с помощью другого вложенного цикла или его нужно снова вводить из sql перед циклом или...? +1 за помощь. - person stackuser; 20.02.2014
comment
давайте продолжим это обсуждение в чате - person Maheswaran Ravisankar; 20.02.2014

Если у вас есть мусорные значения, это может означать, что извлеченные данные имеют значение NULL в базе данных. Вам нужно будет использовать массив индикаторов, чтобы увидеть, что такое NULL. Альтернативой является установка для столбца DB значения NOT NULL, чтобы гарантировать, что у вас будут данные для выборки в переменные.

Я думаю, первое, что нужно проверить, это выполнить SQL-запрос в SQLPLUS и посмотреть, какие результаты вы получите. Если в результате нет значений NULL, то ваши извлеченные переменные не должны содержать мусора.

Если в результате есть значения NULL, попробуйте ввести массив индикаторов в свой код ProC, чтобы справиться с этой ситуацией.

person anonymous    schedule 20.02.2014