I'm new to Oracle SQL DEVELOPER and I can't create a object inside a anonymous block.
In this page Oracle Database Object-Relational Developer's Guide
CREATE TYPE address_typ AS OBJECT (
street VARCHAR2(30),
city VARCHAR2(20),
state CHAR(2),
postal_code VARCHAR2(6) );
/
CREATE TYPE employee_typ AS OBJECT (
employee_id NUMBER(6),
first_name VARCHAR2(20),
last_name VARCHAR2(25),
email VARCHAR2(25),
phone_number VARCHAR2(20),
hire_date DATE,
job_id VARCHAR2(10),
salary NUMBER(8,2),
commission_pct NUMBER(2,2),
manager_id NUMBER(6),
department_id NUMBER(4),
address address_typ,
MAP MEMBER FUNCTION get_idno RETURN NUMBER,
MEMBER PROCEDURE display_address ( SELF IN OUT NOCOPY employee_typ ) );
/
CREATE TYPE BODY employee_typ AS
MAP MEMBER FUNCTION get_idno RETURN NUMBER IS
BEGIN
RETURN employee_id;
END;
MEMBER PROCEDURE display_address ( SELF IN OUT NOCOPY employee_typ ) IS
BEGIN
DBMS_OUTPUT.PUT_LINE(first_name || ' ' || last_name);
DBMS_OUTPUT.PUT_LINE(address.street);
DBMS_OUTPUT.PUT_LINE(address.city || ', ' || address.state || ' ' ||
address.postal_code);
END;
END;
/
CREATE TABLE employee_tab OF employee_typ;
And...
--Requires the top code
DECLARE
emp employee_typ; -- emp is atomically null
BEGIN
-- call the constructor for employee_typ
emp := employee_typ(315, 'Francis', 'Logan', 'FLOGAN',
'415.555.0100', '01-MAY-04', 'SA_MAN', 11000, .15, 101, 110,
address_typ('376 Mission', 'San Francisco', 'CA', '94222'));
DBMS_OUTPUT.PUT_LINE(emp.first_name || ' ' || emp.last_name); -- display details
emp.display_address(); -- call object method to display details
END;
/
There are 2 separated block of code (Example 3-1 Working With Object Types and Example 3-2 Declaring Objects in a PL/SQL Block). How can i put it all in one anonymous block?
Can't I create the object inside the scope of a anonymous block, use it and them remove it?
you cannot do direct DDL within an anonymous block. You should use dynamic SQL:
declare
l_sql varchar2(2000);
begin
l_sql := 'CREATE TYPE address_typ AS OBJECT (
street VARCHAR2(30),
city VARCHAR2(20),
state CHAR(2),
postal_code VARCHAR2(6) )';
execute immediate l_sql;
end;
So put each of your "create type" in a dynamic SQL and it should do what you want!
If you just need a temporary data structure you can use record type in block level declaration, is less readable and limitated but can works for some simple stuff
declare
type address_typ is record(
street VARCHAR2(30),
city VARCHAR2(20),
state CHAR(2),
postal_code VARCHAR2(6)
);
TYPE employee_typ is record (
employee_id NUMBER(6),
first_name VARCHAR2(20),
last_name VARCHAR2(25),
email VARCHAR2(25),
phone_number VARCHAR2(20),
hire_date DATE,
job_id VARCHAR2(10),
salary NUMBER(8,2),
commission_pct NUMBER(2,2),
manager_id NUMBER(6),
department_id NUMBER(4),
address address_typ/*,
MAP MEMBER FUNCTION get_idno RETURN NUMBER,
MEMBER PROCEDURE display_address ( SELF IN OUT NOCOPY employee_typ ) */
);
emp employee_typ;
begin
emp.employee_id := 315;
emp.first_name := 'Francis';
emp.last_name := 'Logan';
--and so on for the others fields
emp.address.street := '376 Mission';
emp.address.city := 'San Francisco';
emp.address.state := 'CA';
emp.address.postal_code := '94222';
DBMS_OUTPUT.PUT_LINE(emp.first_name || ' ' || emp.last_name);
--no member available so you have to print each field
DBMS_OUTPUT.PUT_LINE(emp.address.street);
DBMS_OUTPUT.PUT_LINE(emp.address.city || ', ' || emp.address.state || ' ' || emp.address.postal_code);
end;
Related
I wrote a procedure and I need to return the last generated ID when doing an insert in the database using the node-oracledb library.
I'm using DBMS_OUTPUT to return the value of a variable declared in the procedure, I can return the value output in SQL Developer, but I can't return it in the node-oracledb library.
Procedure:
create or replace PROCEDURE TESTE.CREATE_PATIENT(
p_DT_NASCIMENTO IN TESTE.PESSOA_FISICA.DT_NASCIMENTO%TYPE,
p_IE_SEXO IN TESTE.PESSOA_FISICA.IE_SEXO%TYPE,
p_NR_CPF IN TESTE.PESSOA_FISICA.NR_CPF%TYPE,
p_NR_REG_GERAL_ESTRANG IN TESTE.PESSOA_FISICA.NR_REG_GERAL_ESTRANG%TYPE,
p_NR_PASSAPORTE IN TESTE.PESSOA_FISICA.NR_PASSAPORTE%TYPE,
p_NM_PESSOA_FISICA IN TESTE.PESSOA_FISICA.NM_PESSOA_FISICA%TYPE,
p_CD_ACOMPANHANTE IN TESTE.PESSOA_FISICA_ACOMPANHANTE.CD_ACOMPANHANTE%TYPE,
p_NR_TELEFONE_CELULAR IN TESTE.PESSOA_FISICA.NR_TELEFONE_CELULAR%TYPE
)
IS
p_CD_PESSOA_FISICA NUMBER;
BEGIN
INSERT INTO TESTE.PESSOA_FISICA (DT_NASCIMENTO,IE_SEXO,NM_PESSOA_FISICA,NR_CPF,NR_PASSAPORTE,NR_REG_GERAL_ESTRANG,NR_TELEFONE_CELULAR) VALUES(p_DT_NASCIMENTO,p_IE_SEXO,p_NM_PESSOA_FISICA,p_NR_CPF,p_NR_PASSAPORTE,p_NR_REG_GERAL_ESTRANG,p_NR_TELEFONE_CELULAR) RETURNING CD_PESSOA_FISICA INTO p_CD_PESSOA_FISICA;
INSERT INTO TESTE.PESSOA_FISICA_ACOMPANHANTE (CD_PESSOA_FISICA, CD_ACOMPANHANTE) VALUES(p_CD_PESSOA_FISICA, p_CD_ACOMPANHANTE);
DBMS_OUTPUT.PUT_LINE(p_CD_PESSOA_FISICA);
COMMIT;
END;
Call the procedure:
SET SERVEROUTPUT ON
BEGIN CREATE_PATIENT(TO_DATE('1981-12-10', 'YYYY-MM-DD'), 'F', '25845685236', '12345645', '65432145', 'Marina Santos', '3', '+5511999999999'); END;
See the return:
Return In SQL Developer
I need to return this value with node-oracledb, has anyone experienced this and could help me out?
Don't use DBMS_OUTPUT; use an OUT parameter:
create PROCEDURE TESTE.CREATE_PATIENT(
p_DT_NASCIMENTO IN TESTE.PESSOA_FISICA.DT_NASCIMENTO%TYPE,
p_IE_SEXO IN TESTE.PESSOA_FISICA.IE_SEXO%TYPE,
p_NR_CPF IN TESTE.PESSOA_FISICA.NR_CPF%TYPE,
p_NR_REG_GERAL_ESTRANG IN TESTE.PESSOA_FISICA.NR_REG_GERAL_ESTRANG%TYPE,
p_NR_PASSAPORTE IN TESTE.PESSOA_FISICA.NR_PASSAPORTE%TYPE,
p_NM_PESSOA_FISICA IN TESTE.PESSOA_FISICA.NM_PESSOA_FISICA%TYPE,
p_CD_ACOMPANHANTE IN TESTE.PESSOA_FISICA_ACOMPANHANTE.CD_ACOMPANHANTE%TYPE,
p_NR_TELEFONE_CELULAR IN TESTE.PESSOA_FISICA.NR_TELEFONE_CELULAR%TYPE,
o_CD_PESSOA_FISICA OUT TESTE.PESSOA_FISICA.CD_PESSOA_FISICA%TYPE
)
IS
BEGIN
INSERT INTO TESTE.PESSOA_FISICA (
DT_NASCIMENTO,
IE_SEXO,
NM_PESSOA_FISICA,
NR_CPF,
NR_PASSAPORTE,
NR_REG_GERAL_ESTRANG,
NR_TELEFONE_CELULAR
) VALUES(
p_DT_NASCIMENTO,
p_IE_SEXO,
p_NM_PESSOA_FISICA,
p_NR_CPF,
p_NR_PASSAPORTE,
p_NR_REG_GERAL_ESTRANG,
p_NR_TELEFONE_CELULAR
)
RETURNING CD_PESSOA_FISICA INTO o_CD_PESSOA_FISICA;
INSERT INTO TESTE.PESSOA_FISICA_ACOMPANHANTE (
CD_PESSOA_FISICA,
CD_ACOMPANHANTE
) VALUES(
o_CD_PESSOA_FISICA,
p_CD_ACOMPANHANTE
);
END;
/
Note: Using COMMIT in the procedure means that you cannot chain multiple procedures and/or DML statements together in a single transaction and then use ROLLBACK on them all if a later one fails. You should remove the COMMIT and call it outside the procedure when the transaction is complete.
Then if you want to call the procedure and output the value in SQL Developer then you can use DBMS_OUTPUT in the block you call the procedure from:
SET SERVEROUTPUT ON
DECLARE
v_CD_PESSOA_FISICA TESTE.PESSOA_FISICA.CD_PESSOA_FISICA%TYPE;
BEGIN
CREATE_PATIENT(
TO_DATE('1981-12-10', 'YYYY-MM-DD'),
'F',
'25845685236',
'12345645',
'65432145',
'Marina Santos',
'3',
'+5511999999999',
v_CD_PESSOA_FISICA
);
DBMS_OUTPUT.PUT_LINE(v_CD_PESSOA_FISICA);
COMMIT;
END;
/
You can call the same procedure in Node and read the out parameter into a variable.
User(
id :number ,
name : string pk)
How can I get the biggest latest id to insert a new one? The objective is to take the previous id and add a value but the id is not auto-increment. I was thinking about making a query to get the latest id but I hope there may be other optimized suggestion
thanks
You can do what you want. But just because you can do something does not mean you should. I repeat you should use a serial or identity. #Stefanov.sm is corrent in a MVCC environment this is a very bad idea; because it is a virtual guarantee you will get a duplicate at some point.
One way to get this is to write a function to do the insert. This function get the MAX(ID) + an increment. It also handles the duplicate error and retries the insert if needed.
create or replace
function establish_user (user_name_in text )
returns integer
language plpgsql
as $$
declare
k_id_inc constant integer := 1;
l_id integer;
l_inserted boolean = false;
begin
select coalesce(max(id) + k_id_inc)
into l_id
from users;
while not l_inserted
loop
begin
insert into users (id, name)
values (l_id, user_name_in);
l_inserted = true;
exception
when others then
if sqlstate != '23505'
or sqlerrm not like '%users_id_uk%'
then
raise;
else
l_id = l_id + 1;
end if;
end;
end loop;
return l_id;
end;
$$;
See Demo here.
Is there any way we can use DateTime.TryParse in U-SQL WHERE condition?
I am creating a stored procedure in Azure U-SQL catalog.
Simple U-SQL script supports C# functions.
For stored procedure, the script is not adding any assembly reference to my custom C# code OR if adding it's not being used while calling the stored procedure.
Yes this is possible using in-line functions. This article shows how to do that. Here is a simple example:
#input = SELECT *
FROM (
VALUES
( (int)1, (string)"1/1/2017" ),
( (int)2, (string)"1/2/2017" ),
( (int)3, (string)"bad date" )
) AS x ( rn, someString );
// inline function example
#output =
SELECT *
FROM
(
SELECT *,
(
(Func<string, DateTime?>)
(dateString => // input_paramater
{
DateTime dateValue;
return DateTime.TryParse(dateString, out dateValue) ? (DateTime?)dateValue : (DateTime?)null;
}
)
) (someString) AS someDate
FROM #input
) AS x
WHERE someDate IS NOT NULL;
OUTPUT #output TO "/output/output.csv"
USING Outputters.Csv(quoting:false);
If you want to use the assembly you have created as a U-SQL Class project and registered then you can just reference it in the stored procedure using REFERENCE ASSEMBLY:
DROP PROCEDURE IF EXISTS dbo.usp_testCleanDate;
CREATE PROCEDURE dbo.usp_testCleanDate()
AS
BEGIN
REFERENCE ASSEMBLY USQLCSharpProject1;
#input = SELECT *
FROM (
VALUES
( (int)1, (string)"1/1/2017" ),
( (int)2, (string)"1/2/2017" ),
( (int)3, (string)"bad date" )
) AS x ( rn, someString );
#output =
SELECT *,
USQLCSharpProject1.Class1.tryParseDate(someString) AS x
FROM #input;
OUTPUT #output
TO "/output/output.csv"
USING Outputters.Csv(quoting : false);
END;
Using Function dt_TryParse_USQL
Using above code-behind and calling function. Function consumes a string and attempts to convert the string to a DateTime value using DateTime.TryParse. Using the Code-Behind above.
#employees =
SELECT * FROM
( VALUES
(1, "Noah", "2/16/2008"),
(2, "Sophia", "2/16/2008 12:15:12 PM"),
(3, "Liam", "16/02/2008 12:15:12"),
(4, "Amy", "2017-01-11T16:52:07"),
(5, "Justin", "")
) AS T(EmpID, EmpName, StartDate);
#result =
SELECT
EmpID,
EmpName,
ReferenceGuide_Examples.MyClass.dt_TryParse_USQL(StartDate) AS validated_StartDate
FROM #employees;
OUTPUT #result
TO "/Output/ReferenceGuide/DDL/Functions/dt_TryParse_USQL.csv"
USING Outputters.Csv(outputHeader: true);
https://msdn.microsoft.com/en-us/azure/data-lake-analytics/u-sql/u-sql-functions
Additional information: Click here
To answer this part of the question:
For stored procedure, the script is not adding any assembly reference to my custom C# code OR if adding it's not being used while calling the stored procedure.
U-SQL Functions and Procedures carry their own static context and do not inherit the static name context from the invocation context. That allows to understand the semantics of the function/procedure independently from where it is being called.
Thus in order to refer to assembly code, you will need to register the assembly in the catalog and explicitly reference it inside the procedure with REFERENCE ASSEMBLY. Code-behind is not meant to be used within a procedure (since the procedure is stored for future use).
Here is what I am working with:
CREATE OR REPLACE PROCEDURE SPROCLABPROCEDURE (Dept in VARCHAR2 DEFAULT
'Administration', 'Marketing', 'Purchasing', 'Human Resources', 'Shipping', 'IT',
'Public Relations', 'Sales', 'Executive', 'Finance', 'Accounting',
Mgr in VARCHAR2 DEFAULT NULL) AS
vManager Varchar2(30) := Mgr;
vDepartment Varchar2(30) := Dept;
I want the default values to be that entire list of strings. Later in my code, I want the option to be able to specify a few managers, or a few departments, or not input anything, so that the entire list is returned:
WHERE m.first_name ||' ' || m.last_name IN (vManager) -- NULL if I don't input anything
OR d.department_name IN (vDepartment) -- All departments if I don't input anything
When I call the function, and don't input anything, I'm expecting to return all possible rows. That is the desired effect.
In order to be able to pass a list of strings as a parameter you should change the parameter to a list.
CREATE OR REPLACE TYPE t_departments IS TABLE OF VARCHAR2(30);
And then create your procedure based on that new type:
CREATE OR REPLACE PROCEDURE SPROCLABPROCEDURE (Dept IN t_departments DEFAULT t_departments('Administration', 'Marketing', 'Purchasing', 'Human Resources', 'Shipping', 'IT','Public Relations', 'Sales', 'Executive', 'Finance', 'Accounting'), Mgr IN VARCHAR2 DEFAULT NULL) AS
Later you could use MEMBER OF instead of IN to check if a certain value is in the table:
...
WHERE d.department_name MEMBER OF Dept
...
Alternatively, if you can't change the type of the procedure parameters, you could just use variables:
CREATE OR REPLACE PROCEDURE SPROCLABPROCEDURE (Dept IN VARCHAR2 DEFAULT NULL, Mgr IN VARCHAR2 DEFAULT NULL) AS
TYPE t_departments IS TABLE OF VARCHAR2(30);
v_all_departments t_departments := t_departments('Administration', 'Marketing', 'Purchasing', 'Human Resources', 'Shipping', 'IT','Public Relations', 'Sales', 'Executive', 'Finance', 'Accounting');
vManager VARCHAR2(30);
vDepartment t_departments;
BEGIN
IF Dept IS NULL THEN
vDepartment := v_all_departments;
ELSE
vDepartment := t_departments(Dept);
END IF;
...
WHERE d.department_name MEMBER OF vDepartment
...
END;
I think something is unclear here...
First, this is a procedure - not a function, therefor it will not return a thing.
A varchar is not an array.
When you are getting or returning a varchar2 - there is only one possible string to pass back and forth... If you want to use a list - you should check the array types PL/SQL offers.
http://docs.oracle.com/cd/A97630_01/appdev.920/a96624/05_colls.htm
Third - if you have constant values, in a database the place for it is within a table, not as constants in a procedure.
I would recommend you to check out Temporary tables - they're a great data facilitator for "within session" communication for a list that you would like to return. http://asktom.oracle.com/pls/asktom/f?p=100:11:0::::P11_QUESTION_ID:5824706700346810645
These would be a great "in session"/"in connection" joins, like the one you described above (select ... where last_name in (select manager from temptable) for example).
Another thing - using the last name for identifying records in the DB is not the way you wanna go. You can use unique identifiers for that, and foreign keys. (if you are unfamiliar with the terms - What are database constraints? .
If I did not answer your question (or pointed you out to the answer) - please clarify your question, since I am not sure (probably as many others) what exactly you want to do (logically).
It's a quite unclear what you're asking but here is a minimal example how you can pass a list of values that can be used as a part of a SQL query to a PL/SQL subprogram:
-- SQL type so that can be used in SQL context (the select-statement)
create or replace type strlist_t is table of varchar2(4000);
/
create or replace procedure foo(p_owners in strlist_t default strlist_t('JANI')) is
begin
for i in (select * from all_tables
where owner in (select * from table(p_owners)))
loop
dbms_output.put_line(i.owner || '.' || i.table_name);
end loop;
end;
/
show errors
begin
-- define your own criteria
foo(strlist_t('SYS', 'JANI'));
-- use the default criteria
foo;
end;
/
Hope this helps !
How to extract the list of all email addresses available in a string, as a comma/semicolon separated list
SELECT dbo.getEmailAddresses('this is misc andrew#g.com')
--output andrew#g.com
SELECT dbo.getEmailAddresses('this is misc andrew#g.com and a medium text returning %John#acme.com')
--output andrew#g.com; John#acme.com
Here is a UDF for this. Correct this line SET #MailTempl='[A-Za-z0-9_.-] if you need additional (for example non-English symbols in an E-mail address). Here is a full list of allowed characters
CREATE FUNCTION [dbo].[getEmailAddresses] (#Str varchar(8000))
RETURNS varchar(8000) AS
BEGIN
declare #i int, #StartPos int,#AtPos int,#EndPos int;
declare #MailList varchar(8000);
declare #MailTempl varchar(100);
SET #MailList=NULL;
SET #MailTempl='[A-Za-z0-9_.-]'; --allowing symbols in e-mail not including #
SET #AtPos=PATINDEX('%'+#MailTempl+'#'+#MailTempl+'%',#Str)+1;
While #AtPos>1
begin
--go left
SET #i=#AtPos-1;
while (substring(#Str,#i,1) like #MailTempl) SET #i=#i-1;
SET #StartPos=#i+1;
--go right
SET #i=#AtPos+1;
while (substring(#Str,#i,1) like #MailTempl) SET #i=#i+1;
SET #EndPos=#i-1;
SET #MailList=isnull(#MailList+';','')+Substring(#Str,#StartPos,#EndPos-#StartPos+1);
--prepare for the next round
SET #Str=substring(#Str,#EndPos+1,LEn(#Str));
SET #AtPos=PATINDEX('%'+#MailTempl+'#'+#MailTempl+'%',#Str)+1;
end;
RETURN #MailList;
END
try this:
create function getEmailAddresses
(
#test varchar(max)
)
returns varchar(max)
As
BEGIN
declare #emaillist varchar(max)
--SET #test=' this is it by it a#b.com dsdkjl dsaldkj a#b.com dasdlk c#bn.com dsafhjkf anand#p.com d fdajf s#s.com .'
;WITH CTE as(
select reverse(left(reverse(left(#test,CHARINDEX('.com',#test)+3)),charindex(' ',reverse(left(#test,CHARINDEX('.com',#test)+3))))) as emailids,
right(#test,len(#test)-(CHARINDEX('.com',#test)+3)) rem
union all
select CASE WHEN len(rem)>2 then reverse(left(reverse(left(rem,CHARINDEX('.com',rem)+3)),charindex(' ',reverse(left(rem,CHARINDEX('.com',rem)+3))))) else 'a' end as emailids ,
CASE WHEN len(rem) > 2 then right(rem,len(rem)-(CHARINDEX('.com',rem)+3)) else 'a' end rem
from CTE where LEN(rem)>2
)
select #emaillist =STUFF((select ','+emailids from CTE for XML PATH('')),1,1,'')
return #emaillist
END
select dbo.getEmailAddresses('this is it by it a#b.com dsdkjl dsaldkj a#b.com dasdlk c#bn.com dsafhjkf anand#p.com d fdajf s#s.com .')
Try this
Declare #str varchar(max) = 'this is misc andrew#g.com and a medium text returning John#acme.com'
;With Cte AS(
SELECT
Items = Split.a.value('.', 'VARCHAR(100)')
FROM
(
SELECT
CAST('<X>' + REPLACE(#str, ' ' , '</X><X>') + '</X>' AS XML) AS Splitdata
) X
CROSS APPLY Splitdata.nodes('/X') Split(a) )
SELECT Email = STUFF((
SELECT ';'+ Items
FROM Cte
Where Items
LIKE '[A-Z0-9]%[#][A-Z]%[.][A-Z]%'
FOR XML PATH('')),1,1,'')
Result
EmailAddress
andrew#g.com;John#acme.com
N.B.~ You may need to do the following
a) As per your requirement , you need to make a TVF (Table valued function). You can refer the article on Split Function in Sql Server using Set base approach
b) The Email validation like clause is OK to work with but for more complex requirement you may have to enhance it.
c)If needed, you may have to clean the data before applying the filtration clause. e.g. %John#acme.com is an invalid email.so remove the "%" sign and then apply the filtering clause.
But as some one mentioned that, it is better not to do the string splitting/ manipulation much in Sql Server side and I agree to his point , so here is a C# code for achieving the same
static void Main(string[] args)
{
string str = "this is misc andrew#g.com and a medium text returning John#acme.com";
var result = GetValidEmails(str).Aggregate((a,b)=>a+";" + b);
Console.WriteLine(result);
Console.ReadKey();
}
private static List<string> GetValidEmails(string input)
{
List<string> lstValidEmails = new List<string>();
string regexPattern = #"^[A-Z0-9._%+-]+#[A-Z0-9.-]+\.[A-Z]{2,4}$";
foreach (string email in input.Split(' '))
{
if (new Regex(regexPattern, RegexOptions.IgnoreCase).IsMatch(email))
{
lstValidEmails.Add(email);
}
}
return lstValidEmails;
}
Hope this will be helpful.