| Sybase | Oracle | Comments |
|---|---|---|
-- |
-- |
Two dashes to indicate comments are identical between Oracle and Sybase. |
/* and */ |
/* and */ |
C style comments are allowed in both
The translator moves any comments before your CREATE PROCEDURE statement to just after the procedure heading and before its parameters (if any). Oracle accepts comments prior to your CREATE PROCEDURE statement, but doesn't include them in the source. |
@VAR |
MY_VAR |
Variables names may not begin with an at-sign (@). These are removed.
This is a real problem if your variables (sans the at-sign) are the same as column
names. It is usually a problem in your where clause when you specify something like this:where GROUPTYPE = @GROUPTYPEwhich becomes where GROUPTYPE = GROUPTYPEcausing Oracle to compare the column GROUPTYPE to the column GROUPTYPE. Although this compiles okay, it produces incorrect results and is very difficult to debug because it "looks" like it should work. The translator provides a default template that maps @VAR to MY_VAR, but you can provide your own on the command line. Your template requires exactly one percent sign (%) which is where your variable name is placed. The default template is MY_%You may wish the translator to leave your variables names untouched because their spelling does not also collide with the spelling of object names. This is possible with the following template: %But your procedure will not compile if any input parameters are used as the target of an assignment statement. You can also force "qualification" of all your variable names with the following: %.%This leaves your variables untouched (except the at-sign is stripped, of course) and prepends the name of your procedure to the variable name. If you procedure is called sp_test, then %.% applied to @VAR translates to sp_test.VAR. Note the translator does not qualify variables used as the target of an assignment statement because this is not needed. Variable names can contain special characters (like @) if they are quoted. The translator automatically determines if quotes are needed or you can quote the template yourself; for example, @%translates VAR to "@VAR." The quotes look really ugly, but the option is yours if you want it. |
@GROUP |
"GROUP" |
Follows the rules above for @VAR, but in this case GROUP is a reserved word. Reserved
words are quoted in the parameter declaration, when assigned to locally mapped declarations, and when
used as the parameter name in procedure invocations. If you disable the template mapping (with % or %.%),
reserved words used as local variables will cause syntax errors because they are not quoted.
The translator will preserve case-sensitivity for any reserved words you might use. That is because quoted variable names are case-sensitive. |
@VAR = NULL or @VAR != NULL |
VAR IS NULL or VAR IS NOT NULL |
NULL never equals NULL. NULL IS NULL or NULL IS NOT NULL.
Suppose VAR is NULL. The expression VAR != 'STRING'will return false in Oracle but true in Sybase. It doesn't matter if you are testing for inequality or equality because the expression VAR = 'STRING'also returns false because NULL is NULL or NULL is not NULL (not because VAR doesn't equal 'STRING'). The translator will replace = with IS and != with IS NOT, but does not handle expressions like VAR != 'STRING.' |
VAR like '[specifier]' |
VAR like '[specifier]' |
Be careful because Oracle's LIKE Operator does not recognize Sybase specifiers (like [a-z] or [^z-a]). The translator
will attempt to warn you if you invoke this kind of expression, but is unable to do so if your pattern was built at
run-time. In both cases you can use PATINDEX() instead of LIKE.
HTTP.PATINDEX('[specifier]',VAR) > 0works exactly the same as LIKE. |
statement |
statement; |
All statements are terminated by a semicolon. In Oracle you may use the keyword NULL to denote a null statement. There is no such construct in Sybase. |
DECLARE |
DECLARE |
Your declarations in Sybase are global to all blocks in your stored procedure.
Oracle allows you to declare variables that are local to the block in which they were declared.
Declarations other than your cursor declarations (which are translated to open statements) are moved to the outer block. In your declaration comments of the form (--) are preserved. Comments of the form /* and */ are lost. |
IF expression
statement |
IF expression THEN
statement;
END IF; |
The translator does not remove your BEGIN and END pairs. You may end up with something likeIF expression THEN |
ELSE |
ELSE |
The translator preserves your ELSE clause and creates a matching END IF for each IF. You may end up with something likeEND IF;when in reality the mapping of ELSE to ELSIF would avoid this. |
WHILE expression
statement |
WHILE expression LOOP
statement;
END LOOP; |
Like IF statements, BEGIN and END pairs are not required. |
BREAK |
EXIT; |
|
GOTO label ... label: |
GOTO label; ... <<label>> |
Looks odd to me too.
Be sure that your label points to the beginning of a statement. If your label occurs as the line of your procedure source, you will get a syntax error. You can add NULL or RETURN if this is the case. |
CONTINUE |
GOTO CONTINUE_1; |
There is no CONTINUE statement in PL/SQL. A GOTO statement is built instead which also includes a PL/SQL <<label>> which is inserted at the beginning of your while loop. The actual label name is determined automatically by the translator and (more often than not) includes the occurrence number of your while loop. Sometimes this is not the case because the translator also creates while loops where none existed before. |
'STRING' |
'STRING' |
Oracle likes single-quoted strings.
PL/SQL is case-insensitive, but the contents of strings are case-sensitive. 'STRING' does not equal 'String'. The translator does not translate SQL embedded within strings. If you are building dynamic SQL (perhaps because you are using FILTER=CGI), you will have to translate this yourself. |
"STRING" |
'STRING' |
String must be single-quoted, not double quoted. The translator will correctly convert all strings to single quoted strings. Sybernet strings (such as in URL's) can be either single or double quoted. |
DECLARE variable CURSOR FOR |
OPEN variable FOR |
variable will be declared as a generic cursor type in the outer block. Your cursor declaration will actually be the open statement for this cursor. |
OPEN cursor |
Normally, you are opening a cursor because you have declared a cursor.
The translator will declare a generic cursor in the outer-block of your program and
open the cursor where the cursor is declared. Your OPEN statement is not needed and
is removed.
One problem is that you declare a cursor that depends on the value of a variable that is not set until after the declaration and before your open statement. In this case, you must move the assignment of that variable before your cursor declaration because that is when the variable's value is determined. |
|
DEALLOCATE cursor |
Cursors are not deallocated so this statement is removed. | |
RETURN number |
RETURN; |
Unlike Sybase, procedures do not return values. If you want your procedure to return a value, then you can create a FUNCTION instead of a PROCEDURE. Oracle functions can return any datatype, not just integer values. |
BEGIN TRANSACTION [transaction_name] |
SAVEPOINT [transaction_name]; | In Sybase the transaction_name is optional. In Oracle it is required. The translator will "invent" transaction names if you omit them, but since the corresponding ROLLBACK can occur almost anywhere in your source, it is difficult (if not impossible) to match the two statements. Your procedure may compile successfully, but you are advised to make sure the transaction names agree with your program. If you omit the transaction name, the translator will warn you exactly once that this has happened. |
ROLLBACK TRANSACTION [transaction_name] |
ROLLBACK TO SAVEPOINT transaction_name; | See BEGIN TRANSACTION. |
COMMIT TRANSACTION |
COMMIT WORK; |
|
EXEC PROCNAME
@NAME = @NAME
, @VALUE = @VALUE output |
PROCNAME
(
NAME => MY_NAME
, VALUE => MY_VALUE
) ; |
Output parameters are determined by the declaration of the parameter, not by passing OUTPUT to the procedure.
The translator removes OUTPUT.
Notice that the translator does not map parameter names passed to another stored procedure, but does map your parameter values if they are Sybase variable names. Parameter names are (on the whole) preserved with the exception of reserved words (like SIZE) which are mapped to something else (see below) or left untouched (in which case your stored procedure will not compile). The translator also doesn't touch output parameters. The theory here is that this name is going to be referenced by another stored procedure, and if the name were mapped, it would be impossible to return the local (mapped) declaration to that output parameter. Obviously, output parameters should not be used in a where clause with a table having a column of that name. Like Sybase you can declare your parameters with default values. Unlike Sybase you can not pass parameters to a stored procedure that do not exist. |
EXEC @RSLT = PROCNAME |
RSLT:=PROCNAME; |
Similar to the example above, but you'll notice that the return result is simply the expression part of an assignment statement. |
EXEC @RSLT = @PROCNAME |
EXECUTE IMMEDIATE 'BEGIN :1=' || PROCNAME || ';END;' USING OUTPUT RSLT; |
The difference here is that PROCNAME is actually a variable, and its value is not known at compile-time. For this case, the translator passes the appropriate anonymous block to EXECUTE IMMEDIATE. If you are passing parameters, these are handled as well. |
SELECT @x = expression |
x:=expression; |
The translator attempts to use Oracle's string concatenation operator (||) the best it can.
Normally, it determines this from the target of the assignment.
If the target is a string, plus-signs (+) are translated to the concat operator (||).
For numeric values, the plus-sign is preserved.
While parsing string or arithmetic expressions, the translator can keep up as long as it knows the type of variable or literal. If the preceding or succeeding operand is a literal string or number, plus-signs are converted correctly. This also applies to any parameters or local declarations when the translator can determine the type of this expression. If your expression consists of column names in a table, the translator will look its type up in Oracle. In this case it is important that the table exists. If the table doesn't exist and you started editing the Oracle source yourself, you run the risk of missing something that (although compiles) is actually incorrect. |
SELECT "<html>" , "<body>" |
HTTP.WRITE
(
'<html>'
, '<body>'
) ; |
Oracle does not allow a stored procedure to return more than one result set.
This simple select statement in Sybase constitutes a result set.
WRITE is a stored procedure that allows you to send your results to Sybernet.
WRITE() can accept up to 100 parameters (an arbitrary number I thought sounded good). Each parameter to WRITE() may be as large as 32,767 bytes, but the total number of bytes of all parameters may not exceed 32,767 bytes. WRITELN() is similar to WRITE() except that it appends a carriage-return to the end of each parameter. This serves no other purpose than making your HTML output look good when you do a view source. Be careful if you use WRITELN() yourself because there are tames when carriage returns are undesirable, like in URL's. The translator always uses WRITE() because it is faster to use than WRITELN(). |
|
|
WRITELN
(
'function check(month)'
, '{'
, 'if (parseInt(month,Base10) == 4'
, '|| parseInt(month,Base10) == 6'
, '...'
, '}'
) ;
WRITE() will accept strings as long as 32,767 bytes. Your strings are now preserved. |
SELECT @variable = column FROM table |
BEGIN
SQL_ERROR:=0; SQL_ROWCOUNT:=0;
SELECT column
INTO variable
FROM table;
EXCEPTION
WHEN NO_DATA_FOUND THEN SQL_ROWCOUNT:=0;
WHEN TOO_MANY_ROWS THEN SQL_ROWCOUNT:=2;
WHEN OTHERS THEN SQL_ERROR:=SQLCODE;
HTTP.WRITE('<h3>',SQLERRM,'</h3>');
END;
|
In Oracle, such a statement that returns no rows or more than one row is considered an error.
Errors are raised by Oracle and must be trapped if you want you stored procedure to continue.
The translator executes this SQL statement in its own block and ignores the condition when exactly one row is not returned. Where you now interrogate @@error or @@rowcount, the translator will translate these to SQL_ERROR and SQL_ROWCOUNT. You can use them exactly the way you are used to. Note that if this statement returns more than one row, SQL_ROWCOUNT (@@rowcount) is set to 2. If you absolutely, positively have to know the number of collisions, then you will have to code a count(*) on your own. If you are only interested in SQL_ROWCOUNT > 1, then all will be well. |
select '<tr>' , '<td>' , NAME , '</td>' , COLOR , '</td>' , '</tr>' from HTTP_COLORS |
BEGIN
DECLARE CURSOR cur IS
SELECT NAME
, COLOR
from HTTP_COLORS;
col_1 varchar(255);
col_2 varchar(255);
BEGIN
OPEN cur;
FETCH cur INTO col_1, col_2;
While (cur%FOUND) LOOP
BEGIN
HTTP.WRITE
(
'<tr>'
, '<td>'
, col_1
, '</td>'
, col_2
, '</td>'
, '</tr>'
) ;
FETCH cur INTO col_1, col_2;
END;
END LOOP;
CLOSE cur;
END;
END; |
Oracle does not allow a stored procedure to return more than one result set.
Instead it is translated to open and fetch a cursor which accomplishes the same thing.
Sometimes you use this construct because you know exactly one row will be returned. The translator doesn't know this and always creates a cursor declaration. In this case, it is cleaner to issue a SELECT ... INTO to retrieve that single row into local variables. You would then pass those local variables to WRITE(). You definitely don't want to use this as an example of how you should create cursors in Oracle. The translator does this because it is the only way to map your select statement in-line. You will notice that the mapping of Sybase cursors looks very similar to Oracle cursors. Oracle even allows you to fetch all your columns into a "record" instead of individual, local variables. This is extremely useful because there's less for you type (you fetch into the record declaration) and the attributes of the table are retrieved automatically. If you've ever built a drop-down list dynamically from a table and marked the appropriate SELECTED option, you probably did this with 3 select statements:
select '<option>' + name from table where name < @name select '<option selected>' + @name select '<option>' + name from table where name > @nameThe translator has no idea this is what you are doing and will create two cursor declarations. If you're going to end up with a cursor anyway, why not just test for SELECTED inside of the cursor loop? Notice that the variables col_1 and col_2 are locally declared as varchar(255). This will work for all column types except TEXT and IMAGE columns and is why the translator uses them, but this is not the most efficient use of Oracle as I'm sure you can imagine. col_1 and col2 should really be declared thusly, but this would fall apart if col_1 was actually the result of a string expression, so the translator takes the easy way out.col_1 CRON.NAME%TYPE; col_2 CRON.COLOR%TYPE; |
EXISTS (SELECT ...) |
HTTP.SYBEXISTS('SELECT ...') |
Oracle has no such thing as an EXISTS() function. Your SELECT statement is passed as a string to
SYBEXISTS(). This function uses native dynamic SQL to evalute your expression and means it's
probably on the slow side if your procedure calls it too many times. You can easilty rewrite
this code inline to speed things along.
The translator calls SYBEXISTS because it works and allows complex expressions (perhaps several calls to EXISTS) to be correctly evaluated.DECLARE SQL_EXISTS int; BEGIN SELECT 1 INTO SQL_EXISTS FROM DUAL WHERE EXISTS(SELECT ...); EXCEPTION WHEN OTHERS THEN SQL_EXISTS:=0; END; IF (SQL_EXISTS=1) THEN |
(SELECT ...) |
HTTP.SYBSELECT('SELECT ...') |
Oracle does not allow SELECT statements within an expression. Like EXISTS above,
your select statements are passed to a function that uses native dynamic SQL to evaluate your
expression. It too is probably on the slow side if your procedure calls this too many times.
SYBSELECT() returns a varchar. The translator relies on Oracle's ability to implicitly convert its result to a number or date if needed when the final result is evaluted; for example,
Correctly evaluates to 5 (believe it or not). In the case of DATE variables, SYBSELECT returns
the default date format (currently DD-MON-YY) which means you will lose the hour, minute and second.
This is important if you are comparing dates where those are needed.
You can easily rewrite this code inline to speed things along: Passing your SELECT statement as a string also means the translator must make sure local variables are evaluated when the string is built. If a variable is NULL at run-time, you will surely get a syntax error because the string is incomplete. For this case, you may want to rewrite this code inline.DECLARE SQL_COUNT INT; BEGIN SELECT COUNT(*) INTO SQL_COUNT FROM TBL; EXCEPTION WHEN OTHERS THEN SQL_COUNT:=0; END; if (SQL_COUNT > 3) THEN |
CREATE PROCEDURE P
(
@NAME varchar(30) = NULL
, @VALUE varchar(20) = NULL
, @TIMESTAMP smalldatetime = null
, @DEFAULT varchar(255) = NULL OUT
)
AS
SELECT @NAME = "MYSELF"
SELECT @DEFAULT = "BLUE";
RETURN 0
|
CREATE OR REPLACE PROCEDURE P
(
NAME IN VARCHAR2 := NULL
, VALUE IN VARCHAR2 := NULL
, TIMESTAMP IN VARCHAR2 := NULL
, PREFERENCE OUT VARCHAR2
)
AS
MY_NAME varchar(30) := NAME;
MY_VALUE varchar(30) := VALUE;
MY_TIMESTAMP DATE := HTTP.DATENAME(TIMESTAMP);
BEGIN
MY_NAME:='MYSELF';
PREFERENCE:='BLUE';
RETURN;
END;
|
IN and OUT determine if this is an input or output parameter.
Compare this with the execute statement above where the OUTPUT option was deleted because
this is determined by declaration of that parameter.
Input parameters may not be used as the target of an assignment statement. This is probably something you do all the time, but this is not allowed. The translator maps all parameters with the default template (unless you have suppressed this option) so that parameters can not only be used as the assignment of an expression, but also so that their name does not collide with a column name when both are used at the same time. Output parameters may not specify default values. If you do so, the translator will omit them and give you a warning this has occurred. You might have to rethink this. I've seen one case (and it wasn't I) where the programmer was using an output parameter when they really meant an input parameter. Sybase is forgiving. Oracle is unforgiving. Oracle likes generic names in the procedure heading, like NUMBER or VARCAHR2 instead of INT or VARCHAR(255). VARCHAR2 can be as large as 32,767 bytes, and you can manipulate it with such things as substring (I mean substr) just like you are used to. Although Oracle support blobs, the maximum column size allowed for a varchar is 4000. The table CRON_PARAMETERS even allows you to pass that large a VALUE to SP_CRON_INSERT_PARAMETER. Notice too that DEFAULT is actually a reserved word in Oracle. This was mapped to PREFERENCE by the translator. Output parameters never get mapped with the default template because it is impossible to redefine then associate the local declaration with this named parameter. Although dates can be passed to stored procedures, the translator assumes your date parameters are really strings submitted via an HTML form screen. If no format is specified (and there would be none), strings must match the default format (currently DD-MON-YY). To provide the same flexibility that you now enjoy in Sybase, your datetime parameters are declared as varchar2 in the procedure heading, then passed to HTTP.DATETIME when the parameter name is mapped locally. HTTP.DATETIME recognizes the standard Sybase styles and returns a valid DATE to your program. Stored procedures may not return a value. If you currently rely on the return status, you might want to consider using an output variable or even more desirable is to create an Oracle function. Oracle functions can return any datatype, not just integers. Passing the option -F to the translator will compile your procedure as a function. You've probably already figured out that "OR REPLACE" means you don't have to drop the procedure if it already exists. That's very convenient. Even more convenient is that the permissions on your stored procedure are not lost when you submit procedures in this fashion. |
DELETE tablename |
DELETE FROM tablename; |
Oracle requires the keyword FROM. The translator will insert FROM for all delete statements where this is omitted. |
INSERT tablename |
INSERT INTO tablename; |
Oracle requires the keyword INTO. The translator will insert INTO for all insert statements where this is omitted. | CHARINDEX(char_1,char_2) |
INSTR(char_2,char_1) |
INSTR() works the same way as CHARINDEX() except the 2 parameters are swapped. The translator will handle both cases. |
SUBSTRING() |
SUBSTR() |
SUBSTRING() works exactly like SUBSTR(). |
CONVERT(INT,exprepression) |
TO_NUMBER(expression) |
Basically the same, but expression cannot be a number. |
CONVERT(VARCHAR,expression) |
TO_CHAR(expression) or RTRIM(expression) |
Almost the same, except that expression cannot be a character string. If expression looks like a character string, the translator translate this to RTRIM() which is "probably" what you meant. Sometimes not, however. |
CONVERT(CHAR,expression) or CONVERT(CHAR(30),expression) |
RPAD(expression,1) or RPAD(expression,30) |
The RPAD() function will both pad or truncate your expression (a string or arithmetic expression) to the desired specified length. |
CONVERT(DATETIME,string) |
HTTP.DATETIME(string) |
|
CONVERT(VARCHAR,string,style)) |
TO_CHAR(string,format) |
Oracle does not recognize Sybase datetime styles. The translator will successfully translate all static styles to Orcale formats. Non static styles (computed at run-time) are not translated and means you have to translate this yourself. |
CONVERT(MONEY,123.456) |
TO_CHAR(123.456,'999,999,999,999.99') |
The translator assumes you are displaying this value and not performing arithmetic on it. The latter will fail because the format changes your number to a string with commas. Use NUMERIC(12,2) if this is the case and defer the conversion to MONEY when the value is actually displayed. |
CONVERT(NUMERIC,123) or CONVERT(NUMERIC(12,2),123) |
TO_CHAR(123,'999999999999999999') or TO_CHAR(123,'999999999999.99') |
|
CONVERT(INT,123.456) |
ROUND(123.456) |
|
@@spid |
USERENV('SESSIONID') |
See USERENV. |
@@ERROR |
SQLCODE |
The function SQLCODE returns the number code associated with the most recently raised exception.
SQLCODE is meaningful only in an exception handler. Outside a handler, SQLCODE always returns 0.
For internal exceptions, SQLCODE returns the number of the associated Oracle error. The number that SQLCODE returns is negative unless the Oracle error is no data found, in which case SQLCODE returns +100. |
@@ROWCOUNT |
SQL%ROWCOUNT or SQL_ROWCOUNT |
The %ROWCOUNT attribute yields the number of rows affected by an INSERT, UPDATE, or DELETE statement,
or returned by a SELECT INTO statement. If the translator, however, changed your SELECT statement into
a CURSOR declaration, @@ROWCOUNT is stored in SQL_ROWCOUNT.
%ROWCOUNT can also be appended to the name of a cursor or cursor variable. When a cursor is opened, %ROWCOUNT is zeroed. Before the first fetch, cursor_name%ROWCOUNT yields 0. Thereafter, it yields the number of rows fetched so far. The number is incremented if the latest fetch returned a row. |
A =* B |
A(+) = B |
|
A *= B |
A = B(+) |
|
DATALENGTH() |
LENGTHB() |
Oracle's LENGTHB() is the same as Sybase's DATALENGTH(). The translator performs the appropriate translation. |
DATETIME and SMALLDATETIME |
DATE and DATE |
Now you know why sp_addtype exists |
TEXT and IMAGE |
CLOB and BLOB |
To insert binary data into a Sybase IMAGE column you might specify a hexadecimal value of
0x22. In Oracle you would specify the string '22' to denote the same expression.
You cannot pass a LOB directly to WRITE(), but you can retrieve a LOB in pieces and pass them. See Example: Display the LOB Data Using PL/SQL from the write up on Internal Persistent LOBs. You can pass a "lob locator" to HTTP_SELECT_CLOB() or HTTP_SELECT_BLOB(). Binary LOB's need to be sent to Sybernet as Hexadecimal strings which are translated to Ascii using the <ASCEncode> and </ASCEncode> tags. HTTP_SELECT_BLOB() does this automatically for you.
or
In general, you can treat LOB's just like any other datatype and store them into local variables (something
you cannot do in Sybase). You just can't display them by selecting them (as you can do in Sybase).
There are also two procedures for writing writing LOB's (HTTP_WRITE_CLOB and HTTP_WRITE_BLOB). Since parameters to a stored procedure can be as large as 32,767 bytes, they have advantage over writing INSERT statements since the largest value allowed here is 4,000 bytes.
HTTP_WRITE_BLOB works exactly the same, but expects a BLOB instead of a CLOB.
|
CHAR() |
CHR() |
Not the declaration for a CHAR string, but the Ascii equivalent of the value passed to it. |
ISNULL() |
NVL() |
The parameters passed (and their order) as well as the result is identical between the two. |
USER_ID() |
HTTP.USER_ID() |
There is no USER_ID() function in Oracle so I wrote my own. |
USER_NAME() |
HTTP.USER_NAME() |
There is no USER_NAME() function in Oracle so I wrote my own. |
SUSER_NAME() |
USER |
|
DB_NAME() |
HTTP.DB_NAME |
There is only one database so the translator assumes you really mean the owner's
name. DB_NAME() returns the owner's name of the procedure that invoked it, not the user who
invoked your procedure. If you build your procedure name dynamically as in
the translator is unable to remove the second node (dbo); however, Sybernet will delete it when it can detect it. One case where it is not detected is when you pass this string to sp_cron_insert.db_name + ".dbo" + "WE_PAGER" Note: This theory falls apart if your Run Context is User instead of Author. Since most procedures will be compiled with Author, this should work fine. For more information, see the section on Run Context. |
GETDATE() |
SYSDATE |
|
/ |
/ |
Sybase performs integer division. Oracle performs arithmetic division. The translator does not handle this. If you want integer division, you probably need to call round() or floor(). |
% |
MOD |
|
REPLICATE(char_exp,n) |
HTTP.REPLICATE(char_exp,n) |
Oracle does not provide this function. I wrote my own. |
REVERSE(char_exp) |
HTTP.REVERSE(char_exp) |
Oracle does not provide this function. I wrote my own. |
RIGHT(char_exp,n) |
HTTP.RIGHT(char_exp,n) |
Oracle does not provide this function. I wrote my own. |
SPACE(n) |
HTTP.SPACE(n) |
Oracle does not provide this function. I wrote my own. |
STR(approx_numeric,length,decimal) |
HTTP.STR(approx_numeric,length,decimal) |
Oracle does not provide this function. I wrote my own. |
VALID_NAME(name) |
HTTP.VALID_NAME(name) |
Oracle does not provide this function. I wrote my own. |
STUFF(char_expr1,start,length,char_expr2) |
HTTP.STUFF(char_expr1,start,length,char_expr2) |
Oracle does not provide this function. I wrote my own. |
DATEPART(datepart,date) |
HTTP.DATEPART('datepart',date) |
Oracle does not provide this function. I wrote my own. Notice that the datapart (i.e., year) is quoted by the translator. If you use the abbreviated form of datepart (i.e., mm), they will be translated to the long form (i.e., month). |
DATENAME(datepart,date) |
HTTP.DATENAME('datepart',date) |
See DATEPART(). |
DATEADD(datepart,number,date) |
HTTP.DATEADD('datepart',number,date) |
See DATEPART().
While extremely accurate, this function is also extremely slow. You are encouraged to use Oracle datetime arithmetic for speedier results. |
DATEDIFF(datepart,date1,date2) |
HTTP.DATEDIFF('datepart',date1,date2) |
See DATEPART().
While extremely accurate, this function is also extremely slow. You are encouraged to use Oracle datetime arithmetic for speedier results. |
@@sqlstatus = 0 or @@sqlstatus = 2 or @@sqlstatus != 1 |
cursor%FOUND or cursor%NOTFOUND or cursor%ISOPEN |
In this context, cursor is the name of your cursor. The translator determines this name
from the FETCH statement that precedes this expression.
In the last example, there is no equivalent to @@sqlstatus = 1. In the case where you test for not equal to, the translator assumes that you will later test for found (0) or notfound (2). Thus the expression cursor%ISOPEN should provide the desired effect. |
select * into #TEMP from TableName |
Neither Oracle nor the translator recognize this kind of statement. The translator "thinks" the
syntax is valid and attempts to create a cursor declaration. Oracle generates the following error:
In Sybase this both creates and populates a table in one fell swoop. In Oracle, you must create the table first, then insert the desired rows. Were you to change your code in Sybase, it would look something like this:PLS-00103: Encountered the symbol "INTO" when expecting one of the following: from Statements of this sort are translated correctly, but see below on how the translator deals with temporary tables.CREATE TABLE #TEMP (PROCNAME VARCHAR(92), ...) INSERT #TEMP SELECT * FROM TableName |
|
CREATE TABLE #TEMP | CREATE TABLE TEMP_TEMP | Although Oracle supports temporary tables, they may not be created in procedures. The
translator deletes all table creations (even permanent ones) from your procedure source and
submits them in a separate batch. If you always give your temporary tables the same name and
their schemas are not identical, you will want to come up with another naming convention
(since they are now permanent tables).
Permanent tables work here because you are in a transaction, and you will be deleting everything from that table before your stored procedure terminates. Even if your procedure raises an error, the information is still erased because Oracle rolls back your transaction. Many times I have created temporary tables when a permanent table would have worked equally as well, if not better. Oracle recommends adding a SESSION_ID column to your temporary table so that you insure the rows inserted and retrieved go hand-in-hand with the user invoking your stored procedure. SESSION_ID is similar (if not identical) to @@spid. The expression USERENV('SESSIONID')returns the user's session ID. Sometimes you create temporary tables because you just want a list of names that you can step through. This is possible with something called a collection (see Collection and Records).
And to step through each name so that I can display it, I might do something like this:
Collections are more complicated than this, but it's a good example where a temporary
table is no longer needed.
|
DROP TABLE #TEMP | DELETE FROM TEMP_TEMP; | Because your temporary is now a permanent table, you don't really want to drop it. The
translator will instead change this to a delete statement.
If you don't drop the table because you expect it to vanish as soon as your procedure exits, then you will need to add the appropriate DELETE statement yourself. So, watch out for this. |
DROP TABLE PERMANENT | EXECUTE IMMEDIATE 'DROP TABLE PERMANENT'; | Tables (or any other objects) may not be dropped in a stored procedure. |
OBJECT_ID("PROCNAME") |
HTTP.OBJECT_ID('PROCNAME') |
There is no OBJECT_ID function in Oracle so I wrote my own. Unlike Sybase, your procedure
will have a valid OBJECT_ID even if it did not compile. HTTP.OBJECT_ID() also recognizes parameters that look like fully qualified Sybase identifiers. This can happen because you built this name dynamically or it was contained in a string. Names that look like are evaluated as though you saidDATABASE . OWNER . NAME HTTP.OBJECT_ID() also assumes that if you pass it dbo.NAME that you really mean USER.NAME.DATABASE . NAME |
OBJECT_NAME(object_id) |
HTTP.OBJECT_NAME(object_id) |
There is no OBJECT_NAME function in Oracle so I wrote my own. |
@@PROCID |
HTTP.OBJECT_ID('procname') |
I have not found a function in Oracle that dynamically returns your procedure's object ID. That doesn't mean it doesn't exist. For the time being, the translator will simply pass your procedure name to the function OBJECT_ID. |
WAITFOR DELAY "HH:MM:SS" |
DBMS_LOCK.SLEEP(seconds); |
The translator only recognizes the DELAY option. Any other option raises an error. |
GRANT ... | EXECUTE IMMEDIATE 'GRANT ...'; | I haven't a clue why you can't execute a grant or revoke statement in PL/SQL;
however, you can pass this statement as a string to EXECUTE IMMEDIATE using a facility known as
Native Dynamic SQL. You can do other clever things with this facility, like drop a table
when you don't know its name at compile-time.
Refer to the PL/SQL User's Guide and Reference page for more information on Native Dynamic SQL. |
REVOKE ... | EXECUTE IMMEDIATE 'REVOKE ...'; | See GRANT. |
DELTEK.DELTEK.EMPL or HRPRD.dbo.PS_PERSONAL_DATA | DELTEK.EMPL or HRPRD.PS_PERSONAL_DATA | There is only one database so the translator strips the owner name (DELTEK or DBO) and assumes the owner is now what you used to call the database. The translator, however, is unable to do this when you build the object name dynamically, but in most cases Sybernet will take care of this. |
something |
BEGIN something; END; | The translator will create an "anonymous block" if your batch attempts to submit
anything other than a CREATE or BEGIN statement. Typically, you might do this in something as
benign as
which becomes
|
SET ROWCOUNT n |
I'm pretty sure a similar feature exists in Oracle, but I can't remember what it is.
For the time being, this and all SET statements are deleted.
Every table in Oracle contains something called pseudo columns. One in particular is called ROWNUM which is the one-relative row number of each row. If you just wanted to list the first N rows of a table you could do something like this: SELECT * FROM TABLE WHERE ROWNUM <= Nwhich in some cases is why you were specifying SET ROWCOUNT. Another reason for using SET ROWCOUNT is that you want to retrieve the first or last row of a table. Getting the first row is accomplished thusly: but getting the last row isn't so easy. Clearly, we need to request an ORDER BY that is descending, but ROWNUM in this case doesn't necessarily specify the row number that you are interested in. That is, ROWNUM = 1 might be the row number before the ORDER BY is applied. The last row is achieved this way:select PROCNAME , GROUPNAME into MY_PROCNAME , MY_GROUPNAME from HTTP_REGISTER where ROWNUM = 1
With an additional WHERE clause you can also implement NEXT and BACK buttons this way.
|
|
PATINDEX("%pattern%",char_expr) |
HTTP.PATINDEX("%pattern%",char_expr) |
There is no such function in Oracle so I wrote my own. |
IDENTITY |
NULL |
Oracle does not support IDENTITY columns. Oracle recommends a trigger to insert the identity
value, and that is what the translator will build (a trigger) if you submit a CREATE TABLE
statement containing an identity column. This is what the trigger looks like.
The function GET_IDENTITY returns the next identity value for this table. It determines this
from the table called HTTP_IDENTITIES. This table is used by all triggers that are created either
through the translator or sp_csv.
The HTTP_IDENTITIES table contains four columns:
COLUMN_NAME is also used by sp_csv when you copy this table to Sybase (so that it can restore the IDENTITY declaration) or Oracle (so it can build another trigger). Instead of asking for @@identity (which you are unable to do), the translator will instead ask Oracle to return the identity column when data is inserted.
The code is not all that different from Sybase. Perhaps in some cases you don't really
need an identity column at all and can use Oracle's rownum or rowid pseudo columns instead.
|
@@IDENTITY |
SQL_IDENTITY |
While the translation looks simple, something quite unnatural happens here. Seeing @@identity causes
the translator to go back to your last insert statement and modifies it to return your identity column.
See above for what the syntax looks like. This is not perfect, however, if there are multiple insert
statements because they are the result of an IF ... ELSE construct. Consider this:
The translator should really modify both insert statements, but it doesn't. Only the 2nd one is correctly
modified.
Since an IDENTITY column can exist anywhere in a table, how does it know the name of your identity column? In general it doesn't know, unless the table was created by the translator or was copied to Oracle with sp_csv Both routines will create an entry in the table HTTP.HTTP_IDENTITIES just for this purpose. If you created the table on your own, you can "seed" this table by calling HTTP.GET_IDENTITY, passing it the owner, table name, and identity column.
|
INSERT TABLE VALUES (1,2,3) |
INSERT INTO TABLE VALUES (1,2,3) or INSERT INTO TABLE (A,B,C) VALUES (1,2,3) RETURNING identity INTO SQL_IDENTITY |
If your table contains an identity column, then your INSERT statement must specify the column names as
well as the values. The translator will do this for you automatically by asking Oracle. The translator
assumes (and otherwise does not know) that an identity column exists if it detects the use of @@identity
in your source.
Note that if you do an insert into a table containing an identity column and do not reference @@identity, you will get a syntax from Oracle if all the column names are not supplied. |
raiserror 42001 'Cursor failed.' |
raise_application_error (-42001,'Cursor failed.'); |
If your error_number is positive, the translator will make it negative. Oracle's documentation on raise_application_error says that error_number is a negative integer in the range -20000 .. -20999. The translator will display a warning message if your error_number is outside of this range. |
HOST_NAME() |
HTTP.HOST_NAME() |
HOST_NAME() returns your IP address or host name. You can also pass a session ID to retrieve the
host name of other users.
Sybernet maintains this information in the table HTTP.HTTP_USERDATAFILE when your session is started and deletes this information when your session is ended. Thus, only the IP address of Sybernet users can be retrieved with this function. If a session is abnormally terminated (perhaps because it was killed), Sybernet will not erase this information until a new user attempts to occupy this slot.
|
sp_getProcname @PROCID, @PROCNAME output |
HTTP.SP_GETPROCNAME (PROCID,PROCNAME) |
In this context PROCID is actually the object ID of your procedure. SP_GETPROCNAME returns
a fully qualfied (schema.name) procedure name. The current object ID is also written to the
table HTTP.HTTP_USERDATAFILE which can be retrieved.
As of this writing these are the columns in HTTP.USERDATAFILE:
When a session is ended, this information is erased. |
| Option | Description |
|---|---|
| Template |
It is too bad, but you can't prefix your variable names with an at-sign (@). I couldn't decide
on the best naming convention to use, so I left it as an option to you.
If your variables names (sans the at-sign) are unique and do not match column names, then you can tell the translator to leave your variable names untouched with the following template: %Or you can force qualification with %.%which prepends your procedure name (followed by a period) to each variable name when used in an expression. Or you may not want to give up the at-sign. For this you can specify @%which causes your variable names to be double-quoted and look really ugly, but pretty much guarantees your procedure won't have any naming conflicts. The default, however, is MY_%which looks pretty good in some cases (EMPL_ID becomes MY_EMPLID), but looks pretty bad in other cases (i becomes MY_i). |
| Translate As |
The translator will allow you to compile your procedure as either a procedure or a function.
You will want to select function if your procedure's return result is used by other procedures
or functions; for example, sp_cron_insert is a procedure in Sybase but a function in Oracle because
its return result is important to the procedures that call it.
If you select Function, you will not be able to register it with the Stored Procedure Register or with the Sybercron Register. Sybercron no longer uses your return result to determine if your procedure completed successfully (since there is none). Instead, Sybercron uses the built-in function ERRNUM to determine this. If you are compiling a trigger instead of a stored procedure, that needs to be specified as well, though I think you may be disappointed with some of the translation. I confess I didn't spend too much time on this, and although triggers are very similar to procedures, those constructs that are unique to triggers are not mapped very well (if at all). |
| Run Context |
Author says you want to run under the context of the author of this stored procedure. This is
typical for both Costpoint and PeopleSoft stored procedures which (because you are the owner)
can select, delete, insert, and update any table owned by that author.
User says you want to run under the context of the user that is invoking this procedure. Sybase procedures in sybsystemprocs are like this; for example, the Sybernet Interactive SQL procedure was compiled with a run context of user so that you can only see tables or execute procedures that you are allowed to touch. An interesting upshot of this facility is that when you are running the Sybernet Interactive SQL, Stored Procedure Editor, Stored Procedure Register, or Sybercron Register, you can't see anything you are not allowed to see. This is different from Sybase where although you may not touch a particular table, you can see that it exists. |
| Reserved Words |
This drop-down list determines how reserved words and non-reserved words are cased. PL/SQL
is case-insensitive, so it doesn't matter which you choose. It might matter to you because
you don't want to see mixcased reserved words. You can choose from
The default is "uppercase" which means Oracle reserved words are in uppercase and non-reserved words are lowercase. It's purely cosmetic, so the choice is yours. |
| Date To Varchar |
This option determines how the translator will map datetime parameters. Yes (the default)
means they are converted to varchar2 and passed to HTTP.DATETIME to create a local DATE
variable. No means datetime parameters are mapped directly to DATE.
You can't pass a string to a DATE variable in Oracle without also specifying the format of that date. For example,
is valid, but only because I told Oracle the format of the date. When a datetime is submitted
through a form screen, you can't really control what that date is going to look like. Date
strings must match the current default date format (DD-MON-YY) which doesn't allow you to
specify the century or the time.
If your datetime parameters are really dates because they are being passed to this procedure by another procedure, then you will want to reset this option. |
| Tabs |
As you can see the translator will insert more text than it removes. To do so while attempting
to preserve your particular style of formatting, it removes all tabs from your source file, but
puts them back when it is done.
This option determines how many spaces a tab represents. 4 (the default) and 8 are the two most popular. If you select 0, then your tabs are not removed, but then the translator won't know how much to indent the text that it inserts. |
| Check |
The default is NO and does no checking. When set to YES, the translator will "attempt" to compare
your variable names with column names in a table. If the types don't match, you'll get a warning.
Here's one now.
#WARNING: @JOB_FAMILY IS DECLARED AS CHAR(2) BUT THIS COLUMN WAS DECLARED AS CHAR(6)The translator assumes that your variable names (sans the @) are exactly the same as the column name. @JOB_FAMILY is matched with the column JOB_FAMILY. If you declared your variable as something else (@JBFAM), it ain't going to work. The translator also assumes that all column names with the same name have consistent types. That is, all JOB_FAMILY columns in your database are declared as CHAR(6). The reason for this is that it doesn't know the table name, so it uses the first one it finds. PeopleSoft is consistent in this case so I think this is a reasonable assumption. Also, it will search your schema first. If it doesn't find a column by that name, it will search everyone's schema. |
| Browse | You will use the Browse button to locate the source of your Sybase stored procedure. It's probably a very bad idea to point this to an Oracle stored procedure. |
| Translate |
The Translate button translates your Sybase stored procedure to Oracle Syntax, but instead of
submitting it to Oracle for compilation, the translated source is written to your browser.
If you choose to edit the Sybase source instead of the Oracle source, then this is quite
useful because the source is not only colorized, but each line is numbered as well.
Costpoint, for example, runs on a case in-sensitive server, but Oracle is case-sensitive. Consider the expression If the value of VAR is really SYBASE (upper-case) and not Sybase (mixed-case), then perhaps there is an argument to modify the Sybase source instead of the Oracle source.VAR = 'Sybase' |
| Compile |
The Compile button translates your Sybase stored procedure to Oracle Syntax. You might get
lucky and the procedure compiles without errors. But even if it doesn't, the procedure
will still reside in Oracle. You can use the Sybernet Stored Procedure Editor to extract
the source and make the necessary changes.
Even if it does compile (or when it does compiles), it is still your responsibility to make sure it works. |
| Sybase | Oracle |
|---|---|
create procedure sp_html_colors
as
select '<table border=1>'
select '<tr>'
, '<th>name</th>'
, '<th>color</th>'
, '</tr>'
select '<tr bgcolor=#'
, COLOR
, '>'
, '<td>'
, NAME
, '</td>'
, '<td>'
, COLOR
, '</tr>'
from http..HTTP_COLORS
select '</table>'
return 0
go
|
CREATE OR REPLACE procedure SP_HTML_COLORS
as
/*
** YOUR DECLARATIONS GLOBAL TO ALL BLOCKS
*/
SQL_ERROR INT; -- @@error
SQL_ROWCOUNT INT; -- @@rowcount
SQL_IDENTITY INT; -- @@identity
begin
HTTP.WRITE
(
'<table border=1>'
, '<tr>'
, '<th>name</th>'
, '<th>color</th>'
, '</tr>'
) ;
begin
declare cursor CUR is
select COLOR
, NAME
, COLOR
from HTTP_COLORS;
COL_1 varchar(255);
COL_2 varchar(255);
COL_3 varchar(255);
begin
open CUR;
fetch CUR into COL_1, COL_2, COL_3;
while (CUR%FOUND) loop
begin
HTTP.WRITE
(
'<tr bgcolor=#'
, COL_1
, '>'
, '<td>'
, COL_2
, '</td>'
, '<td>'
, COL_3
, '</tr>'
) ;
fetch CUR into COL_1, COL_2, COL_3;
end;
end loop;
SQL_ROWCOUNT:=CUR%rowcount;
close CUR;
end;
end;
HTTP.WRITE
(
'</table>'
) ;
return;
end;
|
| Command | Action |
|---|---|
VAR = 'string' |
Strings in Oracle are case-sensitive. Did you mean 'string' or did you mean 'STRING'? |
VAR != 'string' |
Be careful if VAR could be NULL. Oracle returns FALSE while Sybase returns TRUE
for this expression when VAR is NULL. You might instead prefer to write
NVL(VAR,'NULL') != 'string' or VAR is NULL or VAR != 'string' |
VAR like '%[a-z]%' |
Oracle doesn't recognize Sybase specifiers. Either modify your procedure or call HTTP.PATINDEX() instead.
|
declare VAR char(4):='Hello'; |
Because VAR isn't large enough to hold this string, Oracle generates this error:
Be careful because this is the sort of thing that happens when form data is sent to your procedure. The user entered "Hello" in a text field, but the receiving parameter was a char(4). You'll get a similar error if you attempt to insert this string into a column of the same size.ORA-06502: PL/SQL: numeric or value error: character string buffer too small |
CREATE TABLE #TEMP |
Temporary tables become permanent tables when passed through the translator. Make sure your temporary table
name does not collide with the same in other procedures.
Are you expecting your temprary table to vanish automatically when your procedure exits? If you are, then you will want to add the appropriate DELETE statement (since your temporary table is now a permanent table). |
DECLARE CURSOR |
Make sure your cursor declaration does not rely on variables that are assigned after the cursor is declared. |
SET |
Your SET statements are neither retained nor translated. You'll need to check how and why you are using these. |
'' |
In Sybase this is a space. In Oracle this is a string of length 0, and it is null. |
db_name() + ".dbo.WE_PAGER" |
This is a problem because the translator does not touch your strings and procedures no longer take the form
database . owner . namebecause the database name is no longer allowed. There is only one database so owner . namewould be the desired form. Procedures that call sp_getProcname, however, are unaffected because this procedure will determine the correct name. Sybernet, however, will treat procedure calls like this (when it can detect them) as DATABASE.NAME. In other words, it strips the owner name when it can, and it assumes that the database name is actually the owner of this procedure. In the above example (assuming it was executed from user DELTEK), the procedure name would be DELTEK.WE_PAGER. |
SELECT col INTO MY_col from TableName | If col is a char column larger than the size of MY_col, you will get an error. These are extremely hard to debug because Oracle blames the SELECT statement, not the column or variable causing the error. The translator might have declared MY_col as a char(4) when the column col is really a char(5). You either need to redeclare MY_col as a char(5), do a substring (substr) on col, or even more desireable, declare MY_col dynamically as TableName.col%TYPE. |
A / B | Oracle does not do integer division. If you are expecting an integer, then you really mean round( A / B ). |
| Construct | Comments |
|---|---|
SELECT "STRING" FROM TableName |
Notice that there are no columns selected from the table. The translator creates a cursor declaration
in anticipation that one or more columns actually do exist in your select list. If there are none,
the resultant code will produce a syntax error in Oracle.
Workaround: Omit the table. |
@NAME = NAME |
You probably meant to quote NAME in the right-half of this this expression. Sybase will do this
for you, but the translator won't.
Workaround: Quote the name. |
IF ( ... |
The translator assumes your procedure compiles in Sybase. In this example, a final right parenthesis
is missing. In some cases you might get an appropriate warning, but usually you will be told that your
procedure is too long.
Workaround: Fix all syntax errors. |
INSERT or DELETE or UPDATE |
The translator does not catch exceptions if these SQL statements return an error. Not so much a bug
as bad programming style. To catch exceptions you need to write your own exception code, probably
in its own block. However, I've never been real impressed with interrogating @@ERROR after one of
these statements fail in Sybase and since Oracle will abort when an exception is raised, that seems
reasonable enough to me.
Workaround: None required. |
SELECT "<br>" "</font>" |
The programmer meant to separate these two strings with a comma. Sybase interprets the second
string as a label. The translator should do likewise and throw it away, but it does not.
The result is a syntax error at compile-time which makes sense to me.
Workaround: Add a comma. |
EXECUTE XYZ |
If procedure XYZ were compiled as a function because it returns a result, then this statement will
fail because it needs be invoked as a function and not a procedure; for example, SP_CRON_INSERT
and SP_CRON_INSERT_PARAMETER are procedures in Sybase, but functions in Oracle. If you are calling
either of these without caring about the result, then you are being silly. Both return -1 if an
error occured or a value greater than zero if the call was successful.
Workaround: Ask for the return result (i.e., EXEC @i = XYZ). |
where B.EFFDT <= convert(char(10),getdate()) |
The problem here is that you are asking Sybase to convert a datetime variable (getdate) into a
character string. That works fine, and that is what is translated. But what you end up with is
an Oracle date (B.EFFDT) compared to a string. If the default date format doesn't match your
string, you end up with a run-time error (complaining, for example, that the month is invalid).
Workaround: Convert your string date to a date, perhaps with HTTP.DATETIME. |
SELECT BALANCE_YEAR , BALANCE_YEAR FROM PS_PAY_CAL_BAL_ID GROUP by BALANCE_YEAR |
All is well until you attempt to issue an ORDER BY or GROUP BY clause. Because BALANCE_YEAR
is referenced twice, Oracle has no idea which BALANCE_YEAR you want to group. Unfortunately,
this is a run-time error, not a compilation error.
Workaround: Alias one of the columns to a different name. |
update Distribution set EmpNo=@Empno from distribution d, document dd where d.Control_No=dd.Control_No and dd.project_no=@Project |
This isn't legal because you cannot join tables in an UPDATE or DELETE statement.
Workaround: Eliminate the join.
update Distribution
set EmpNo = @Empno
where Control_No in
(
select Control_No
from document
where project_no = @Project
)
|
UPDATE temp_task_comments SET assigned_analyst_name = sn FROM temp_task_comments , deltek_of.task_handling_staff WHERE assigned_analyst = employeenumber |
Similar to the above except now we are updating a column from another table.
Workaround: Eliminate the join.
UPDATE temp_task_comments
SET assigned_analyst_name =
(
select sn
from deltek_of.task_handling_staff
where assigned_analyst = employeenumber
)
|
SELECT DISTINCT RPAD(FY_CD,4) || FY_CD FROM DELTEK.SUB_PD ORDER BY FY_CD |
All is well until you attempt to issue an ORDER BY clause. Although this compiles in Sybase
and FY_CD is a real column, Oracle replies with ORA-01791: not a SELECTed expression.
Workaround: Order by RPAD(FY_CD,4) || FY_CD or use GROUP BY instead of DISTINCT. |
if 'ALL' in
(
select PRIV_ID
from deltek.USER_REPORT_PRIV
where USER_ID = suser_name()
and REC_CD = 'JE'
)
|
I've used this construct myself. Unfortunately, this was not addressed and will produce a
syntax error in Oracle.
Workaround: Use EXISTS() instead.
if exists
(
select 1
from deltek.USER_REPORT_PRIV
where USER_ID = suser_name()
and REC_CD = 'JE'
and PRIV_ID = "ALL"
)
|
select @per_cyl_chg_factor =
(
select sum(amount)
from deltek.deltek.gas_scratch
where file_indicator = "S"
and pd_indicator = "D"
)
/
(
select sum(no_of_unit)
from deltek.deltek.gas_scratch
where file_indicator = "U"
)
|
Although the translator does handle SELECT statements inside of IF and WHILE loops, it does
not handle SELECT statments inside of assignment statements, as illustrated here.
Workaround: Calculate each value separately or use the following in Oracle.
SELECT A.AMOUNT/B.UNIT
INTO my_per_cyl_chg_factor
from
(
SELECT SUM(amount) AMOUNT
FROM deltek.gas_scratch
WHERE file_indicator = 'S' AND pd_indicator='D'
) A
,
(
SELECT SUM(no_of_unit) UNIT
FROM deltek.gas_scratch
WHERE file_indicator = 'U'
) B
|
select @procname = "deltek.deltek.sp_ap_report" exec @procname @out = @out output |
This is an oversight on my part and is a real bug. The translator assumes that
all parameters are by-value, that there are no output parameters.
Workaround: If you do not have to build the name dynamically, then don't.
exec deltek.deltek.sp_ap_report
@out = @out output
If you have to build the name dynmaically, then you are forced to modify the Oracle
source.
|
select DESC_CODE from TAB1 where DESC_CODE like "A%" union select DESC_CODE from TAB1 where DESC_CODE like "B%" |
This statement causes the translator to build a cursor. Unfortunately, the translator
was not expecting a UNION statement in this situation. The first select is ignored
(causing a syntax error) while the second select is translated into a cursor.
Workaround: If the order is not important, it may be possible to break this into two seprate statements: select DESC_CODE from TAB1 where DESC_CODE like "A%" select DESC_CODE from TAB1 where DESC_CODE like "B%"You may also want to consider using a Sybase cursor instead to build this query. In this case, the translator does handle UNION. |
ACTION="Sybase.cgi" |
Sybernet.cgi used to be called Sybase.cgi. This expression works in Sybase because there is a unix link for
Sybase.cgi that points to Sybernet.cgi. There is no such link for the Oracle version.
Workaround: Change the name to Sybernet.cgi |
| Feature | Comments | Ranking |
|---|---|---|
DECLARE VAR VARCHAR(30) := 'Hello World' |
You can now declare local variables and give them a default value at the same time. |
|
P (VAR => 'Hello' || 'World') |
Value parameters passed to stored procedures may be real expressions. No longer do you need to declare a local variable, assign it an expression, and pass that as the value to a procedure. |
|
CREATE PROCEDURE P (VAR IN VARCHAR2) |
VAR can be as large as 32,767 bytes! |
|
CREATE TABLE T (VAR varchar(4000)) |
VAR can be as large as 4,000 bytes! |
|
VAR != 'STRING' |
Oracle returns FALSE if VAR is NULL. |
|
NVL(DEPTID,'023') |
DEPTID is a char(10) and so is this expression, but Oracle is no longer able to compare this
to a literal string. While a
generates 12 entries, aSELECT * FROM PEOPLESOFT where DEPTID = '023' generates none. I am suppose to rpad() the right-hand side or rtrim the left-hand side to make this work.SELECT * from PEOPLESOFT where nvl(DEPTID,'023') = '023' |
|
DECLARE VAR CRON.NAME%TYPE; |
VAR is automatically declared with the same type as the column NAME in table CRON. If the table changes, you don't have to redeclare VAR in your stored procedure. |
|
DECLARE VAR PLHIST%ROWTYPE; |
VAR here is a record of PLHIST. I no longer need to declare every variable when I fetch
from a cursor. I can say instead,
which means I just fetched 30 columns of information into one variable, and to get at those values I just append the colum name as in, VAR.EMPL_STATUS.FETCH cursor_name INTO VAR |
|
DECLARE VAR1 VARCHAR(30):='String'; VAR2 CHAR(30) :='String'; |
VAR1 does not equal VAR2.
You'll want to have a look at the PL/SQL User's Guide and Reference on CHAR versus VARCHAR2 Semantics. Not that you will feel any better after reading it, but it's certainly worth your time. |
|
FUNCTIONS |
I invite you to explore the functions in HTTP. Most of the names should look familiar
because they perform the same task as their Sybase name sake. My two favorites are
PATINDEX() and DATETIME(), though the latter isn't really a Sybase function, but it
certainly made the translation from Sybase to Oracle possible. You're going to like
Oracle functions!
The PL/SQL User's Guide and Reference has a good write up on functions. |
|