|
Solutions for Section 8.1
Solutions for Section 8.2
Solutions for Section 8.4
Solutions for Section 8.5
Solutions for Section 8.6
Solutions for Section 8.7
void closestMatchPC() { EXEC SQL BEGIN DECLARE SECTION; char manf[30], SQLSTATE[6]; int targetPrice, /* holds price given by user */ speedOfClosest, modelOfClosest, priceOfClosest, /* for closest price found so far */ tempSpeed, tempModel, tempPrice; /* for tuple just read from PC */ EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE pcCursor CURSOR FOR SELECT model, price, speed FROM PC; EXEC SQL OPEN pcCursor; /* ask user for target price and read the answer into variable targetPrice */ /* Initially, the first PC is the closest to the target price. If PC is empty, we cannot answer the question, and so abort. */ EXEC SQL FETCH FROM pcCursor INTO :modelOfClosest, :priceOfClosest, :speedOfClosest; if(NO_MORE_TUPLES) /* print message and exit */ ; while(1) { EXEC SQL FETCH pcCursor INTO :tempModel, :tempPrice, :tempSpeed; if(NO_MORE_TUPLES) break; if( /* tempPrice closer to targetPrice than is priceOfClosest */) { modelOfClosest = tempModel; priceOfClosest = tempPrice; speedOfClosest = tempSpeed; } } /* Now, modelOfClosest is the model whose price is closest to target. We must get its manufacturer with a single-row select */ EXEC SQL SELECT maker INTO :manf FROM Product WHERE model = :modelOfClosest; printf("manf = %s, model = %d, speed = %d\n", :manf, :modelOfClosest, :speedOfClosest); EXEC SQL CLOSE CURSOR pcCursor; }
Again, the macro NO_MORE_TUPLES is used to test for the end of the relation.
void lowerPrices() { EXEC SQL BEGIN DECLARE SECTION; char SQLSTATE[6]; EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE pcCursor INSENSITIVE CURSOR FOR PC; EXEC SQL OPEN pcCursor; while(1) { EXEC SQL FETCH FROM pcCursor; if(NO_MORE_TUPLES) break; EXEC SQL UPDATE PC SET price = price - 100 WHERE CURRENT OF execCursor; } EXEC SQL CLOSE pcCursor; }
void twoMoreExpensive() { EXEC SQL BEGIN DECLARE SECTION; char SQLSTATE[6], cd[5], cd1[5]; int model, model1, speed, speed1, ram, ram1, price, price1; float hd, hd1; /* we use the variables cd1, model1, etc, to read the tuple 2 positions later, to see if the speed has not changed (in which case there are at least two PC's with the same speed and at least as high a price) */ EXEC SQL END DECLARE SECTION; EXEC SQL DECLARE pcCursor SCROLL CURSOR FOR SELECT * FROM PC ORDER BY speed, price; EXEC SQL OPEN pcCursor; while(1) { EXEC SQL FETCH NEXT FROM pcCursor INTO :model, :speed, :ram, :hd, :cd, :price; if(NO_MORE_TUPLES) break; EXEC SQL FETCH RELATIVE +2 FROM pcCursor INTO :model1, :speed1, :ram1, :hd1, :cd1, :price1; if(NO_MORE_TUPLES) break; if(speed1 == speed) /* print the tuple :model, :speed, etc. */ ; EXEC SQL FETCH RELATIVE -2 FROM pcCursor INTO :model1, :speed1, :ram1, :hd1, :cd1, :price1; /* line above just to reset the cursor to where it was */ } EXEC SQL CLOSE pcCursor;
CREATE FUNCTION PresNetWorth(IN studioName CHAR[15]) DECLARE presNetWorth INT; BEGIN SELECT netWorth INTO presNetWorth FROM Studio, MovieExec WHERE Studio.name = studioName AND presC# = cert#; RETURN(presNetWorth); END;
CREATE FUNCTION status(IN person, IN addr) DECLARE isStar INT; DECLARE isExec INT; BEGIN SELECT COUNT(*) INTO isStar FROM MovieStar WHERE MovieStar.name = person AND MovieStar.address = addr; SELECT COUNT(*) INTO isExec FROM MovieExec WHERE MovieExec.name = person AND MovieExec.address = addr; IF isStar + isExec = 0 THEN RETURN(4) ELSE RETURN(isStar + 2*isExec) END IF; END;
CREATE PROCEDURE twoLongest( IN studio CHAR(15), OUT longest VARCHAR(255), OUT second VARCHAR(255) ) DECLARE longestLg INT; DECLARE secondLg INT; DECLARE t VARCHAR(255); DECLARE l INT; DECLARE Not_Found CONDITION FOR SQLSTATE = '02000'; DECLARE MovieCursor CURSOR FOR SELECT title, length FROM Movie WHERE studioName = studio; BEGIN SET longest = NULL; SET second = NULL; SET longestLg = -1; SET secondLg = -1; OPEN MovieCursor; mainLoop: LOOP FETCH MovieCursor INTO t, l; IF Not_Found THEN LEAVE mainLoop END IF; IF l > longestLg THEN SET secondLg = longestLg; SET second = longest; SET longestLg = l; SET longest = t; ELSIF l > secongLg THEN SET secondLg = l; SET second = t; END IF; END LOOP; CLOSE MovieCursor; END;
In explanation, as we run through movies, we need to remember not only the titles of the two longest movies seen so far, but their lengths. That way, when we see a new title and length, fetched into the pair of local variables (t, l), we can compare the length l with the two longest so far. The body of the loop first asks if l is longer than the longest; if so, the old longest becomes second, and the current movie becomes longest. If the current movie is not longest, then we next ask if it is longer than the second longest, and we replace the latter, if so.
Since we need to take the first model as ``closest'' regardless of its price, we use -1 as a value of the square of the price differences to indicate that there is no closest model yet selected.
CREATE FUNCTION closestMatchPC(IN targetPrice INT) DECLARE closestModel INT; DECLARE diffSq INT; DECLARE currSq INT; DECLARE m INT; DECLARE p INT; DECLARE Not_Found CONDITION FOR SQLSTATE '02000'; DECLARE PCCursor CURSOR FOR SELECT model, price FROM PC; BEGIN SET closestModel = -1; SET diffSq = -1; OPEN PCCursor; mainLoop: LOOP FETCH PCCursor INTO m, p; IF Not_Found THEN LEAVE mainLoop END IF; SET currSq = (p - targetPrice)*(p - targetPrice); IF diffSq = -1 OR diffSq > currSq THEN BEGIN SET closestModel = m; SET diffSq = currSq; END IF; END LOOP; CLOSE PDCursor; RETURN(closestModel); END;
#include sqlcli.h int closestMatchPC(int targetPrice) { int diffSq, currSq, closestModel; SQLHENV myEnv; SQLHDBC myCon; SQLHSTMT pcStat; SQLINTEGER m, p, mInfo, pInfo; diffSq = closestModel = -1; SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &myEnv); SQLAllocHandle(SQL_HANDLE_DBC, myEnv, &myCon); SQLAllocHandle(SQL_HANDLE_STMT, myCon, &pcStat); SQLPrepare(pcStat, "SELECT model, price FROM PC", SQL_NTS); SQLExecute(pcStat); SQLBindCol(pcStat, 1, SQL_INTEGER, &m, size(m), &mInfo); SQLBindCol(pcStat, 2, SQL_INTEGER, &p, size(p), &pInfo); while(SQLFetch(pcStat) != SQL_NO_DATA) { currSq = (p - targetPrice)*(p - targetPrice); if(diffSq == -1 || diffSq > currSq) { diffSq = currSq; closestModel = m; } } return(m); }
#include sqlcli.h void lowerPrices() { SQLHENV myEnv; SQLHDBC myCon; SQLHSTMT pcStat; SQLAllocHandle(SQL_HANDLE_ENV, SQL_NULL_HANDLE, &myEnv); SQLAllocHandle(SQL_HANDLE_DBC, myEnv, &myCon); SQLAllocHandle(SQL_HANDLE_STMT, myCon, &pcStat); SQLPrepare(pcStat, "UPDATE PC SET price = price - 100", SQL_NTS); SQLExecute(pcStat); }Solutions for Section 8.5
Exercise 8.5.1(a)
int closestMatchPC(int targetPrice) { Connection myCon = DriverManager.getConnection(appropriate arguments); PreparedStatement PCStat = myCon.createStatement( "SELECT model, price FROM PC" ); ResultSet PCresult = PCStat.executeQuery(); int diffSq = -1; int closestmodel = -1; while(PCresult.next()) { int m = PCresult.getInt(1); int p = PCresult.getInt(2); int currSq = (p - targetPrice)*(p - targetPrice); if(diffSq == -1 || diffSq > currSq) { diffSq = currSq; closestModel = m; } } return(m); }Exercise 8.5.1(f)
void lowerPrices() { Connection myCon = DriverManager.getConnection(appropriate arguments); Statement PCStat = myCon.createStatement(); PCStat.executeUpdate("UPDATE PC SET price = price - 100"); }Solutions for Section 8.6
Exercise 8.6.1(b)
EXEC SQL BEGIN DECLARE SECTION; int modelNo; EXEC SQL END DECLARE SECTION; void deleteModel() { /* get the model number from the user and store it in modelNo */ EXEC SQL SET TRANSACTION ISOLATION LEVEL SERIALIZABLE; EXEC SQL DELETE FROM Product WHERE model = :modelNo; EXEC SQL DELETE FROM PC WHERE model = :modelNo; EXEC SQL COMMIT; }We think that it is wise to set the isolation level to serializable, even though no tuples are written by this transaction. The potential problem is that another transaction could read one of the two tuples for a given model, either from Product or PC, while this transaction was deleting the other tuple. On the other hand, that transaction, if it really needed to be sure that either both tuples or none were present, could set its own isolation level to serializable. Since there is no harm that can come to our transaction if one and not the other tuple is present, we think it is also acceptable to replace the SET-TRANSACTION statement by:EXEC SQL SET TRANSACTION READ WRITE ISOLATION LEVEL READ UNCOMMITTED;Exercise 8.6.4
It makes no sense to run T serializably. Once T starts, it would never see any changes to the database. In practice, a long-running transaction like T would have to be aborted by the system periodically. That would have the fortunate effect of allowing T to restart and see any new PC's that were added to the database.For T's purposes, it is sufficent if it runs with isolation level repeatable-read. That will guarantee that whenever it sees a tuple, it will continue to see that tuple. However, it will also be allowed to see new tuples as they are added to the database. The potential problem with repeatable-read is that the transaction of Exercise 8.6.1(c), which lowers the price of a PC, might not be able to run while T is running, because it would change the tuple with the old price, which T is required to see. On the other hand, systems that support ``multiversion'' concurrency control would allow T to see the old tuple, while other transactions can see the new one.
T can also run at isolation level read-committed, and this choice is probably best. It will eventually see any new or updated tuple that the transactions of Exercise 8.6.1(c) or (d) produce, while not putting any constraints on these transactions.
Isolation level read-uncommitted for T is a possibility, although it would have the consequence that a new PC could be inserted into the database very briefly, while the person inserting the PC has a change of heart and aborts the change at the terminal, while T has already seen the PC's information.
Solutions for Section 8.7
Exercise 8.7.1(c)
We need:The privilege INSERT(name) on Studio will suffice for (3).
- SELECT on Movie
- SELECT on Studio
- INSERT on Studio
Exercise 8.7.2
Here are the diagrams after Step 4; Step 5; Step 6.