Sorting query results - sql

How can I properly sort the results to show the results in the following order
1st - Parent Name
2nd - Child Name
3rd - Teacher Name
4th - Other Name
Where the parent name and child name is on the same table Family
id | parent name | child name
-----------------------------
1 Denz Hanz
2 Denz Pog
3 Joann Mac
while the other names are on different tables teacher table
id | teacher name
-----------------
1 Miguel
2 Sean
and Other_guest table
id | guest name
-----------------
1 Mike
2 Mal
where in cases that the parent did not arrive, the child name will be showed. Result of the query should show something like this
Participant Name
----------------
1. Denz
2. Denz
3. Mac
4. Miguel
5. Sean
6. Mal
7. Mike
I tried using order by field(),order by field asc, field2 dec ... etc but it seems not the result we wanted.

Below query works in most of the databases, this query will first sort by type and then name:
select * from (
select parent_name, 0 sort_by from table1
union all
select chile_name, 1 from table1
union all
select teacher_name, 2 from table2
union all
select guest_name, 3 from table3) t
order by sort_by, parent_name;

Here is my solution for Oracle DB:
Note that the UNION does not allow ORDER BY clause in subsequent select statements, therefore a procedure is necessary that generates the list of guests in the GUESTS table.
The table ARRIVED contains the IDs of the arrived parents, all other tables are self explanatory.
create table Family (id number, parentname varchar2(100), childname varchar2(100));
create table Teacher (id number, teachername varchar2(100));
create table Other_guest (id number, guestname varchar2(100));
create table Arrived (id number);
create table Guests (guestname varchar2(100));
create sequence Family_seq start with 1 order;
create sequence Teacher_seq start with 1 order;
create sequence Other_seq start with 1 order;
create or replace trigger Family_id before insert on Family for each row
begin
select Family_seq.nextval into :new.id from dual;
end;
create or replace trigger Teacher_id before insert on Teacher for each row
begin
select Teacher_seq.nextval into :new.id from dual;
end;
create or replace trigger Other_id before insert on Other_guest for each row
begin
select Other_seq.nextval into :new.id from dual;
end;
insert into Family (parentname, childname) values ('Denz', 'Hanz');
insert into Family (parentname, childname) values ('Denz', 'Pog');
insert into Family (parentname, childname) values ('Joann', 'Mac');
insert into Teacher (teachername) values ('Miguel');
insert into Teacher (teachername) values ('Sean');
insert into Other_guest (guestname) values ('Mike');
insert into Other_guest (guestname) values ('Mal');
insert into Arrived (id) values (1);
insert into Arrived (id) values (2);
create or replace procedure update_guest_list as
pragma autonomous_transaction;
cursor c_parents is select parentname from family where id in (select id from arrived) order by family.parentname;
cursor c_children is select childname from family where id not in (select id from arrived) order by family.childname;
cursor c_teachers is select teachername from teacher order by teacher.teachername asc;
cursor c_others is select guestname from other_guest order by other_guest.guestname asc;
begin
delete from guests;
for parents_rec in c_parents
loop
insert into guests (guestname) values (parents_rec.parentname);
end loop;
for children_rec in c_children
loop
insert into guests (guestname) values (children_rec.childname);
end loop;
for teachers_rec in c_teachers
loop
insert into guests (guestname) values (teachers_rec.teachername);
end loop;
for others_rec in c_others
loop
insert into guests (guestname) values (others_rec.guestname);
end loop;
commit;
end;
/
execute update_guest_list;

Related

How do I loop through a row while using a cursor

create table ranks (
rank varchar(20)
);
create table people (
name varchar(20)
);
insert into people values('Sam', 'Bob', 'Tim');
declare cursor c1 is (select substr(name, -1) from people)
begin
for i in c1
loop
update ranks
set rank = 'S'
where i = 'S';
end loop;
end;
Hello, I am trying to use the last letter of the people table to decide who gets the S rank, but it isn't working. I keep getting - expression is of wrong type - error. Please help.
Data model looks wrong. That should be only one table with two columns.
SQL> CREATE TABLE people
2 (
3 name VARCHAR2 (20),
4 RANK VARCHAR2 (1)
5 );
Table created.
SQL> INSERT INTO people (name) VALUES ('Sam');
1 row created.
SQL> INSERT INTO people (name) VALUES ('Bob');
1 row created.
SQL> INSERT INTO people (name) VALUES ('Tim');
1 row created.
SQL> COMMIT;
Commit complete.
SQL> SELECT * FROM people;
NAME RANK
-------------------- -----
Sam
Bob
Tim
SQL>
Then, you don't need PL/SQL - a simple UPDATE will do. However, code you posted doesn't make much sense either - substr(name, -1) selects the last letter; nobody has a name that ends with an S so - no rows will ever be updated (at least, not for sample data). That's why I modified it to use the 1st letter.
SQL> UPDATE people
2 SET RANK = 'S'
3 WHERE SUBSTR (name, 1, 1) = 'S';
1 row updated.
SQL> SELECT * FROM people;
NAME R
-------------------- -
Sam S
Bob
Tim
SQL>
If it has to be PL/SQL (because you're learning it), then you'd
SQL> ROLLBACK;
Rollback complete.
SQL> BEGIN
2 FOR cur_r IN (SELECT name FROM people)
3 LOOP
4 UPDATE people
5 SET RANK = 'S'
6 WHERE name = cur_r.name
7 AND SUBSTR (name, 1, 1) = 'S';
8 END LOOP;
9 END;
10 /
PL/SQL procedure successfully completed.
SQL> SELECT * FROM people;
NAME RANK
-------------------- ----
Sam S
Bob
Tim
SQL>
insert into people values('Sam', 'Bob', 'Tim');
Will fail as you only have one column and not three. You want to either use multiple inserts:
insert into people (name) values('Sam');
insert into people (name) values('Bob');
insert into people (name) values('Tim');
Or, use an INSERT ... SELECT ...
insert into people (name)
SELECT 'Sam' FROM DUAL UNION ALL
SELECT 'Bob' FROM DUAL UNION ALL
SELECT 'Tim' FROM DUAL;
Then you want something like:
begin
for i in (select substr(name, -1) AS last_character from people)
loop
update ranks
set rank = 'S'
where i.last_character = 'S';
end loop;
end;
/
But that can be simplified to get rid of the cursor and use a single UPDATE statement:
UPDATE ranks
SET rank = 'S'
WHERE EXISTS(
SELECT 1
FROM people
WHERE name LIKE '%S'
);
But neither of those will do anything as:
The ranks table contains zero rows.
None of the people have a name ending in S.
If you fix both of those then you will just end up updating every row in the ranks table as there is no relationship between a person and a rank.
db<>fiddle here
A simplified version of using cursors is to declare the cursor and then use it
multiple times if required later. This way opening similar dataset in multiple
cursors can be avoided. A good practice to develop when working on code
intensive procedures.
SQL> Declare
cur_people is SELECT name FROM people;
BEGIN
FOR cur_r IN cur_people
LOOP
UPDATE people
SET RANK = 'S'
WHERE name = cur_r.name
AND SUBSTR (name, 1, 1) = 'S';
END LOOP;
END;
/

how to add trigger to count number of rows automatically after inserting in oracle sql developer

I want to add trigger to count number of movies after inserting!
This is the table to store the count value:
CREATE TABLE mov_count
(mcount NUMBER);
and the movie table:
create table movie
(mov_id number primary key,
mov_title varchar(20),
mov_lang varchar(20));
This is the trigger I have created:
create trigger count_movie_trg
after insert on movie
for each row
BEGIN
UPDATE mov_count
SET mcount = (SELECT COUNT(*) FROM movie);
END;
/
After creating this i tried to add movie but its showing mutating trigger/function may not see it error.
It is the FOR EACH ROW that bothers you. It is a table-level trigger, so:
Enter a dummy value for beginning (as you'll update it later):
SQL> insert into mov_count values (0);
1 row created.
Trigger:
SQL> create or replace trigger count_movie_trg
2 after insert on movie
3 begin
4 update mov_count c set
5 c.mcount = (select count(*) from movie m);
6 end;
7 /
Trigger created.
Testing:
SQL> insert into movie
2 select 1, 'Titanic' from dual union all
3 select 2, 'Superman' from dual;
2 rows created.
SQL> select count(*) from mov_count;
COUNT(*)
----------
1
SQL>
Why not just maintain the value without referring to the original table?
create trigger count_movie_trg after insert on movie for each row
begin
update mov_count set mcount = mcount + 1;
end;
To keep the count up-to-date, you'll need a delete trigger as well.
Don't use the table at all; use a view instead.
CREATE VIEW mov_count ( mcount ) AS
SELECT COUNT(*) FROM movie;
db<>fiddle

want query to connect 1st table 1st row and 2nd table first two column

I want to connect query to connect 1st table 1st row and 2nd table first two column
means,
Table A,
ID Date Username Password
1 19/2/2016 XYZ ******
2 19/2/2016 ABC ******
Table B,
ID Date Username City
1 19/2/2016 XYZ NYC
2 19/2/2016 ABC LA
that when I insert some data in table A's 1st row then i want to check that data is available at table B's ID,DATE
Do you mean you want to enforce referential integrity between this two tables?
In this case you need a foreign key constraint
ALTER TABLE table_a
ADD CONSTRAINT reference_table_b_fk
FOREIGN KEY (id, date)
REFERENCES table_b (id, date);
If you want to just check before performing Insert option try this:
IF EXISTS (SELECT ID FROM TableB WHERE ID=1 AND Date='19/2/2016')
// Your either insert or not query
ELSE
// Your else logic will be here
so if you insert an entry into table1 only if an equivalent entry exists in table2, here's a little script to do that:
create table table1 (id number(2), date_t1 date, username varchar2(5), password varchar2(8));
create table table2 (id number(2), date_t1 date, username varchar2(5), city varchar2(8));
insert into table1 values (1, to_date('16.02.2016', 'dd.mm.yyyy'), 'XYZ', 'ABC123');
insert into table1 values (2, to_date('16.02.2016', 'dd.mm.yyyy'), 'ABC', 'XYZ123');
insert into table2 values (1, to_date('16.02.2016', 'dd.mm.yyyy'), 'XYZ', 'NYC');
insert into table2 values (2, to_date('16.02.2016', 'dd.mm.yyyy'), 'ABC', 'LA');
declare
n_id number(2);
d_date date;
v_username varchar2(5);
v_password varchar2(8);
n_check number(1);
begin
n_check := 0;
-- fill the variables with data which you want to insert:
n_id := 2;
d_date := to_date('16.02.2016', 'dd.mm.yyyy');
v_username := 'ABC';
v_password := 'CCCCC';
-- check whether an entry exists in table2:
begin
select count(1)
into n_check
from table2 t2
where t2.id = n_id
and trunc(t2.date_t1) = trunc(d_date);
exception when no_data_found then n_check := 0;
end;
-- if an entry exists in table2, then insert into table1:
if n_check <> 0 then
insert into table1 (id, date_t1, username, password)
values (n_id, d_date, v_username, v_password);
end if;
end;
/
select * from table1;
select * from table2;
delete from table1;
delete from table2;

Move rows to another table

I want move rows from one table to another (in order to move unused data to historic storage).
How to do this in most clever way?
I found such solutions but looks like it is not working for Oracle dialect
INSERT dbo.CustomersInactive (
CustomerID,
FirstName,
LastName
) SELECT
CustomerID,
FirstName,
Lastname
FROM (
DELETE dbo.CustomersActive
OUTPUT
DELETED.CustomerID,
This solution seems working:
DECLARE
TYPE CustomerSet IS TABLE OF CustomersActive%ROWTYPE;
inactive CustomerSet;
BEGIN
delete from CustomersActive returning CustomerID,FirstName,Lastname bulk collect into inactive;
FOR i IN inactive.FIRST .. inactive.LAST LOOP
insert into CustomersInactive values (inactive(i).CustomerID,inactive(i).FirstName,inactive(i).Lastname);
END LOOP;
END;
I hope this is the case you need:
--init objects
create table active_cust
(cust_id integer,
name varchar2(100 char)
);
create table inactive_cust as
select *
from active_cust
where 1=2;
--init data
insert into active_cust values (1, 'Przemo');
insert into active_cust values (2,'Pan Miecio');
insert into active_cust values (3,'Pan Franio');
insert into inactive_cust values (3,'Pan Franio');
--merge active and inactive
merge into inactive_cust dest
using (select * from active_cust) srce
on (srce.cust_id = dest.cust_id)
when not matched then insert values
(srce.cust_id, srce.name )
--here specify conditions on which customer is being
--accounted as inactive
/*where srce.some_status_date < sysdate - 100 */
;--only two rows merged as we have >Pan Franio< already in a list of inactive customers!
--now as we have all inactive customers in inactive_cust table, delete from active_cust where id is present in inactive_cust
delete from active_cust ac
where ac.cust_id in (select cust_id
from inactive_cust);
drop table active_cust;
drop table inactive_cust;

PL/SQL trigger to insert next value

I created a trigger which works like when I update/insert a row in one table, an insert of a row will a done in another table which contains a primary key.
Now when I insert a row in the first table I want the trigger to check the last value of primary key of another table and if that is null or '-' then I've to insert 1 into that primary key column so as to insert the remaining values.
I've written the code as follows:
create or replace trigger "T1"
AFTER
insert or update on "buses"
for each row
begin
-- Here I want to check the V_id on vehicles table, if that is null or '-' then insert V_id as 1 along with the below insert statement.
if :NEW."b_key" is not null then
INSERT INTO vehicles (b_KEY,B_NAME,ADDRESS_1,CITY,STATE,ZIP,PHONE,WEBSITE) VALUES (:new.b_KEY,:new.b_NAME,:new.ADDRESS_1,:new.CITY,:new.STATE,:new.ZIP,:new.PHONE,:new.WEBSITE);
end if;
end;
How to find the last b_id in the vehicles table, so that if that value is null or '-' insert b_id as 1, followed by the above insert statement in the same row.
By adding another trigger we can do that as follows:
create or replace TRIGGER "B_VEHICLES"
before insert on "buses"
for each row
declare b_number number;
begin
select max(B_ID) into b_number from Vehicles;
if :OLD."B_ID" is null and b_number is null then
select 1 into :new."B_ID" from dual;
else select b_number + 1 into :new."B_ID" from dual;
end if;
end;​