What is wrong with my SQL Query for the Cursor - oracle

Here is what I am supposed to do. Assume that the tables are created, and all the columns are correctly named
"Using stored procedures and cursors, display the location (including street, zip code, city and country) of the managers with job id of either IT_PROG or SA_MAN and with salary greater than 3000".
Here is the code I have written so far but the sql statement for the cursor doesn't seem to want to work. For the DEPARTMENTS Table the FK's are MANAGER_ID and LOCATION_ID, for the EMPLOYEES Table the FK is JOB_ID and the LOCATIONS table has no FK. All the primary keys are set
Here is the code:
create or replace procedure mgtLocation
is
cursor getLoc is
select LOCATIONS.STREET_ADDRESS, LOCATIONS.POSTAL_CODE, LOCATIONS.CITY,
LOCATIONS.COUNTRY, LOCATIONS.LOCATIONS_ID, LOCATIONS.LOCATIONS_ID
from LOCATIONS
inner join DEPARTMENTS on DEPARTMENTS.MANAGER_ID = EMPLOYEES.EMPLOYEE_ID
inner join LOCATIONS on LOCATIONS.LOCATION_ID = DEPARTMENTS.LOCATION_ID
where EMPLOYEES.Job_ID in (select Job_ID from EMPLOYEES where Job_ID = 'IT_PROG' or Job_ID = 'SA_MAN' and SALARY > 3000);
EmpLoc getLoc%rowtype;
begin
dbms_output.put_line('=================');
open getLoc;
loop
fetch getLoc into EmpLoc;
EXIT WHEN getLoc%NOTFOUND;
dbms_output.put_line('Street: ' || EmpLoc.STREET_ADDRESS ||
' Zip Code: ' || EmpLoc.POSTAL_CODE ||
' City: ' || EmpLoc.CITY ||
' Country: ' || EmpLoc.COUNTRY);
end loop;
dbms_output.put_line('=================');
close getLoc;
end;
/
execute mgtLocation;
I get an error for the inner joins and I cannot seem to figure out how to fix them in order for this to work.

You could try this:
cursor getLoc is
select LOCATIONS.STREET_ADDRESS,
LOCATIONS.POSTAL_CODE, LOCATIONS.CITY,
LOCATIONS.COUNTRY, LOCATIONS.LOCATIONS_ID,
LOCATIONS.LOCATIONS_ID
from LOCATIONS
inner join DEPARTMENTS on
DEPARTMENTS.LOCATION_ID = LOCATIONS.LOCATION_ID
inner join EMPLOYEES on
EMPLOYEES.EMPLOYEE_ID =
DEPARTMENTS.MANAGER_ID
where (EMPLOYEES.Job_ID = 'IT_PROG' or
EMPLOYEES.Job_ID = 'SA_MAN')
and SALARY > 3000;

Related

How to insert rows from one Snowflake table to another without hardcoding the column names

I have a Snowflake table A as below:
CREATE or replace table A
(
Address VARCHAR(50),
Zip VARCHAR(50));
insert into A (Address, Zip) values
('ABC', '20987'),
('XYZ', '20989'),
('CBZ', '20980');
I have an empty Snowflake table B as below
CREATE or replace table B
(
Address VARCHAR(50),
Zip VARCHAR(50),
State VARCHAR(50));
Now I want to insert rows from Table A to Table B exactly like below
insert into B (Address, Zip)
select Address, Zip from A
but instead of hardcoding the column names, I want to get it from information_schema as below
select COLUMN_NAME
from db.information_schema.columns
where table_schema = 'schema' and TABLE_NAME = 'A'
If you're using an insert from a select and don't want to list out the column names, just remove the column names and put NULL where there's nothing else to put from the select.
Change this:
insert into B (Address, Zip)
select Address, Zip from A
To this:
insert into B
select Address, Zip, NULL from A
If you have a huge number of columns and want a labor-saving approach, the age-old DBA technique of a SQL generator will work fine. Something like this:
select 'insert into b select ' || (select listagg(COLUMN_NAME,',')
within group (order by ORDINAL_POSITION) COLS
from information_schema.columns
where TABLE_SCHEMA = 'PUBLIC' and TABLE_NAME = 'A') || ' from A;'
;
You can then add nulls where required because table B has columns table A does not, or the generator can get more sophisticated and map the columns between the table filling in nulls where a column exists in B but not A.
Actual code using Snowflake Scripting and parameters. Matching based on column names on target and source. The generated code is executed using EXECUTE IMMEDIATE and actual statement is returned for debug.
DECLARE
trg_table_schema TEXT := 'PUBLIC';
trg_table_name TEXT := 'B';
src_table_schema TEXT := 'PUBLIC';
src_table_name TEXT := 'A';
sql TEXT;
BEGIN
WITH trg AS (
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = :trg_table_name
AND TABLE_SCHEMA = :trg_table_schema
), src AS (
SELECT *
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = :src_table_name
AND TABLE_SCHEMA = :src_table_schema
)
SELECT
REPLACE(REPLACE(REPLACE(REPLACE(REPLACE(
'INSERT INTO <trg_table_schema>.<trg_table_name>(<col_list>)
SELECT <col_list> FROM <src_table_schema>.<src_table_name>;'
, '<trg_table_schema>', :trg_table_schema)
, '<trg_table_name>', :trg_table_name)
, '<col_list>', LISTAGG(src.COLUMN_NAME, ','))
, '<src_table_schema>', :src_table_schema)
, '<src_table_name>', :src_table_name)
INTO :sql
FROM trg
JOIN src ON trg.COLUMN_NAME = src.COLUMN_NAME;
EXECUTE IMMEDIATE :sql;
RETURN :sql;
END;
Output:
The key part is main query which works by taking matching(by name) columns from source and target tables.
INSERT INTO <trg_table_schema>.<trg_table_name>(<col_list>)
SELECT <col_list> FROM <src_table_schema>.<src_table_name>;
You can write a stored procedure to generate and execute the INSERT statement.
https://docs.snowflake.com/en/sql-reference/stored-procedures-usage.html#stored-procedure-considerations
https://docs.snowflake.com/en/sql-reference/sql/execute-immediate.html

Need help in output for Oracle PLSQL Procedure

Am currently working on an oracle PL SQL procedure to list the project numbers, titles and names of employees who work on a project.
I am able to write a procedure that is able to get this info. However, I can only draw one output at a time as such:
1001 Computation
Alvin
Peter
How can I change my code to output all of the entries at the same time while printing them as such:
[Fragment Example][Showing only 1st 3 entries]
1001 Computation: Alvin, Peter
1002 Study methods: Bob, Robert
1003 Racing car: Robert
[Current Code]
create or replace procedure PROJECTGROUPS(projectid IN WorksOn.P#%TYPE)
is
PID Project.P#%TYPE;
PNAME Project.PTitle%TYPE;
ENAME Employee.Name%TYPE;
CURSOR query is
select Employee.Name from Employee
left outer join WorksOn On Employee.E# = WorksOn.E#
where WorksOn.P# = projectid
order by Employee.Name ASC
fetch first 20 rows only;
--
--
begin
select P#, PTitle into PID, PNAME from project where project.p# = projectid;
DBMS_OUTPUT.PUT_LINE(PID || ' ' || PNAME);
--
open query;
loop
fetch query into ENAME;
if query%NOTFOUND then exit;
end if;
DBMS_OUTPUT.PUT_LINE(ENAME);
end loop;
close query;
end PROJECTGROUPS;
You can directly use it in a single query and loop through it as follows:
CREATE OR REPLACE PROCEDURE PROJECTGROUPS (
PROJECTID IN WORKSON.P#%TYPE
) IS
BEGIN
FOR I IN (
SELECT PROJECT_NAME,
PTITLE,
LISTAGG(EMP_NAME,
',') WITHIN GROUP(
ORDER BY EMP_NAME
) AS EMP_NAMES
FROM (
SELECT P.P# PROJECT_NAME,
P.PTITLE,
E.EMPLOYEE.NAME EMP_NAME,
ROW_NUMBER() OVER(
PARTITION BY P.P#
ORDER BY E.NAME
) AS RN
FROM PROJECT P
JOIN WORKSON W
ON W.P# = P.P#
JOIN EMPLOYEE E
ON E.E# = W.E#
WHERE P.P# = PROJECTID
)
WHERE RN <= 20
GROUP BY PROJECT_NAME,
PTITLE
) LOOP
DBMS_OUTPUT.PUT_LINE(I.PROJECT_NAME
|| ' '
|| I.PTITLE
|| ' : '
|| I.EMP_NAMES);
END LOOP;
END PROJECTGROUPS;
Also, your query is not actually outer joined as you have used the condition in the WHERE clause.
left outer join WorksOn On Employee.E# = WorksOn.E# -- you want outer join
where WorksOn.P# = projected -- but the outer join is converted to inner join

pl/sql SQL Statement ignored and missing right parenthesis

this code has to sum salary of employees of department_id 100.so it gives this error "missing right parenthesis"
DECLARE
v_department_name VARCHAR(100);
v_department_manager VARCHAR(100);
v_totalsalary NUMBER(30);
BEGIN
SELECT departments.department_name, concat(employees.first_name,
employees.last_name), employees.salary INTO v_department_name,
v_department_manager, v_totalsalary
FROM employees JOIN departments ON employees.department_id =
departments.department_id
WHERE employees.salary = (SELECT departments.department_id,
sum(employees.salary)
FROM EMPLOYEES
where departments.department_id=100
GROUP BY DEPARTMENT_ID
ORDER BY DEPARTMENT_ID);
DBMS_OUTPUT.PUT_LINE ('Department Name is : ' || v_department_name || 'And
Department Manager is : ' || v_department_manager || 'Total Amount of Salary
is : ' || v_totalsalary );
END;
The "missing right parenthesis" error is clearly caused by the ORDER BY clause in the subquery (where it is not allowed).
Once you clear that error, you get the "too many values" error, because you are comparing a single variable (salary) to the output from a subquery that returns two values (department_id AND sum(salary)). Not sure why you thought you need to include the department_id in the SELECT clause of the subquery.
When you include error messages in your question, include the full text of the message (which shows the line number and position at which the error occurred - a crucial detail!)
Take it one small step at a time. Forget for the moment PL/SQL; are you able to write the correct query in SQL, which will return the department name, the manager's name and the sum of the salaries of all the employees in the department? If you can do that, then the PL/SQL around it is easy.
Here is one way to get all the values in one SQL statement:
select d.department_name,
m.first_name || ' ' || m.last_name as manager_name,
sum(e.salary) as sum_salary
from departments d
join
employees m on d.manager_id = m.employee_id
join
employees e on d.department_id = e.department_id
where d.department_id = 100
group by d.department_id, d.department_name, m.first_name, m.last_name
;
DEPARTMENT_NAME MANAGER_NAME SUM_SALARY
--------------- --------------- ----------
Finance Nancy Greenberg 51608
Perhaps 80% of writing good PL/SQL code is simply writing good, efficient SQL statements. If you have any difficulty with this query, you should probably spend the majority of your time writing SQL statements, for the next few days or weeks; return to PL/SQL when you feel this query (in my answer) is "simple", "easy", "standard" (which it is!)

utl_match comparing many records

I have 2 tables - one with 1 million records, and the other with 40000 records.
I need to compare for each record in a table if there's a similar string on the other table.
the thing is that this procedure is very slow
I need optimize this procedure
for tablea in
( select first_name||' '||last_name as fullname from employee )
loop
SELECT COUNT(*)
INTO num_coincidencias
FROM table b
WHERE utl_match.jaro_winkler_similarity(b.name ,tablea .fullname) > 98
dbms_output.put_line(num_coincidencias);
end loop;
You do realize you are doing 40 billion comparisons? This is going to take a long time no matter what method you use.
Turning this into a SQL statement will eliminate context switches, I don't know if your computer has the resources to do it all in a single SQL statement:
SELECT COUNT (*) c, a.first_name || ' ' || a.last_name full_name
FROM employee a CROSS JOIN tableb b
WHERE UTL_MATCH.jaro_winkler_similarity (b.first_name, a.first_name || a.last_name) > 98
GROUP BY a.first_name || ' ' || a.last_name

Given a pk value, is there a way to find which tables have a relationship with that specific pk?

Let's say that i have a given value of a PK column:
10
Is there a query to find all tables which have a relationship with that specific pk value?
Example for single column PK.
PK constraint name can be derived from DBA_CONSTRAINTS (USER_CONSTRAINTS, ALL_CONTRAINTS).
The code can be extended to multi-column PKs using collection for PK values
declare
p_pk_name varchar2(30):= '<PK constraint_name>';
p_pk_value varchar2(500):='<PK value>';
l_cnt int;
begin
for x in
(
select t.owner, t.table_name, cc.column_name
from dba_constraints c,
dba_tables t,
dba_cons_columns cc
where c.r_constraint_name = p_pk_name
and t.owner = c.owner
and t.table_name = c.table_name
and cc.owner = c.owner
and cc.constraint_name = c.constraint_name
)
loop
execute immediate 'select count(1) from '||x.owner||'.'||x.table_name|| ' where '||x.column_name||' = :p_pk_value and rownum <= 1' into l_cnt using p_pk_value ;
if l_cnt <> 0 then
dbms_output.put_line(x.owner||'.'||x.table_name);
end if;
end loop;
end;
Not a single query, but you could write a PL/SQL block to do it. You'd start with a query on USER_CONSTRAINTS (or ALL_CONSTRAINTS or DBA_CONSTRAINTS) to find the tables with foreign keys that reference the given primary key. For each, you'd create and execute a dynamic SQL query to see if there are any rows with the particular PK value you're interested in.
Making it work for multi-column keys would be an additional challenge.
Here's how it would look in SQL Server:
DECLARE #keyColumn VARCHAR(100)
DECLARE #keyValue INT
SET #keyColumn = 'SiteID'
SET #keyValue = 400
SELECT 'SELECT ''' + TABLE_NAME + ''' AS TABLENAME, "' + #keyColumn
+ '" FROM "' + TABLE_NAME + '" WHERE "' + #keyColumn + '" = '
+ CONVERT(VARCHAR(100), #keyValue) + ' UNION ALL'
FROM INFORMATION_SCHEMA.KEY_COLUMN_USAGE AS KCU
WHERE KCU.COLUMN_NAME = #keyColumn
I know Oracle has INFORMATION_SCHEMA, so something similar should work.
I tried with SQLFiddle to adapt it but failed. Perhaps someone else here can edit my answer so it works with Oracle.
Here's what the generated SQL looks like, in part:
SELECT 'StockInventory' AS TABLENAME, "SiteID" FROM "StockInventory" WHERE "SiteID" = 400 UNION ALL
SELECT 'SiteDomains' AS TABLENAME, "SiteID" FROM "SiteDomains" WHERE "SiteID" = 400 UNION ALL

Resources