How to declare and assign value to a variable before query? - sql

I have a complicated query that has to use a number called SubUnitRate. This variable comes from another table with special condition. in short we have:
DECLARE
SUBUNITRATE NUMBER;
BEGIN
SELECT NVL (NULLIF (CU.SUBUNITRATE, 0), 1)
INTO SUBUNITRATE
FROM CURRENCYS CU
JOIN ACCOUNTS ACC ON CU.ID = ACC.CURRENCY
WHERE ACC.ID = :ACCOUNTID;
END;
SELECT SUBUNITRATE * 100 FROM DUAL;
My goal is to acquire the result of(in simple case):
SELECT SUBUNITRATE * 100 FROM DUAL;
But how is that possible?

Assuming you want to use the value of SUBUNITRATE multiple times in the same query you could use the WITH clause:
with cte as (
select case
when CU.SUBUNITRATE = 0 then 1
else CU.SUBUNITRATE
end as SUBUNITRATE
FROM CURRENCYS CU
JOIN ACCOUNTS ACC ON CU.ID = ACC.CURRENCY
WHERE ACC.ID = :ACCOUNTID
)
select cte.SUBUNITRATE * 100
from cte;

A PL/SQL block cannot return the results of a query as a query. Instead, you can print the results out.
So, does this do what you want?
DECLARE
SUBUNITRATE NUMBER;
BEGIN
SELECT NVL(NULLIF(CU.SUBUNITRATE, 0), 1)
INTO SUBUNITRATE
FROM CURRENCYS CU JOIN
ACC
ON CU.ID = ACC.CURRENCY
WHERE ACC.ID = :ACCOUNTID;
DBMS_OUTPUT.PUT_LINE(SUBUNITRATE * 100)
END;

No need for PL. APC' solution, simplified and in a form you can use directly in your query (wherever you would say ... = subunitrate, say ... = (select sur from cte) instead - including the parentheses):
with cte_prelim as (select subunitrate from ... etc.),
cte (sur) as select case when subunit rate is null or subunitrate = 0 then 100
else subunitrate * 100 end from cte_prelim)
select... (your query where you need to use the value)

Related

MSSQL Check if a SELECT statement will have rows, if yes, then execute the same SELECT statement

I would like to execute a SELECT statement in a stored procedure, but before executing, I have to know if it will have rows. If not, I have to jump to a specified Label, and set an output.
My current method works just fine, but I will need more of these in several procedures, and I was wondering if I could do this without code repetition (and without Ctrl+C, Ctrl+V)
(It's just a sample code, not my real code)
IF EXISTS (
SELECT *
FROM sample s
INNER JOIN sample2 s2 ON s.Id = s2.sId
WHERE s2.number = #input
)
BEGIN
INSERT INTO #Temp
SELECT *
FROM sample s
INNER JOIN sample2 s2 ON s.Id = s2.sId
WHERE s2.number = #input
END
ELSE
BEGIN
SET #noresult = 1;
GOTO label;
END
Thank you for helping!
Why not just load the table and check afterwards?
INSERT INTO #Temp
SELECT *
FROM sample s JOIN
sample2 s2 ON s.Id = s2.sId
WHERE s2.number = #input;
IF NOT EXISTS (SELECT 1 FROM #temp)
BEGIN
SET #noresult = 1;
GOTO label;
END;
If #noresult is the same for all the tables, you can have just one if after loading a bunch of temp tables.

Error to postgresql

Pls Help!
count_rate :=
(
SELECT COUNT(trate.rid) AS count_rate
FROM tlot LEFT JOIN trate ON trate.ridlot = tlot.lid
GROUP BY tlot.lid
);
FULL:
CREATE FUNCTION editstatuswait()
RETURNS void
LANGUAGE plpgsql
AS $$
DECLARE count_rate INTEGER;
BEGIN
count_rate := (SELECT COUNT(trate.rid) AS count_rate FROM tlot LEFT JOIN trate ON trate.ridlot = tlot.lid GROUP BY tlot.lid);
IF (count_rate != 0) THEN
UPDATE tlot SET lstatus = 3
WHERE tlot.lexpirationdate < NOW()
AND tlot.lexpirationdate > NOW()-INTERVAL '24 hours' AND tlot.lstatus = 2;
ELSE
UPDATE tlot SET lstatus = 0
WHERE tlot.lexpirationdate < NOW()
AND tlot.lexpirationdate > NOW()-INTERVAL '24 hours' AND tlot.lstatus = 2;
END IF;
END;
$$;
ERROR: [21000] ERROR: more than one row returned by a subquery used as an expression Где: SQL statement
SELECT (SELECT COUNT(trate.rid) AS count_rate FROM tlot LEFT JOIN trate ON trate.ridlot = tlot.lid GROUP BY tlot.lid
I can not understand how to get rid of this error...
The solution will depend on what you are trying to achieve.
A variable can only contain a single value, so your attempt to store the result of a subselect that returns more than one row in count_rate is bound to fail.
You will have to come up with a subselect that returns at most one row (if it returns no row, NULL will be assigned to the variable).
If you are only interested in the first row (unlikely, since there is no ORDER BY), you could append LIMIT 1 to the query.
If you want only the count for a certain tlot.lid, you should use WHERE tlot.lid = ... instead of a GROUP BY.
If you want to process multiple results, you would use a construction like:
FOR count_rate IN SELECT ... LOOP
...
END LOOP;
Remove the GROUP BY:
count_rate := (SELECT COUNT(trate.rid) AS count_rate FROM tlot LEFT JOIN trate ON trate.ridlot = tlot.lid);
Of course, this may not do what you intend. It will at least fix the error.

Query Blocks with SELECT INTO

QUERY is self explanatory.
DECLARE
ID NUMBER (10);
ISFIRST NUMBER (1);
BEGIN
SELECT M.ID, M.ISFIRST
INTO ID, ISFIRST
FROM MERCHANT M
WHERE M.PHONE = :1;
IF (ISFIRST=1) THEN
SELECT * FROM CUSTOMER C WHERE C.ISFIRST=1 AND C.MERCHANTID = ID;
ELSE
SELECT * FROM CUSTOMER C WHERE C.ISFIRST=0 AND C.MERCHANTID = ID;
END IF;
END;
This query gives me "PLS-00428: an INTO clause is expected in this SELECT statement".
I need to select data from CUSTOMER table depending on MERCHANT.ISFIRST and MERCHANT.ID.
Any workaround or little explanation what went wrong would be appreciated.
PS: The problem is solved with UNION ALL statement. This question needs to be closed.
The SELECT statements have nothing wrong, except that you are not doing anything with them. I mean the last two. What do you want them to do? Produce a result that is going to be discarded? PL/SQL does not allow it.
Just to try if this is correct, you can pick a single field and do SELECT {aField} INTO {aVariable} instead of SELECT *.
IF( ISFIRST = 1 ) THEN
SELECT {aField} INTO {aVariable} FROM CUSTOMER C WHERE C.ISFIRST=1 AND C.MERCHANTID = ID;
ELSE
SELECT {aField} INTO {aVariable} FROM CUSTOMER C WHERE C.ISFIRST=0 AND C.MERCHANTID = ID;
END IF;
Don't forget to declare {aVariable}!!!
I think this query will do the same thing :
SELECT C.*
FROM CUSTOMER C
INNER JOIN MERCHANT M ON C.MERCHANTID = M.ID AND
C.ISFIRST = M.ISFIRST AND
M.PHONE = :1;

Replace no result

I have a query like this:
SELECT TV.Descrizione as TipoVers,
sum(ImportoVersamento) as ImpTot,
count(*) as N,
month(DataAllibramento) as Mese
FROM PROC_Versamento V
left outer join dbo.PROC_TipoVersamento TV
on V.IDTipoVersamento = TV.IDTipoVersamento
inner join dbo.PROC_PraticaRiscossione PR
on V.IDPraticaRiscossioneAssociata = PR.IDPratica
inner join dbo.DA_Avviso A
on PR.IDDatiAvviso = A.IDAvviso
where DataAllibramento between '2012-09-08' and '2012-09-17' and A.IDFornitura = 4
group by V.IDTipoVersamento,month(DataAllibramento),TV.Descrizione
order by V.IDTipoVersamento,month(DataAllibramento)
This query must always return something. If no result is produced a
0 0 0 0
row must be returned. How can I do this. Use a isnull for every selected field isn't usefull.
Use a derived table with one row and do a outer apply to your other table / query.
Here is a sample with a table variable #T in place of your real table.
declare #T table
(
ID int,
Grp int
)
select isnull(Q.MaxID, 0) as MaxID,
isnull(Q.C, 0) as C
from (select 1) as T(X)
outer apply (
-- Your query goes here
select max(ID) as MaxID,
count(*) as C
from #T
group by Grp
) as Q
order by Q.C -- order by goes to the outer query
That will make sure you have always at least one row in the output.
Something like this using your query.
select isnull(Q.TipoVers, '0') as TipoVers,
isnull(Q.ImpTot, 0) as ImpTot,
isnull(Q.N, 0) as N,
isnull(Q.Mese, 0) as Mese
from (select 1) as T(X)
outer apply (
SELECT TV.Descrizione as TipoVers,
sum(ImportoVersamento) as ImpTot,
count(*) as N,
month(DataAllibramento) as Mese,
V.IDTipoVersamento
FROM PROC_Versamento V
left outer join dbo.PROC_TipoVersamento TV
on V.IDTipoVersamento = TV.IDTipoVersamento
inner join dbo.PROC_PraticaRiscossione PR
on V.IDPraticaRiscossioneAssociata = PR.IDPratica
inner join dbo.DA_Avviso A
on PR.IDDatiAvviso = A.IDAvviso
where DataAllibramento between '2012-09-08' and '2012-09-17' and A.IDFornitura = 4
group by V.IDTipoVersamento,month(DataAllibramento),TV.Descrizione
) as Q
order by Q.IDTipoVersamento, Q.Mese
Use COALESCE. It returns the first non-null value. E.g.
SELECT COALESCE(TV.Desc, 0)...
Will return 0 if TV.DESC is NULL.
You can try:
with dat as (select TV.[Desc] as TipyDesc, sum(Import) as ToImp, count(*) as N, month(Date) as Mounth
from /*DATA SOURCE HERE*/ as TV
group by [Desc], month(Date))
select [TipyDesc], ToImp, N, Mounth from dat
union all
select '0', 0, 0, 0 where (select count (*) from dat)=0
That should do what you want...
If it's ok to include the "0 0 0 0" row in a result set that has data, you can use a union:
SELECT TV.Desc as TipyDesc,
sum(Import) as TotImp,
count(*) as N,
month(Date) as Mounth
...
UNION
SELECT
0,0,0,0
Depending on the database, you may need a FROM for the second SELECT. In Oracle, this would be "FROM DUAL". For MySQL, no FROM is necessary

How to use IF...ELSE sql statement with multiple selects?

Simple academic project. Simple procedure. If there is something already in Payments table - then take debt value from that table, if there are no values - take it from Tariff table. But why such conidtion doesn't work?
ALTER PROCEDURE dbo.GetDebt
(
#LoanId int
)
AS
IF NOT EXISTS (SELECT top 1 * FROM Payment WHERE LoanId = #LoanId)
BEGIN
SELECT (TotalLoan + ( ( TotalLoan / 100 ) * Interest)) as Debt FROM Loan L, Tariff T
WHERE L.TariffIf = L.TariffId
END
ELSE
BEGIN
SELECT MIN(Debt) as Debt FROM Loan L
RIGHT OUTER JOIN Payment P -- Joins are cool.
ON L.LoanId = P.LoanId
WHERE P.LoanId = #LoanId
END
If/Else is almost always the entirely wrong approach for sql code. It's hard to give you an exact example without knowing more about your tables, but you really want something more like this:
SELECT COALESCE(P.Debt, TotalLoan + ( ( TotalLoan / 100 ) * Interest)) as Debt
FROM Loan L
LEFT JOIN Tariff T ON T.LoanID = L.LoanID
LEFT JOIN (SELECT LoanID, Min(Debt) As Debt FROM Payment GROUP BY LoanID) P
WHERE L.LoanID = #LoanID
No If/Else required.
use BEGIN and END around your statements like so:
IF (SELECT count(*) FROM Payment WHERE LoanId = #LoanId) = 0
BEGIN
SELECT (TotalLoan + ( ( TotalLoan / 100 ) * Interest)) as Debt FROM Loan L
RIGHT OUTER JOIN Tariff -- TODO: Add correct ON clause here
WHERE L.LoanId = #LoanId
END
ELSE
BEGIN
SELECT MIN(Debt) as Debt FROM Loan L
RIGHT OUTER JOIN Payment P -- Joins are cool.
ON L.LoanId = P.LoanId
WHERE P.LoanId = #LoanId
END
Also note that you are missing an on clause for your right outer joins which will cause errors.
Also it might be more efficient to change
IF (SELECT count(*) FROM Payment WHERE LoanId = #LoanId) = 0
to
IF NOT EXISTS (SELECT * FROM Payment WHERE LoanId = #LoanId)
The keyword being "might"