我正在尝试从 Excel VBA 调用 PL SQL 函数。当我在 ORACLE SQL DEVELOPER 上执行它时,它运行得很好。但是当我尝试从 excel vba 运行相同的内容时,它给出了以下错误。
请查看以下代码片段。
Private Sub CommandButton1_Click()
Dim con As Object
Dim rs As Object
Dim cmd As Object
Dim sQuery As String
Set con = CreateObject("ADODB.Connection")
Set rs = CreateObject("ADODB.Recordset")
Set cmd = CreateObject("ADODB.Command")
strcon = "Provider=OraOledb.Oracle;Data Source=10.1.2.238:1521/oracle.MultiActTrade.LOCAL; User ID=; Password=;Persist Security Info=True"
con.Open strcon
sQuery = "SELECT * FROM TABLE(MA_DWM_AVG(TO_CHAR('L48D88-S-IN'),TO_NUMBER(2),TO_NUMBER(11),TO_NUMBER(40)))"
cmd.ActiveConnection = con
cmd.CommandText = sQuery
Set rs = cmd.Execute()
Sheets("Sheet1").Cells(2, 1).CopyFromRecordset rs
rs.Close
End Sub
即使使用 sQuery 如下所示,错误仍然存在。变量数据类型已经得到很好的处理。不存在类型不匹配的情况。
SELECT * FROM TABLE(MA_DWM_AVG('L48D88-S-IN',2,11,40))
以下是我遇到错误的函数代码。函数代码非常大,因此仅给出所需的代码片段。
create or replace function MA_DWM_AVG(FID IN VARCHAR, CHOICE INT, PERIOD1 INT,PERIOD2 INT)
return TEMP_NESTED as
--to create a seperate transcion for the function we have created.
PRAGMA AUTONOMOUS_TRANSACTION;
V_RET TEMP_NESTED;
begin
--TRUNCATING GTT TABLE USED IN A FUNCTION
EXECUTE IMMEDIATE 'TRUNCATE TABLE GTT_DWM_STATS';
--IF DAILTY STATISTICS ARE NEEDED THEN CHOICE=1
--We calculate rownumber,fs_perm_sec_id, date ,closing value its avearages for 2 periods, open price, high price for the day, low price for the day
--weekly statistics are allculated if choice=2
IF CHOICE =2 AND PERIOD1<>0 AND PERIOD2<>0 THEN
--First we calculate rownumber,fs_perm_sec_id, week end date, Closing price and its average for 2 periods
INSERT INTO GTT_DWM_STATS(ROWNUMBER,FID,CLOSINGDATE,PRICE_CLOSE,MOVINGAVERAGE_PERIOD1,MOVINGAVERAGE_PERIOD2)
SELECT ROWNUM,FS_PERM_SEC_ID,"DATE",PPRICE,
CASE
WHEN COUNT(PPRICE) OVER (ORDER BY "DATE" DESC ROWS BETWEEN CURRENT ROW AND PERIOD1-1 FOLLOWING) >= PERIOD1
THEN AVG(PPRICE) OVER (ORDER BY "DATE" DESC ROWS BETWEEN CURRENT ROW AND PERIOD1-1 FOLLOWING)
ELSE NULL
END AS "Moving Average Period 1",
CASE
WHEN COUNT(PPRICE) OVER (ORDER BY "DATE" DESC ROWS BETWEEN CURRENT ROW AND PERIOD2-1 FOLLOWING) >= PERIOD2
THEN AVG(PPRICE) OVER (ORDER BY "DATE" DESC ROWS BETWEEN CURRENT ROW AND PERIOD2-1 FOLLOWING)
ELSE NULL
END AS "Moving Average Period 2"
FROM(
SELECT FS_PERM_SEC_ID,"DATE",P_PRICE AS PPRICE,P_VOLUME,
CASE
WHEN (TO_CHAR("DATE",'D') >= AVG(TO_CHAR("DATE",'D')) OVER (order by "DATE" DESC rows between 1 preceding and current row) and ROWNUM>=1)
or TO_CHAR("DATE",'D')=6
THEN 1
ELSE 0
END AS WEEKFLAG
FROM FP_BASIC_BD WHERE FS_PERM_SEC_ID=FID AND P_VOLUME<>0 ORDER BY "DATE" DESC) WHERE WEEKFLAG=1;
--get week start date
--line 89 is here
UPDATE GTT_DWM_STATS
SET STARTDATE =
(SELECT "DATE" FROM FP_BASIC_BD WHERE FP_BASIC_BD.FS_PERM_SEC_ID=FID AND FP_BASIC_BD."DATE">=TO_CHAR(TRUNC(TO_DATE(GTT_DWM_STATS.CLOSINGDATE,'DD-MON-YY'), 'IW'),'DD-MON-YY') AND P_VOLUME<>0 AND ROWNUM=1)
WHERE EXISTS (SELECT FP_BASIC_BD."DATE" FROM FP_BASIC_BD WHERE FP_BASIC_BD."DATE"=GTT_DWM_STATS.CLOSINGDATE );
--get opening price for the week
UPDATE GTT_DWM_STATS
SET PRICE_OPEN =
(SELECT FP_BASIC_BD.P_PRICE_OPEN FROM FP_BASIC_BD WHERE FP_BASIC_BD.FS_PERM_SEC_ID=GTT_DWM_STATS.FID AND FP_BASIC_BD."DATE"=GTT_DWM_STATS.STARTDATE);
--get high value of p_price_high for week's duration
UPDATE GTT_DWM_STATS
SET PRICE_HIGH=
(SELECT MAX(P_PRICE_HIGH) FROM FP_BASIC_BD WHERE FP_BASIC_BD.FS_PERM_SEC_ID=FID AND FP_BASIC_BD."DATE" BETWEEN GTT_DWM_STATS.STARTDATE AND GTT_DWM_STATS.CLOSINGDATE)
WHERE EXISTS(SELECT P_PRICE_HIGH FROM FP_BASIC_BD WHERE FP_BASIC_BD.FS_PERM_SEC_ID=GTT_DWM_STATS.FID);
--get low value of p_price_low for week's duration
UPDATE GTT_DWM_STATS
SET PRICE_LOW=
(SELECT MIN(P_PRICE_LOW) FROM FP_BASIC_BD WHERE FP_BASIC_BD.FS_PERM_SEC_ID=FID AND FP_BASIC_BD."DATE" BETWEEN GTT_DWM_STATS.STARTDATE AND GTT_DWM_STATS.CLOSINGDATE) WHERE EXISTS(SELECT P_PRICE_LOW FROM FP_BASIC_BD WHERE FP_BASIC_BD.FS_PERM_SEC_ID=GTT_DWM_STATS.FID);
END IF;
--get the GTT values into table derived from object type and return it.
select
cast(
multiset(
select * from GTT_DWM_STATS WHERE GTT_DWM_STATS.FID=FID ORDER BY CLOSINGDATE DESC
)as TEMP_NESTED) into v_ret from dual;
COMMIT;
return V_RET;
end MA_DWM_AVG;
您还可以引用全局临时表的DDL,如下。
CREATE GLOBAL TEMPORARY TABLE "MA_FACTSET"."GTT_DWM_STATS"
( "ROWNUMBER" NUMBER(*,0),
"FID" VARCHAR2(20 BYTE),
"CLOSINGDATE" DATE,
"PRICE_CLOSE" FLOAT(126),
"MOVINGAVERAGE_PERIOD1" FLOAT(126),
"MOVINGAVERAGE_PERIOD2" FLOAT(126),
"STARTDATE" DATE,
"PRICE_OPEN" FLOAT(126),
"PRICE_HIGH" FLOAT(126),
"PRICE_LOW" FLOAT(126)
) ON COMMIT PRESERVE ROWS ;
请不要建议我将函数用作过程。因为函数正在使用全局临时表,并且最后它返回完整的表。因此将其作为强制功能使用。
最佳答案
您对日期的处理完全不正确。很难指出确切的位置,因为有一些潜在的位置。我将解释这段代码有什么问题(它取自您的“第 89 行”语句):
... FP_BASIC_BD."DATE">=TO_CHAR(TRUNC(TO_DATE(GTT_DWM_STATS.CLOSINGDATE,'DD-MON-YY'), 'IW'),'DD-MON-YY') ...
GTT_DWM_STATS.CLOSINGDATE
具有日期格式(根据您的 DDL 语句)。但是 to_date
函数将字符串作为第一个参数。在此位置,Oracle 执行以下操作:
- 隐式将日期转换为字符串(使用 session 格式)
- then
to_date
函数尝试将其转换回日期,并将字符串视为以'DD-MON-YY'
格式编写的日期 - 然后
trunc
截断日期 - 然后
to_char
将其再次转换为字符串,然后将字符串与日期进行比较(我猜)FP_BASIC_BD."DATE"
- 如果
FP_BASIC_BD."DATE"
是日期,Oracle 会再次将等号右侧表达式的结果隐式转换为日期 - 或者如果
FP_BASIC_BD."DATE"
是一个字符串,Oracle 将根据字符串比较规则来比较字符串。
这里你需要的是摆脱所有不必要的转换:
FP_BASIC_BD."DATE" >= TRUNC(GTT_DWM_STATS.CLOSINGDATE, 'IW')
之后,您必须仔细检查您正在处理日期的所有其他语句。如果函数采用日期作为输入参数,则必须传递日期,如果函数采用字符串 - 则传递字符串。比较也是如此:比较字符串与字符串、日期与日期。
关于vba - 从 Excel VBA 调用 Oracle PL SQL 函数,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/51490979/