Creating PL/SQL function from query - html

Little knowledge of PL/SQL here, so need a bit of help.
I have the a query that I need to turn into a function (let's call it reject_list), but not sure how to do it. This is what I have so far:
create or replace function reject_list(ayrc in varchar2,mcrc in varchar2)
return string
begin
select distinct
'<tr><td>'||cap.cap_uci2||'</td>
<td>'||cap.cap_stuc||'</td>
<td>'||cap.cap_mcrc||'</td>
<td>'||cap.cap_ayrc||'</td>
<td>'||stu.stu_fnm1||'</td>
<td>'||stu.stu_surn||'</td>
<td>'||cap.cap_stac||'</td>
<td>'||cap.cap_crtd||'</td></tr>'
from
intuit.srs_cap cap
,intuit.ins_stu stu
,intuit.srs_apf apf
where
cap.cap_stuc = stu.stu_code
and cap.cap_apfs = apf.apf_seqn
and cap.cap_stuc = apf.apf_stuc
and cap.cap_mcrc = &mcrc
and cap.cap_ayrc = &ayrc
and cap.cap_idrc in ('R','CR','CFR')
and apf.apf_recd <= to_date('1501'||substr(&ayrc,1,4),'DDMMYYYY');
end;
This doesn't run - can anyone help?
Thanks :)
EDIT: This query is one that is being run in an application already but we are trying to optimize it for speed. I am not certain whether a function is the best option, but we have, in another part of the application created a function to return simple counts which improved the speed exponentially. I need guidance more than just straightforward instructions on how to turn this into a function. If a view is the best option, for example, please could someone offer some guidance on how would be the best way to do this?
The object, therefore, is to be able to have a query stored on the server which allows me to enter the parameters and return the fields listed. To make this more complicated, one thing I did not mention before is that this needs to be formatted as an HTML table. I have now added the markup that would do this into the query above, and all the fields need to be concatenated.
Any help on this is greatly appreciated.

You may have to loop through the results of the select statement using a cursor. Please consider the following code as a guide. http://www.plsql-tutorial.com/plsql-cursors.htm. Also please consider prefixing your function parameters with P_ or something like that. It will make them easier to spot in the code.
FUNCTION YOUR_FUNCTION(p_ayrc in varchar2,p_mcrc in varchar2)
RETURN SYS_REFCURSOR
IS
THE_RESULT SYS_REFCURSOR;
BEGIN
OPEN THE_RESULT FOR
select distinct
cap.cap_uci2
,cap.cap_stuc
,cap.cap_mcrc
,cap.cap_ayrc
,stu.stu_fnm1
,stu.stu_surn
,cap.cap_stac
,cap.cap_crtd
from
intuit.srs_cap cap
,intuit.ins_stu stu
,intuit.srs_apf apf
where
cap.cap_stuc = stu.stu_code
and cap.cap_apfs = apf.apf_seqn
and cap.cap_stuc = apf.apf_stuc
and cap.cap_mcrc = p_mcrc
and cap.cap_ayrc = p_ayrc
and cap.cap_idrc in ('R','CR','CFR')
and apf.apf_recd <= to_date('1501'||substr(&ayrc,1,4),'DDMMYYYY');
RETURN THE_RESULT;
END;

Try something like this (you must only change varchar(256) on your column's types):
create type t_row as object
(
cap_uci2 varchar(256)
, cap.cap_stuc varchar(256)
, cap.cap_mcrc varchar(256)
, cap.cap_ayrc varchar(256)
, stu.stu_fnm1 varchar(256)
, stu.stu_surn varchar(256)
, cap.cap_stac varchar(256)
, cap.cap_crtd varchar(256)
);
/
create type t_tab is table of t_row;
/
create or replace function reject_list(ayrc varchar2, mcrc varchar2)
return t_tab pipelined
begin
for cur in
(
select distinct
cap.cap_uci2
, cap.cap_stuc
, cap.cap_mcrc
, cap.cap_ayrc
, stu.stu_fnm1
, stu.stu_surn
, cap.cap_stac
, cap.cap_crtd
from intuit.srs_cap cap
, intuit.ins_stu stu
, intuit.srs_apf apf
where cap.cap_stuc = stu.stu_code
and cap.cap_apfs = apf.apf_seqn
and cap.cap_stuc = apf.apf_stuc
and cap.cap_mcrc = mcrc
and cap.cap_ayrc = ayrc
and cap.cap_idrc in ('R', 'CR', 'CFR')
and apf.apf_recd <= to_date ('1501' || substr(ayrc, 1, 4), 'DDMMYYYY')
)
loop
pipe row(cur);
end loop;
end;
/
After that you can use the function this way (change 'xxx', 'yyy' on your param's values):
select *
from table(reject_list('xxx', 'yyy'));

Related

SQL Server: How to query data all rows as Json object into next to other columns?

I have data like this:
I want to query result like this:
Here is my code
SELECT
PML_CODE
,PML_NAME_ENG
,(
SELECT
PML_ID
,PML_NO
,PML_CODE
,PML_NAME_ENG
,PML_FORMULA
FROM DSP.PARAMET_LIST AS A WITH(NOLOCK)
WHERE A.PML_ID = B.PML_ID
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
) AS BR_OBJECT
FROM DSP.PARAMET_LIST AS B WITH(NOLOCK)
My code works for what I want, but I want to know if there is a better, faster way to write this query?
Next time please do not post pictures, but rather try to create some DDL, fill it with sample data and state your own attempts and the expected output. This makes it easier for us to understand and to answer your issue.
You can try it like this:
DECLARE #tbl TABLE(PML_ID BIGINT, PML_NO INT, PML_CODE VARCHAR(10), PML_NAME_ENG VARCHAR(10), PML_FORMULA VARCHAR(10));
INSERT INTO #tbl VALUES
(2017102600050,1,'KHR','Riel','01')
,(2017102600051,2,'USD','Dollar','02')
,(2017102600052,3,'THB','Bath','05')
SELECT
PML_CODE
,PML_NAME_ENG
,BR_OBJECT
FROM #tbl
CROSS APPLY(
SELECT
(
SELECT
PML_ID
,PML_NO
,PML_CODE
,PML_NAME_ENG
,PML_FORMULA
FOR JSON PATH, WITHOUT_ARRAY_WRAPPER
)) AS A(BR_OBJECT);
The big difference to your own approach is that I use a CROSS APPLY using the columns we have already instead of calling a correlated sub-query.
You can just concatenate the values. Be sure to cast the integers and to handle the NULL values. For example, if there is NULL value for column, there can be two cases - ignore the property or add the property with null, right?
For SQL Server 2016 SP1+ and later you can use FOR JSON. Basically, you should end up with something like this:
DECLARE #DataSource TABLE
(
[PML_ID] VARCHAR(64)
,[PML_NO] INT
,[PML_CODE] VARCHAR(3)
,[PML_NAME_ENG] NVARCHAR(32)
,[PML_FORMULA] VARCHAR(2)
);
INSERT INTO #DataSource ([PML_ID], [PML_NO], [PML_CODE], [PML_NAME_ENG], [PML_FORMULA])
VALUES ('201710260000000050', 1, 'KHR', 'Riel', 01)
,('201710260000000051', 2, 'USD', 'Dollar', 02)
,('201710260000000052', 3, 'THB', 'Bath', 05);
SELECT [PML_CODE]
,[PML_NAME_ENG]
,'{"PML_ID":'+ [PML_ID] +',"PML_NO":'+ CAST([PML_NO] AS VARCHAR(12)) +',"PML_CODE":'+ [PML_CODE] +',"PML_NAME_ENG":'+ [PML_NAME_ENG] +',"PML_FORMULA":'+ [PML_FORMULA] +'}' AS [BR_OBJECT]
FROM #DataSource;
-- SQL Server 2016 SP1 and latter
SELECT DS1.[PML_CODE]
,DS1.[PML_NAME_ENG]
,DS.[BR_OBJECT]
FROM #DataSource DS1
CROSS APPLY
(
SELECT *
FROM #DataSource DS2
WHERE DS1.[PML_CODE] = DS2.[PML_CODE]
AND DS2.[PML_NAME_ENG] = DS2.[PML_NAME_ENG]
FOR JSON AUTO
) DS ([BR_OBJECT]);

Stored Procedure With Function giving me errors in Oracle

I have stored procedure and function and I am calling the function in the stored procedure in ORACLE.The function CalculateIncomeTax is what is giving me errors.In MSSQL,this type of update is possible because I have done it before.I called the function in the stored procedure.When I read around the answer I get is to use a package before I cannot use a function to update a table from another table.Please if you have any idea,tell me.The error I get is
table string.string is mutating, trigger/function may not see it
Cause: A trigger (or a user defined plsql function that is referenced in this statement) attempted to look at (or modify) a table that was in the middle of being modified by the statement which fired it.
Action: Rewrite the trigger (or function) so it does not read that table.
This is function
CREATE OR REPLACE function CalculateIncomeTax(periodId NVARCHAR2,
employeeId NVARCHAR2, taxableIncome NUMBER)return NUMBER
AS
IncomeTax NUMBER (18,4);Taxable NUMBER(18,4);
BEGIN
SELECT SUM(CASE WHEN (taxableIncome > T.TAX_CUMMULATIVE_AMOUNT)
THEN (taxableIncome - T.TAX_CUMMULATIVE_AMOUNT)* T.TAX_PERCENTAGE/ 100
ELSE 0.00 END ) INTO IncomeTax
FROM TAX_LAW T JOIN PAY_GROUP P ON P.PAY_FORMULA_ID =T.TAX_FORMULA_ID
JOIN PAYROLL_MASTER PP ON P.PAY_CODE =PP.PAY_PAY_GROUP_CODE
WHERE PP.PAY_EMPLOYEE_ID = employeeId AND PP.PAY_PERIOD_CODE = periodId;
if IncomeTax IS NULL THEN IncomeTax :=0;
end if;
return IncomeTax;
end;/
This is the stored procedure
CREATE OR REPLACE PROCEDURE PROCESSPAYROLLMASTER (periodcode
VARCHAR2) AS BEGIN
INSERT INTO PAYROLL_MASTER
(
PAY_PAYROLL_ID,PAY_EMPLOYEE_ID ,PAY_EMPLOYEE_NAME,PAY_SALARY_GRADE_CODE
,PAY_SALARY_NOTCH_CODE,PAY_BASIC_SALARY,PAY_TOTAL_ALLOWANCE
,PAY_TOTAL_CASH_BENEFIT,PAY_MEDICAL_BENEFIT,PAY_TOTAL_BENEFIT
,PAY_TOTAL_DEDUCTION,PAY_GROSS_SALARY,PAY_TOTAL_TAXABLE,PAY_INCOME_TAX
,PAY_TAXABLE,PAY_PERIOD_CODE,PAY_BANK_CODE,PAY_BANK_NAME,PAY_BANK_ACCOUNT_NO
,PAY_PAY_GROUP_CODE )
SELECT
1,
E.EMP_ID AS PAY_EMPLOYEE_ID ,
E.EMP_FIRST_NAME || ' ' || E.EMP_LAST_NAME AS PAY_EMPLOYEE_NAME,
E.EMP_RANK_CODE,
'CODE',
(SC.SAL_MINIMUM_AMOUNT+( SN.SAL_SALARY_PERCENTAGE *
SC.SAL_MINIMUM_AMOUNT)/100) AS PAY_BASIC_SALARY,
0,
0,
0,
0,
0,
0,
0,
0,
0,
periodcode,
'BANKCODE',
'BANKNAME',
'BANKNUMBER',
'GENERAL'
FROM EMPLOYEE E
LEFT JOIN SALARY_SCALE SC ON SC.SAL_RANK_CODE = E.EMP_RANK_CODE
LEFT JOIN SALARY_NOTCH SN ON SC.SAL_ID = SN.SAL_SALARYSCALE_ID
WHERE E.EMP_RANK_CODE = SC.SAL_RANK_CODE AND E.EMP_STATUS=2;
CALCULATEALLOWANCE(v_payrollId,periodcode);
CALCULATECASHBENEFITS(v_payrollId,periodcode);
CALCULATEDEDUCTIONS(v_payrollId,periodcode);
-- UPDATE PAYROLL PAY_INCOME_TAX
UPDATE PAYROLL_MASTER PM SET PM.PAY_INCOME_TAX = CalculateIncomeTax(PM.PAY_PERIOD_CODE,PM.PAY_EMPLOYEE_ID,PM.PAY_TOTAL_TAXABLE) WHERE PM.PAY_PAYROLL_ID = v_payrollId;
UPDATE PAYROLL_PROCESS set PAY_CANCELLED = 1 WHERE PAY_PAY_GROUP_CODE='GENERAL' AND PAY_PERIOD_CODE=periodcode
AND PAY_ID<>v_payrollId;
COMMIT;
END ;
/
The function is querying the same table you are updating, which is what the error is reporting. As it happens you are not changing the value of the column you're querying, but Oracle doesn't check to that level - not least because there could be, for instance, a trigger that has less obvious side-effects.
The best solution really would be to not have to update at all, and to calculate and set all the value as part of the original insert, by joining to all the relevant tables. But you are already calling other procedures which are, presumably, updating some of the values you're inserting as zeros, including pay_total_taxable.
Unless you're able to reevaluate those as well, you may be stuck with doing a further update. In which case, you could remove the reference to the payroll_master table from the function and instead pass in the relevant data.
I think this is equivalent, though with out the table structures, sample data and what the other procedures are doing it's hard to be sure (so this is untested, obviously):
create or replace function calculateincometax (
p_periodid nvarchar2,
p_employeeid nvarchar2,
p_paypaygroupcode payroll_master.pay_pay_group_code%type,
p_taxableincome number
) return number as
l_incometax number(18, 4);
begin
select coalesce(sum(case when p_taxableincome > t.tax_cummulative_amount
then (taxableincome - t.tax_cummulative_amount) * t.tax_percentage / 100
else 0 end), 0)
into l_incometax
from tax_law t
join pay_group p
on p.pay_formula_id = t.tax_formula_id
where p.pay_code = p_paypaygroupcode;
return l_incometax;
end;
/
and then include the extra argument in your call:
update payroll_master pm
set pm.pay_income_tax = calculateincometax(pm.pay_period_code, pm.pay_employee_id,
pm.pay_pay_group_code, pm.pay_total_taxable)
where pm.pay_payroll_id = v_payrollid;
Although v_payrollid isn't defined in what you've shown, so even that isn't entirely clear.
I've also modified the function argument and local variable names with prefixes to remove potential ambiguity (which you seem to do by removing underscores from the names), removed the unused variable, and added a coalesce() call in place of the separate null check. Those things aren't directly relevant to the approach though.

Mysql function not returning the expected result

As I have mentioned in my question title below Mysql function returns null always :
CREATE DEFINER=`root`#`localhost` FUNCTION `nextCode`(tbl_name VARCHAR(30), prv_code VARCHAR(30)) RETURNS varchar(30) CHARSET utf8
READS SQL DATA
BEGIN
DECLARE nxtCode VARCHAR(30);
SELECT ds.prefix, ds.suffix, ds.is_used, ds.next_number, CHAR_LENGTH(ds.pattern)
INTO #prefix, #suffix, #isUsed, #nxtNum, #pLength
FROM ths_inventory.doc_sequnce ds WHERE ds.`table_name` = tbl_name;
SET nxtCode = CONCAT(#prefix, LPAD((CASE WHEN #isUsed
THEN
(ExtractNumber(prv_code) + 1)
ELSE
(#nxtNum)
END
), #pLength,'0'), #suffix);
RETURN nxtCode;
END
But once I change the below line :
CONCAT(#prefix, LPAD((CASE WHEN #isUsed
THEN
(ExtractNumber(prv_code) + 1)
ELSE
(#nxtNum)
END
), #pLength,'0'), #suffix)
To some static values like below :
CONCAT('PR', LPAD((CASE WHEN true
THEN
(ExtractNumber(prv_code) + 1)
ELSE
(5)
END
), 6,'0'), '')
function start returning values accordingly.
Here is how I call my function :
nextCode('item','PR000002');
UPDATE:
I defined this function to get the next possible code for Item table :
According to my requirement the next possible code should be PR000000005.
But instead of getting it, I always get empty result .
SELECT nextCode('item',(SELECT `code` FROM item ORDER BY id DESC LIMIT 1)) AS next_code;
Any help would be appreciable.
Run a query that uses the function, and then...
SELECT #prefix, #suffix, #isUsed, #nxtNum, #pLength;
...to inspect the values. The # prefix means these are user-defined variables, so they have session scope, not program scope, and will still hold their values after the funcfion executes.
This should help pinpoint your problem.
But, you have two other problems you will need to solve after that.
SELECT ... INTO does not set the target variables when no row matches the query, so once you fix your issue, you will get very wrong results if you pass in arguments that don't match anything.
To resolve this, the function needs to set all these variables to null before the SELECT ... INTO query.
SET #prefix = NULL, #suffix = NULL, #isUsed = NULL, #nxtNum = NULL, #pLength = NULL;
See https://dba.stackexchange.com/a/35207/11651.
Also, your function does not handle concurrency, so two threads trying to find the "next" value for the same table, concurrently, will produce the same answer, so you will need to insure that your code handles this correctly with unique constraints and transactions or other appropriate locks.

MySql Stored Procedure error in WHERE condition

I'm trying to write my first mySql stored procedure and keep on getting an error from the server that I am unable to understand, hope someone will be able to help me fixing it.
What I am doing
I collect some parameters from social networks, and I need to save this data in two different tables. I know that the table schema might not be optimal, but this is something I cannot change at the moment.
The idea is that I call the stored procedure from my server-side code passing in article ID and some other parameters, and the procedure:
Updates the "articles" table
Inserts anew record into the "popularity" tables with some values that are the result of the previous UPDATE
This is the stored procedure I wrote
BEGIN
UPDATE
articles2
SET
fb_shares = n_shares,
fb_comments = n_comments,
fb_reactions = n_reactions,
tw_tweets = #tweets :=(tw_tweets + n_tweets),
tw_retweets = #retweets :=(tw_retweets + n_retweets),
tw_favorites = #favorites :=(tw_favorites + n_favorites),
tw_reach = #reach :=(tw_reach + n_reach),
tw_since_id = n_since_id,
popularity = #popularity :=(
(n_shares * fb_shares_weight) +(
n_comments * fb_comments_weight
) +(
n_reactions * fb_reactions_weight
) +(#tweets * tw_tweets_weight) +(#retweets * tw_retweets_weight) +(
#favorites * tw_favorites_weight
) +(#reach * tw_reach_weight)
),
popularity_updated =(popularity_updated + 1)
WHERE
id = n_id ;
INSERT
INTO
popularity(
article_id,
added,
popularity,
tw_tweets,
tw_reach,
tw_favorites,
tw_retweets,
tw_since_id,
fb_shares,
fb_comments,
fb_reactions
)
VALUES(
n_id,
NOW(), #popularity, #tweets, #reach, #favorites, #retweets, n_since_id, n_shares, n_comments, n_reactions) ;
END
I keep getting an error #1416 - Cannot get geometry object from data you send to the GEOMETRY field and the INSERT is never performed. I suppose that the variables assignment is wrong, but cannot understand how to fix it.
As said, I never wrote a stored procedure before, and since that line looks correct to me, I really cannot understand what's wrong. I cannot exclude that I am trying to do something that should not be done with a stored procedure, but the few examples that I found online makes me think this should be correct...
Thank you in advance,
Simone
Edit:
I got rid of that error, but still the INSERT is not performed... here is the updated stored procedure:
BEGIN
SET #tweets := 0, #retweets := 0, #favorites := 0, #reach := 0, #popularity := 0;
UPDATE
articles2
SET
fb_shares = n_shares,
fb_comments = n_comments,
fb_reactions = n_reactions,
tw_tweets = #tweets :=(tw_tweets + n_tweets),
tw_retweets = #retweets :=(tw_retweets + n_retweets),
tw_favorites = #favorites :=(tw_favorites + n_favorites),
tw_reach = #reach :=(tw_reach + n_reach),
tw_since_id = n_since_id,
popularity = #popularity :=(
(n_shares * fb_shares_weight) +(
n_comments * fb_comments_weight
) +(
n_reactions * fb_reactions_weight
) +(#tweets * tw_tweets_weight) +(#retweets * tw_retweets_weight) +(
#favorites * tw_favorites_weight
) +(#reach * tw_reach_weight)
),
popularity_updated =(popularity_updated + 1)
WHERE
id = n_id ;
SELECT #tweets, #retweets, #favorites, #reach, #popularity;
INSERT
INTO
popularity(
article_id,
added,
popularity,
tw_tweets,
tw_reach,
tw_favorites,
tw_retweets,
tw_since_id,
fb_shares,
fb_comments,
fb_reactions
)
VALUES(
n_id,
NOW(), #popularity, #tweets, #reach, #favorites, #retweets, n_since_id, n_shares, n_comments, n_reactions) ;
END
Check the definition of the popularity table for a field defined with data type GEOMETRY and change to appropriate type.

Inserting images from file path -- Not getting value on the select statement

DECLARE #imgString varchar(800)
DECLARE #insertString varchar(3000)
DECLARE #imgNumber int
Declare #imgName varchar(100)
SET #imgNumber = 1
WHILE #imgNumber<> 101
BEGIN
SET #imgName = 'SELECT (items) FROM dbo.building_piclink'
SET #imgString = 'C:\Documents and Settings\Administrator\Desktop\photos\' + #imgName
SET #insertString = 'INSERT INTO dbo.building__ATTACH (DATA)
SELECT * FROM OPENROWSET(BULK N''' + #imgString + ''', SINGLE_BLOB) as tempImg'
SET #imgNumber = #imgNumber + 1
END
GO
I am having problems with the #imgName. I can't figure out how to get the value from the select statement not the (items) like below:
C:\Documents and Settings\Administrator\Desktop\photos\SELECT (items) FROM dbo.building_piclink
Thank you!
Your code has several problems:
1) You're selecting a file name from the view - but what if that view contains more than one entry?? Which filename are you selecting?? Your current code first of all doesn't work at all the way it is, and even if it were working - you're still potentially selecting hundreds of filenames into a single variable - which of course won't work....
So you'll need to fix this here first:
SET #imgName = 'SELECT (items) FROM dbo.building_piclink'
First of all - loose the single quotes:
SELECT #imgName = (items) FROM dbo.building_piclink
But now - do you have a unique ID that you can select for? Or do you want to get just the first entry (whatever that is) ??
So either you need:
SELECT #imgName = ImageFileName FROM dbo.building_piclink WHERE ..........
and fill in that WHERE clause with a condition that guarantees to return just a single row, or use TOP 1:
SELECT TOP (1) #imgName = ImageFileName FROM dbo.building_piclink
In that case - you'll just get exactly one filename - if you don't specify an ORDER BY, then there's no guarantee what you'll get - maybe you'll want to add a ORDER BY DueDate or something to prioritize which file names you get first.
2) Your code for loading the image data is non workable, either - what you need to do is build up the SQL statement as a string, and then execute it (called dynamic SQL) - something like this:
SET #imgString = 'C:\Documents and Settings\Administrator\Desktop\photos\' + #imgName
SET #insertString =
'INSERT INTO dbo.building__ATTACH (DATA)
SELECT * FROM OPENROWSET(BULK N''' + #imgString + ''', SINGLE_BLOB) as tempImg'
EXEC(#insertString) -- actually execute your SQL statement!
With these two fixes, you should be on the way to get this thing working