Count how many car/cars owner owns - sql

I'm trying to learn plsql and got stuck in understanding some basic stuff. Here is a challenge that I'm trying to solve. I have two tables. One holds information about owners and the other is information about cars.
I want to to write an anonymous block that joins these two tables and with a for loop based on amount of cars that is registered to each owner prints how many cars each person own. furthermore I want an if statement which distinguishes between 1 Car (singular) and 2, 3 Cars (plural).
the tables are these:
CREATE TABLE owners(
id_nr VARCHAR2(13) PRIMARY KEY,
f_name VARCHAR2(20),
s_name VARCHAR2(20)
);
CREATE TABLE cars(
reg_nr VARCHAR2(6) PRIMARY KEY,
id_nr REFERENCES owners(pnr),
model VARCHAR2(20),
year NUMBER(4),
date DATE
);
The result may look like something like this:
19380321-7799, Hans, Anderson, Owns: 1 car
19490321-7899, Mike, Erikson, Owns: 2 cars
. . . etc
I know the this question was already answered but when I try following:
declare
v_suffix varchar2(1);
begin
for o in (select bilägare_pnr, fnamn, enamn,
(select count(1) from fordon where fordon_pnr = bilägare_pnr) as bilar_ägda
from bilägare)
loop
if o.pnr_fordon = 1
then v_suffix = 'bil'
else v_suffix = 'bilar'
end if;
dbms_output.put_line(o.pnr || ', ' || o.fnamn || ', ' || o.enamn
|| ' Äger: ' || o.pnr_fordon || ' bil' || v_suffix);
end loop;
end;
/
I get:
ORA-06550: line 9, column 27:
PLS-00103: Encountered the symbol "=" when expecting one of the following:
:= . ( # % ;
any tips? Im not sure how to declare v_suffix
EDIT (copied from comment on answer below):
Updating my code:
declare
cursor c_BILÄGARE is
select fnamn,enamn,pnr
from BILÄGARE;
begin
for rec in c_BILÄGARE loop
if (rec.antal>1) then
dbms_output.put_line (rec.pnr||','|| rec.fnamn || ',' ||
rec.enamn || ',' || rec.antal || 'bilar');
else
dbms_output.put_line (rec.pnr||','|| rec.fnamn || ',' ||
rec.enamn || ',' || rec.antal || 'bil');
end if;
end loop;
end;
getting:
ORA-06550: line 9, column 9: PLS-00302: component 'ANTAL' must be declared (antal=Quantity)

As noted above, you need to use := as the assignment operator. You also need a semi-colon after each statement - thus, you should use
then v_suffix := 'bil';
instead of
then v_suffix := 'bil'
So your code should look like:
declare
v_suffix varchar2(1);
begin
for o in (select bilägare_pnr, fnamn, enamn,
(select count(1)
from fordon
where fordon_pnr = bilägare_pnr) as bilar_ägda
from bilägare)
loop
if o.pnr_fordon = 1
then v_suffix := 'bil';
else v_suffix := 'bilar';
end if;
dbms_output.put_line(o.pnr || ', ' || o.fnamn || ', ' || o.enamn
|| ' Äger: ' || o.pnr_fordon || ' bil' || v_suffix);
end loop;
end;

Please try this piece of code:
begin
for rec in (select
o.id_nr || ',' || o.f_name || ',' || o.s_name || ', Owns: ' || coalesce(car.cnt, 0) || ' ' || decode(car.cnt , 1, 'Car', 'Cars') as res
from owners o
left outer join (select id_nr, count(1) as cnt from cars group by id_nr) car on (car.id_nr = o.id_nr)) loop
dbms_output.put_line(rec.res);
end loop;
end;
Thanks.

There are multiple issues in your code which is highlighted and fixed in following code:
declare
v_suffix varchar2(5); -- data length should be 5 or more
begin
for o in (select bilägare_pnr, fnamn, enamn,
(select count(1) from fordon where fordon_pnr = bilägare_pnr) as bilar_ägda
from bilägare)
loop
if o.bilar_ägda <= 1 -- replaced it from pnr_fordon and <= is used for 0 or 1 car
then v_suffix := 'bil'; -- := and ; is used here and in next statement
else v_suffix := 'bilar';
end if;
dbms_output.put_line(o.pnr || ', ' || o.fnamn || ', ' || o.enamn
|| ' Äger: ' || o.pnr_fordon || ' bil' || v_suffix);
end loop;
end;
/

Related

Postgresql For Loop Problems :(

I wanted to make a table that sanity checked record integrity for any duplications among my db.
I have a table currently with object names (tables) and their primary keys:
I want to create a procedure that loops through those objects with their keys, and inserts into a separate table the count of duplicates:
below is my code, but I've never done anything like this before and am new to postgres. What I have is from hours of googling/researching but every time I get closer I get a new error and am quite stuck :( Any insights would be greatly appreciated.
My newest error is I believe from my quote_ident(object_names). I don't want to query the column as postgres is reading it, I'd want that to be a raw string:
code:
do $$
declare
object_names varchar;
keys varchar;
rec record;
begin
for rec in select object_name, key from mfr_incentives.public.t_jh_dup_check
loop
object_names = rec.object_name;
keys = rec.key;
execute 'insert into mfr_incentives.public.t_jh_dup_check_final_output
select * from
(select ' || quote_ident(object_names) || ', ' || quote_ident(keys) || ', ' || ' count(*), current_date from
( select ' || keys || ', count(*)
from ' || object_names ||
' group by ' || keys || ' having count(*) > 1
) a
) a';
end loop;
end;
$$;
Found out my problem!
Being unfamiliar with the topic I finally found that I wanted quote_literal() instead of quote_ident().
The below works:
create or replace procedure public.proc_jh_dup_check()
language plpgsql
--IT WORKS NOW
as $$
declare
rec record;
begin
for rec in select object_name, key from mfr_incentives.public.t_jh_dup_check
loop
execute 'insert into mfr_incentives.public.t_jh_dup_check_final_output
select * from
(select ' || quote_literal(rec.object_name) || ', ' || quote_literal(rec.key) || ', ' || ' count(*), current_date from
( select ' || rec.key || ', count(*)
from ' || rec.object_name ||
' group by ' || rec.key || ' having count(*) > 1
) a
) a';
end loop;
end;
$$;

PLSQL procedure output having weird blocks

I am trying to display like first screenshot below and I don't understand the output I am getting (second screenshot).
Below is my PLSQL code that I wrote based on my own database. How do I get rid of those weird blocks in my output? Thanks in advance ~
create or replace procedure numberOfSupplier (x int := 0 ) is
begin
for QRow in (SELECT REGION.R_NAME,NATION.N_NAME,COUNT(SUPPLIER.S_NATIONKEY) counter
FROM REGION
INNER JOIN NATION ON NATION.N_REGIONKEY=REGION.R_REGIONKEY
INNER JOIN SUPPLIER ON SUPPLIER.S_NATIONKEY=NATION.N_NATIONKEY
GROUP BY SUPPLIER.S_NATIONKEY,REGION.R_NAME,NATION.N_NAME
HAVING COUNT(SUPPLIER.S_NATIONKEY)> x)
loop
dbms_output.put_line ('R_NAME' || chr(18) || 'N_NAME' || chr(18) || 'COUNT(S_NATIONKEY)' || chr(10));
dbms_output.put_line (QRow.R_NAME || chr(18) || QRow.N_NAME || chr(18) || QRow.counter || chr(10));
end loop;
end;
/
show errors;
execute numberOfSupplier(130);

ORA-04091 TABLE ODB.EMPLOYEE IS MUTATING, TRIGGER/FUNCTION MAY NOT SEE IT. IS THERE SOMETHING WRONG WITH MY TRIGGER?

Trying to create a trigger when there is an update of Status on Employee Table and capture some values for the record in Employee table and Employee_Header table for that record and send an email. The trigger throws an error.
CREATE OR REPLACE TRIGGER ODB.TRG_EMPLOYEE_STATUS_EMAIL
AFTER UPDATE OF STATUS ON ODB.EMPLOYEE
FOR EACH ROW
DECLARE
s_message varchar2(4000);
s_subject varchar2(1000);
s_return_message varchar2(4000);
s_database varchar2(50);
v_rm EMPLOYEE%ROWTYPE;
v_sh EMPLOYEE_HEADER%ROWTYPE;
BEGIN
if :old."STATUS" = 'HOLD' AND :new."STATUS" = 'ACTIVE' AND :new."CATEGORY" = 'FULLTIME' then
select * into v_rm from EMPLOYEE WHERE :new."STATUS" = 'ACTIVE' AND ROWNUM>1;
select * into v_sh from EMPLOYEE_HEADER WHERE ROWNUM>1;
s_subject := 'NAME ' || v_rm.NAME ||' message ' || ' CHECK LOG OF EMPLOYEE' || Chr(13) || ' STATUS: ' || v_rm.STATUS ;
s_message := 'SAMPLE' || Chr(10)||Chr(13) || 'THE STATUS IN EMPLOYEE_HEADER IS: ' || Chr(10)|| ' STATUS: ' || v_sh.STATUS ;
pkg_email.sendEmail('INPUT PARAMETERS TO SEND EMAIL');
end if;
END;
You can't select from a table which is just being changed; it is mutating. Though, as you can use the :new pseudorecord, you can "skip" that error. Also, where rownum > 1 is useless as it is never true. I don't know what you meant to say by using it.
I see you've created columns using double quotes. In Oracle, that's usually a mistake. Not that it won't work - it will, but you always have to reference columns using double quotes and match letter case.
Finally, trigger might look like this (read comments within code):
create or replace trigger odb.trg_employee_status_email
after update of status on odb.employee
for each row
declare
s_message varchar2(4000);
s_subject varchar2(1000);
s_return_message varchar2(4000);
s_database varchar2(50);
-- v_rm employee%rowtype; -- you don't need that
v_sh employee_header%rowtype;
begin
if :old."status" = 'HOLD' and :new."status" = 'ACTIVE' and
:new."category" = 'FULLTIME'
then
-- You can't select from a table which is just being changed - it is "mutating".
-- Besides, AND ROWNUM > 1 will never return anything. You can only check
-- ROWNUM <= some_value
--select * into v_rm from employee where :new."status" = 'ACTIVE' and rownum>1;
select * into v_sh from employee_header where rownum>1;
-- instead of SELECT ... INTO V_RM, use :NEW pseudorecord
s_subject := 'NAME ' || :new.name ||' message ' || ' CHECK LOG OF EMPLOYEE'
|| chr(13) || ' STATUS: ' || :new.status ;
s_message := 'SAMPLE' || chr(10)||chr(13) || 'THE STATUS IN EMPLOYEE_HEADER IS: ' || chr(10)|| ' STATUS: ' || v_sh.status ;
pkg_email.sendemail('INPUT PARAMETERS TO SEND EMAIL');
end if;
exception
when no_data_found then null;
end;

How Can I Format and Print Column Data Types For Oracle SQL?

I am trying to generate a Create Table Statement. To do this, I have created 2 procedures to Extract Tables and Columns from my Schema. The Output of these procedures is then spooled to a SQL file and is supposed to look like this:
The Formatted Output
Right now, My output Looks like this:
----
---- Run on October 04, 2020 at 22:00
----
-- Start Extracting table IMAGE
CREATE TABLE IMAGE (
MFR CHAR(3,)
, PRODUCT CHAR(5,)
, IMAGE BLOB(4000,)
, TECHSPECS BFILE(530,)
);-- END of Table IMAGE creation
--
--
-- Start Extracting table ORDERS
CREATE TABLE ORDERS (
ORDERNUM NUMBER(227,0)
, ORDERDATE DATE(7,)
, CUST NUMBER(223,0)
, REP NUMBER(223,0)
, MANUF CHAR(3,)
, PROD CHAR(5,)
, QTY NUMBER(225,0)
, AMOUNT NUMBER(225,2)
);-- END of Table ORDERS creation
--
--
-- Start Extracting table PRODUCTS
CREATE TABLE PRODUCTS (
MFR CHAR(3,)
, PRODUCT CHAR(5,)
, DESCRIPTION VARCHAR2(100,)
, PRICE NUMBER(225,2)
, QTYONHAND NUMBER(225,0)
);-- END of Table PRODUCTS creation
--
--
---- Oracle Catalog Extract Utility V1.0 ----
---- Run on October 04, 2020 at 22:00
As you can see, the Data types, along with their Data_length, Data_scale, and DAta_precision are not perfectly formatted as per the requirements. I also haven't been able to make a column that displays the null status of each column. The Date, BFILE, and BLOB as shown in the Output are especially troublesome for me to format properly.
This is my code as of now:
SET ECHO OFF
SET FEEDBACK ON
SET WRAP OFF
--The Procedure to Extract The Columns
SET SERVEROUTPUT ON
CREATE OR REPLACE PROCEDURE Extract_Columns (
--Creating Variables of passed values
wSee IN OUT varchar2,
wTable IN USER_TABLES.table_name%type
)
AS
--The Cursor to run through the Columns
CURSOR Extract_C IS
SELECT COLUMN_NAME, DATA_TYPE, DATA_PRECISION, DATA_SCALE, DATA_LENGTH
FROM USER_TAB_COLUMNS
WHERE TABLE_NAME = wTable
ORDER BY Column_ID;
CurrentRow Extract_C%ROWTYPE;
--Creating variables
wItterations NUMBER(1):=0;
BEGIN
FOR CurrentRow IN Extract_C LOOP
IF wItterations = 0 THEN
wsee := wsee || CHR(10) || ' ' || RPAD(currentrow.column_name, 15) || currentrow.data_type || '(' || currentrow.DATA_LENGTH || currentrow.DATA_SCALE || ')';
ELSE
wsee := wsee || CHR(10) || ', ' || RPAD(currentrow.column_name, 15) || currentrow.data_type || '(' || currentrow.DATA_LENGTH || currentrow.DATA_PRECISION || ',' || currentRow.DATA_SCALE || ')';
END IF;
wItterations:= wItterations +1;
END LOOP;
END;
/
SHOW ERRORS;
SET SERVEROUTPUT ON
CREATE OR REPLACE PROCEDURE Extract_Tables
AS
l_crt varchar2(356);
CURSOR Extract_T IS
SELECT TABLE_NAME
FROM USER_TABLES
ORDER BY TABLE_NAME;
CurrentRow Extract_T%ROWTYPE;
--Creating Variables
wvers VARCHAR2(256) := 'V1.0';
wcur_Tim VARCHAR2(200) := '' || TO_CHAR( CURRENT_DATE, 'Month DD, YYYY') || ' at ' || To_CHAR(CURRENT_DATE, 'HH24:MI');
wheader VARCHAR2(45) := 'CREATE TABLE ' || CurrentRow.table_name ||' (';
wspacing NUMBER := length(wheader);
BEGIN
DBMS_OUTPUT.PUT_LINE('---- Oracle Catalog Extract Utility ' || wvers || ' ----');
DBMS_OUTPUT.PUT_LINE('----');
DBMS_OUTPUT.PUT_LINE('---- Run on ' || wcur_Tim);
DBMS_OUTPUT.PUT_LINE('----');
FOR CurrentRow IN Extract_T LOOP
DBMS_OUTPUT.PUT_LINE('-- Start Extracting table ' || CurrentRow.table_name || '');
DBMS_OUTPUT.PUT_LINE('CREATE TABLE ' || CurrentRow.table_name ||' (');
--This is where i should be calling the other procedure
Extract_Columns(l_crt, CurrentRow.table_name);
--This is where create tables procedure begins again
DBMS_OUTPUT.PUT_LINE(l_crt || chr(10) || LPAD(' ', 15 + length(currentrow.Table_name)) || ');' || '-- END of Table ' || currentrow.Table_name || ' creation');
DBMS_OUTPUT.PUT_LINE('--' || CHR(10) || '--' );
l_crt := NULL;
END LOOP;
--Ending Statement
DBMS_OUTPUT.PUT_LINE('---- Oracle Catalog Extract Utility ' || wvers || ' ----');
DBMS_OUTPUT.PUT_LINE('---- Run on ' || wcur_Tim);
END;
/
SHOW ERRORS;
To execute, I have to compile the 'Extract_column' procedure and then 'Extract_tables' procedure, then I run the tables procedure to generate my output.
How can I format my current output in a way that it exactly matches the picture above?

How to input a primary key from one table and get information from other tables for that key

If I input a value from one table, how do I get the data for that value from other tables? For example, when I type in the invoice number (saleinv) from the saleinv table, how do I get data like the customer's name (cname) and street (cstreet) from the customer table? I have this code here but it doesn't work in SQL Developer. The compiler log says "Statement ignored", referring to the SELECT INTO statement in my procedure. It also highlights the FROM statement in the same thing.
--Procedure
CREATE OR REPLACE PROCEDURE saleinvSearch
(
v_saleinv IN saleinv.saleinv%TYPE,
v_saleinvInfo OUT saleinv%ROWTYPE
)
AS
BEGIN
SELECT s.saleinv, s.saledate, s.salesman, s.fire, s.liability, s.collision, s.property, s.tradeserial, s.tradeallow,
cus.cname, cus.cstreet, cus.ccity, cus.cprov, cus.cpostal, cus.chphone,
c.serial, c.make, c.model, c.cyear, c.color,
i.saleinv, i.ocode, i.saleprice, o.ocode
INTO v_saleinvInfo
FROM saleinv s, customer cus, car c, invoption i, options o
WHERE s.cname = cus.cname
AND c.serial = s.serial
AND i.saleinv = s.saleinv
AND i.ocode = o.ocode
AND s.saleinv = v_saleinv;
END;
/
--Test
SET SERVEROUTPUT ON;
ACCEPT p_saleinv PROMPT 'Enter a saleinv number: ';
DECLARE
v_saleinv saleinv%ROWTYPE;
BEGIN
saleinvSearch(UPPER('&p_saleinv'), v_saleinv);
DBMS_OUTPUT.PUT_LINE(' ' || 'SPECIALTY IMPORTS' || CHR(13));
DBMS_OUTPUT.PUT_LINE(' ' || 'SALES INVOICE' || CHR(10));
DBMS_OUTPUT.PUT_LINE('Invoice No.: ' || v_saleinv.saleinv || CHR(9) || 'Date: ' || v_saleinv.saledate || CHR(13));
DBMS_OUTPUT.PUT_LINE('SOLD TO: Name: ' || v_saleinv.cname || CHR(13));
DBMS_OUTPUT.PUT_LINE(' Address: ' || v_saleinv.cstreet || CHR(13));
END;
/