Oracle function error - sql

I am typing a function and i'm having an error here, I dont know what it is.
Could you give me a Hand ?
CREATE or replace FUNCTION function1(pIdReg in number,pIdPeriod in number) RETURN
number
IS
ncv number DEFAULT 0;
BEGIN
SELECT COUNT(IdPeriod)
INTO ncv
FROM(
SELECT a.IdPeriod, SUM(case when a.nt=0 then -a.valor else a.valor end) AS total --IF(a.nt=0,-a.valor,a.valor))
FROM dc a
JOIN emp b ON a.idDoc = b.idDoc
WHERE a.idReg = pIdReg AND a.IdPeriod < pIdPeriod AND
b.cc != 305 AND
(
b.cc = 302 AND(b.tipomov != 4)
OR
b.cc != 302 AND(1=1)-- emular el TRUE
)
AND a.type != 7
GROUP BY 1 HAVING total != 0
) AS ncv;
RETURN ncv;
END;
/
The error is SQL command not properly ended.
Sqldeveloper shows "AS ncv" underlined. Is there any problem with group by or having clause ?

I see three errors (though there may be more)
Oracle does not use the AS keyword for assigning table aliases. So AS ncv is invalid. If you want to use ncv as the alias for your subquery, you'd need to remove the AS (though it seems odd to choose an alias that happens to collide with the name of a local variable).
You cannot use positional notation in a GROUP BY clause. You would need to specify the name of the column(s) you want to group by not their position.
You cannot use aliases defined in your SELECT list in your HAVING clause. You would have to specify the aggregate function in the HAVING clause
Putting those three things together, I suspect you want something like this
CREATE or replace FUNCTION function1(pIdReg in number,pIdPeriod in number)
RETURN number
IS
ncv number DEFAULT 0;
BEGIN
SELECT COUNT(IdPeriod)
INTO ncv
FROM(
SELECT a.IdPeriod,
SUM(case when a.nt=0
then -a.valor
else a.valor
end) AS total --IF(a.nt=0,-a.valor,a.valor))
FROM dc a
JOIN emp b ON a.idDoc = b.idDoc
WHERE a.idReg = pIdReg AND a.IdPeriod < pIdPeriod
AND b.cc != 305
AND (
b.cc = 302 AND(b.tipomov != 4)
OR
b.cc != 302 AND(1=1)-- emular el TRUE
)
AND a.type != 7
GROUP BY a.IdPeriod
HAVING SUM(case when a.nt=0
then -a.valor
else a.valor
end) != 0
) ncv;
RETURN ncv;
END;
/
If you are still getting errors, it would be extremely helpful if you could edit your question and provide the DDL to create the tables that are referenced in this code. That would allow us to test on our systems whether the function compiles or not rather than trying to guess at the syntax errors

Related

GETTING ERROR-- ORA-00936:MISSING EXPRESSION for below query please help on this

SELECT CASE (SELECT Count(1)
FROM wf_item_activity_statuses_v t
WHERE t.activity_label IN ('WAITING_DISB_REQ',
'LOG_DDE',
'LOG_SENDBACK_DDE')
AND t.item_key IN(
SELECT r.i_item_key
FROM wf_t_item_xref r
WHERE r.sz_appl_uniqueid = '20400000988')
)
WHEN 0 THEN
(
delete
from t_col_val_document_uploaded p
WHERE p.sz_application_no = '20400000988'
AND p.sz_collateral_id = 'PROP000000000PRO1701'
AND p.i_item_key = '648197'
AND p.i_document_srno = '27' )
WHEN 1 THEN
(
DELETE
FROM t_col_val_document_uploaded p
WHERE p.sz_application_no = '20400000988'
AND p.sz_collateral_id = 'PROP000000000PRO1701'
AND p.i_document_srno = '28' )
ELSE NULL
END
FROM dual;
You need to recreate your query and make sure to follow the flow of the clauses properly, please check the next two links to get a better understanding :
[ORA-00936: missing expression tips]
How do I address this ORA-00936 error?
Answer: The Oracle oerr utility notes this about the ORA-00936 error:
ORA-00936 missing expression
Cause: A required part of a clause or expression has been omitted. For example, a SELECT statement may have been entered without a list of columns or expressions or with an incomplete expression. This message is also issued in cases where a reserved word is misused, as in SELECT TABLE.
Action: Check the statement syntax and specify the missing component.
The ORA-00936 happens most frequently:
1 - When you forget list of the column names in your SELECT statement.
2. When you omit the FROM clause of the SQL statement.
ora-00936-missing-expression
I hope this can help you.
You cannot use a simple select query like this. You have to use a PL/SQL block like below -
DECLARE NUM_CNT NUMBER := 0;
BEGIN
SELECT Count(1)
INTO NUM_CNT
FROM wf_item_activity_statuses_v t
WHERE t.activity_label IN ('WAITING_DISB_REQ',
'LOG_DDE',
'LOG_SENDBACK_DDE')
AND t.item_key IN(SELECT r.i_item_key
FROM wf_t_item_xref r
WHERE r.sz_appl_uniqueid = '20400000988');
IF NUM_CNT = 0 THEN
delete
from t_col_val_document_uploaded p
WHERE p.sz_application_no = '20400000988'
AND p.sz_collateral_id = 'PROP000000000PRO1701'
AND p.i_item_key = '648197'
AND p.i_document_srno = '27';
ELSIF NUM_CNT = 1 THEN
DELETE
FROM t_col_val_document_uploaded p
WHERE p.sz_application_no = '20400000988'
AND p.sz_collateral_id = 'PROP000000000PRO1701'
AND p.i_document_srno = '28' )
END IF;
END;

Nested case conditionals in PostgreSQL, syntax error?

I use PostgreSQL and I would like to combine these two case conditions, but when I uncomment the code, I get a syntax error. This is not a difficult instruction. I want to divide or multiply the obtained number depending on the condition. How can I enter it so that the code is compiled?
SELECT
SUM(
CASE
WHEN transactions.recipient_account_bill_id = recipientBill.id AND recipientBill.user_id = 2
THEN 1
ELSE -1
END * transactions.amount_money
/* CASE
WHEN senderBillCurrency.id = recipientBillCurrency.id
THEN NULL
ELSE
CASE
WHEN recipientBillCurrency.base = true
THEN /
ELSE *
END senderBillCurrency.current_exchange_rate
END */
) as TOTAL
FROM transactions
LEFT JOIN bills AS senderBill ON senderBill.id = transactions.sender_account_bill_id
LEFT JOIN bills AS recipientBill ON recipientBill.id = transactions.recipient_account_bill_id
LEFT JOIN currency as senderBillCurrency ON senderBillCurrency.id = senderBill.currency_id
LEFT JOIN currency as recipientBillCurrency ON recipientBillCurrency.id = recipientBill.currency_id
WHERE 2 IN (senderBill.id, recipientBill.id)
You cannot create dynamic SQL expressions like that. A CASE expression cannot return an operator as if SQL was some sort of macro language. You can only return an expression.
You already used the following approach using 1 and -1 with your multiplication. Why not also use it with N and 1/N:
<some expression> * CASE WHEN condition THEN 1 / N ELSE N END
Or in your case:
<some expression> * CASE
WHEN senderBillCurrency.id = recipientBillCurrency.id THEN 1
WHEN recipientBillCurrency.base THEN 1 / senderBillCurrency.current_exchange_rate
ELSE senderBillCurrency.current_exchange_rate
END
Notice, you can put several WHEN clauses in a CASE expression. No need to nest them

Firedac multiparametric query with macro

I created the main query that returns values for a whole, with 2 secondary conditions to restrict choice to be taken in the side combo box.
Everything works with the set parameters. I wish I could turn off or turn on these conditions with side combo box, how should I proceed?
my code is in Delphi:
procedure TForm1.Button3Click(Sender: TObject);
begin
FDQuery3.Close;
FDquery3.Params[0].Value := Datetimepicker1.Date;
FDquery3.Params[1].Value := Datetimepicker2.Date;
FDQuery3.Params[2].Value := Combobox3.Items [Combobox3.Itemindex];
FDQuery3.Params[3].Value := Combobox5.Items [Combobox5.Itemindex];
FDQuery3.Open;
end;
SQL text is:
select
G.NUM_PROG,T.DATA,T.ORA,C.DESCRIZIONE,
(select DESKEY from ANAFORN where CODKEY=T.CODICE ) as Cliente,
O.NOMINATIVO, T.TERMINALE,T.INCASSO
from LG_RIGHE G
inner join LG_TESTA T on G.NUM_PROG =T.NUM_PROG
inner join OPERATORI O on T.OPERATORE = O.CODICE
inner join LG_CAUSA C on T.CAUSALE = C.CODICE
where T.DATA >= :data1
and T.DATA <= :data2
and T.INCASSO = :pagamento
and T.TERMINALE = :terminale
order by G.NUM_PROG
i want turn on/off only Params[2][ and Params[3] (name: pagamento, terminale)
1) The typical way to optionally ignore a condition is to add one more "toggle" parameter. Consider this Delphi code
Result := True; // or False. Actually - some previous part of formula
If Need_Check_Option_A then
Result := Result and ( Option_A > 20 );
If Need_Check_Option_B then
Result := Result and ( Option_B < 10 );
Got the idea?
But very long that is, is there a more concise way to write it ?
Result := .....some other parts....
and (Ignore_Option_A or (Option_A > 20 ))
and (Ignore_Option_B or (Option_A < 10 ))
and ....
Now let's re-phrase it from Delphi to SQL WHERE clause
WHERE (.......) and (......)
AND ( ( :Use_pagamento = 0 ) or ( T.INCASSO = :pagamento ) )
AND ( ( :Use_terminale = 0 ) or ( T.TERMINALE = :terminale ) )
Whether you set that USE_xxxx parameter to zero (similar to false) then the second check would be shortcut out, ignored.
And the calling code would be something like
FDquery3.ParamByName('data1').AsDate := Datetimepicker1.Date;
FDquery3.ParamByName('data2').AsDate := Datetimepicker2.Date;
FDQuery3.ParamByName('pagamento').AsString := Combobox3.Items [Combobox3.Itemindex];
FDQuery3.ParamByName('terminale').AsString := Combobox5.Items [Combobox5.Itemindex];
FDQuery3.ParamByName('Use_pagamento').AsSmallInt := Ord( CheckBox3.Checked );
FDQuery3.ParamByName('Use_terminale').AsSmallInt := Ord( CheckBox5.Checked );
Some more suggestions follow:
2) using names like ComboBox3 are bad. You would not understand what they mean, what was they intended to be for. Look at your SQL - you give names there! You do not make it like
SELECT FIELD1, FIELD2 FROM TABLE1 WHERE FIELD3 < :PARAM1
And you have to give reasonable names to your Delphi objects too!
That FDQuery3, that Checkbox3 that Combobox5 - rename them all, give them some meaningful names!
3) you have a nested select there as the Cliente column. Unless very special circumstances that is slow and inefficient - change it to JOIN too (maybe to LEFT JOIN, if sometimes there is no matching value)
select
G.NUM_PROG,T.DATA,T.ORA,C.DESCRIZIONE,
-- (select DESKEY from ANAFORN where CODKEY=T.CODICE ) as Cliente,
A.DESKEY as Cliente,
O.NOMINATIVO, T.TERMINALE,T.INCASSO
from LG_RIGHE G
inner join LG_TESTA T on G.NUM_PROG =T.NUM_PROG
inner join OPERATORI O on T.OPERATORE = O.CODICE
inner join LG_CAUSA C on T.CAUSALE = C.CODICE
/* left */ join ANAFORN A on A.CODKEY=T.CODICE
where T.DATA >= :data1
and T.DATA <= :data2
AND ( ( :Use_pagamento = 0 ) or ( T.INCASSO = :pagamento ) )
AND ( ( :Use_terminale = 0 ) or ( T.TERMINALE = :terminale ) )
order by G.NUM_PROG
4) Depending on the circumstances you may just want to alter the SQL text.
If the parameter would be ignored - then simply remove it!
This option is not universal, it has good and bad sides though.
But in your case it would rather do good or nothing - because you have human to re-open the query and human would not be able to do it more often than once per second.
Good: then the server gets your SQL text it prepares the QUERY PLAN. The internal program of how to fetch your data. And it does not know yet what your parameters would be, so it prepares the PLAN to always check those parameters. Even if you later would ignore them. Sometimes it might make server choose slow PLAN where it could choose faster one if it knew the parameter would be not used. Sometimes it would make no difference. Game of luck.
Bad: if you keep the SQL text the same, then you can PREPARE the query once and the server would not build different PLAN when you re-open the query with different parameters. But if you do change the SQL text, then server would have to parse that new query and PREPARE the PLAN again before it would give you data. Sometimes it would take considerable time when you open-close queries, say, 1000 times per second. OF course, when you use a human to set those checkboxes, comboboxes and then press buttons, he would not do it that frequently, so in this case that risk is moot.
So in your case you might do something like this instead of introducing those toggle-parameters:
var qt: TStrings; // SQL query text
.....
qt := FDQuery3.SQL;
qt.Clear; // changing SQL Text would auto-close the query
qt.Add('select G.NUM_PROG,T.DATA,T.ORA,C.DESCRIZIONE, ');
qt.Add(' A.DESKEY as Cliente, O.NOMINATIVO, T.TERMINALE,T.INCASSO ');
qt.Add('from LG_RIGHE G ');
qt.Add(' join LG_TESTA T on G.NUM_PROG = T.NUM_PROG ');
qt.Add(' left join ANAFORN A on A.CODKEY=T.CODICE');
qt.Add(' join OPERATORI O on T.OPERATORE = O.CODICE ');
qt.Add(' join LG_CAUSA C on T.CAUSALE = C.CODICE ');
qt.Add('where T.DATA >= :data1 and T.DATA <= :data2 ');
if CheckBox3.Checked then
qt.Add(' and T.INCASSO = :pagamento ');
if CheckBox5.Checked then
qt.Add(' and T.TERMINALE = :terminale ');
qt.Add('order by G.NUM_PROG');
FDquery3.ParamByName('data1').AsDate := Datetimepicker1.Date;
FDquery3.ParamByName('data2').AsDate := Datetimepicker2.Date;
if CheckBox3.Checked then
FDQuery3.ParamByName('pagamento').AsString := Combobox3.Items [Combobox3.Itemindex];
if CheckBox3.Checked then
FDQuery3.ParamByName('terminale').AsString := Combobox5.Items [Combobox5.Itemindex];
FDQuery3.Open;
In this option you do not introduce extra toggle-parameters, but instead you only add value-parameters when user checked to use them. If user unchecked them - then you do not include them into your SQL text and consequently you do not assign them any values (they would not be found anyway).
5) you may use BETWEEN - it may be easier to read.
...
where ( T.DATA BETWEEN :data1 AND :data2 )
and T.INCASSO = :pagamento
....

Conditional WHERE clause with CASE statement in Oracle

I'm brand-new to the Oracle world so this could be a softball. In working with an SSRS report, I'm passing in a string of states to a view. The twist is that the users could also pick a selection from the state list called "[ No Selection ]" ... (that part was not by doing and I'm stuck with implementing things this way)
If they choose the No Selection option, then I just want to return all states by default, otherwise return just the list of states that are in my comma-separated list.
This really seems like it should be easy but I'm stuck. Here is the code I have so far (just trying to get a sample to work) but my eyes have finally gone crossed trying to get it going.
Could anybody give me some direction on this one please?
begin
:stateCode:='MO,FL,TX';
--:stateCode:='[ No Selection ]';
end;
/
select count(*) as StateCount, :stateCode as SelectedVal
from hcp_state vw
where
(case
when (:stateCode = '') then (1)
when (:stateCode != '') then (vw.state_cd in (:stateCode))
(else 0)
end)
;
You can write the where clause as:
where (case when (:stateCode = '') then (1)
when (:stateCode != '') and (vw.state_cd in (:stateCode)) then 1
else 0)
end = 1;
Alternatively, remove the case entirely:
where (:stateCode = '') or
((:stateCode != '') and vw.state_cd in (:stateCode));
Or, even better:
where (:stateCode = '') or vw.state_cd in (:stateCode)
We can use a CASE statement in WHERE clause as:
SELECT employee_no, name, department_no
FROM emps
WHERE (CASE
WHEN :p_dept_no = 50 THEN 0
WHEN :p_dept_no = 70 THEN 0
ELSE -1
END) = 0;

Table or view does not exist - Oracle complains about a comma rather than an actual table or view name

I've never seen this before... I have a query that starts off like this:
with q1 as
(select a.V_ID, a.D_ID, a.C_ID,
case when a.percent > 0 THEN 'Y' ELSE 'N' end L_val,
a.C_val
from ab_a_table a
where a.C_ID = '00000003' -- '00000007' -- test values
and a.B_VAL = '6010001'
and a.Q = '11234567')
select case
when ... /* rest of query omitted */
When I try to run this, Oracle complains about that a table or view does not exist. But it highlights the ',' on line 3, rather than an actual table/view name:
case when a.percent > 0 THEN 'Y' ELSE 'N' end L_VAL,
*
ERROR at line 3:
ORA-00942: table or view does not exist
The rest of the query I omitted is rather long and complex - I'll sanitize and post it if necessary - for now I'll just say that this error only started when I added a third subquery that referenced q1. In fact, it seems I can remove any one of the 3 subqueries and the whole thing will execute (though with incorrect results) so it feels like I've hit some kind of Oracle error rather than a pure SQL error. It's also interesting that I can run the body of q1 as a stand-alone query and it has no problems when I do that. Only when I run the entire query does it complain about the comma after the case in q1.
Has anyone ever experienced this?
(using Oracle 10g).
Edit: Tried added AS keyword. Results are now:
case when a.perc_fault > 0 THEN 'Y' ELSE 'N' end AS L_VAL, a.C_VAL
*
ERROR at line 3:
ORA-00942: table or view does not exist
It looks like the asterisk is in the same position, but under the V because the word L_VAL has been shifted by 3 characters. Very strange...
Assuming you are hitting the Oracle bug(s) and can't patch the database, you could try moving the subquery to a function. Not entirely sure this will work, and assumes your PL/SQL version is in a package, or there's one available that can have a function added:
In the package spec:
type q1_rec is record(
d_id ab_a_table.v_id%TYPE,
v_id ab_a_table.d_id%TYPE,
c_id ab_a_table.c_id%TYPE,
l_val char(1),
c_val ab_a_table.c_val%TYPE);
type q1_arr is varray(9999); -- assuming you can pick a max size
function q1 return q1_arr pipelined;
pragma restrict_references(q1, wnds);
In the package body:
function q1 return q1_arr pipelined is
cursor c is
select a.V_ID, a.D_ID, a.C_ID,
case when a.percent > 0 THEN 'Y' ELSE 'N' end L_val,
a.C_val
from ab_a_table a
where a.C_ID = '00000003' -- '00000007' -- test values
and a.B_VAL = '6010001'
and a.Q = '11234567');
begin
for r in c loop
pipe row(r);
end loop;
end;
And then in your main query replace the subquery with table(q1()).
Using a ref cursor or nested table might be a bit neater but would need a table type built outside the package, which I guess you want to avoid based on your extra-object comment about using a view.
I don't know for sure if I'm experience Oracle bug 5130732 but it sure feels like it. Anyway, I rewrote the query like this:
select case ...
from
(select ...
from (select a.V_ID, a.D_ID, a.C_ID,
case when a.percent > 0 THEN 'Y' ELSE 'N' end L_val,
a.C_val
from ab_a_table a
where a.C_ID = '00000003' -- '00000007' -- test values
and a.B_VAL = '6010001'
and a.Q = '11234567') q1, <other tables>
where ...) subquery1,
(select ...
from (select a.V_ID, a.D_ID, a.C_ID,
case when a.percent > 0 THEN 'Y' ELSE 'N' end L_val,
a.C_val
from ab_a_table a
where a.C_ID = '00000003' -- '00000007' -- test values
and a.B_VAL = '6010001'
and a.Q = '11234567') q1, <other tables>
where ...) subquery2,
(select ...
from (select a.V_ID, a.D_ID, a.C_ID,
case when a.percent > 0 THEN 'Y' ELSE 'N' end L_val,
a.C_val
from ab_a_table a
where a.C_ID = '00000003' -- '00000007' -- test values
and a.B_VAL = '6010001'
and a.Q = '11234567') q1, <other tables>
where ...) subquery3, <other tables>
where....
Yes, I included a copy of q1 in every subquery that used it and everything works fine now. A real view would have worked too, but this was easier (politically, that is - no code-promotion requests to the environment where the analysis needs to be done, no meetings about late-added object in database, etc...)
UPDATE
And now that I've added the query to my PL/SQL script, Oracle gives me ORA-00600 [qcscpqbTxt], [600], which seems to be related to Oracle bug #5765958.... * sigh *... Can anyone suggest a workaround? I don't have metalink access (well, I might, through a DBA, if this can somehow get onto their radar).