Oracle - using nested table column in where clause - sql

I have a staging table (table_B) with columns using nested table data types:
CREATE OR REPLACE TYPE nested_column_type AS OBJECT
(
abc_1 VARCHAR2(100),
abc_2 VARCHAR2(100),
col_id VARCHAR2(100),
tbl_id NUMBER
);
CREATE OR REPLACE TYPE nested_column_tab AS TABLE OF nested_column_type;
CREATE TABLE table_B
(col_id NUMBER,
nested_column NESTED_COLUMN_TAB)
NESTED TABLE nested_column STORE AS column_nested);
I want to use nested_column in the where clause of a delete statement like this:
DELETE FROM table_A a
WHERE tbl_id = v_tbl_id
AND NOT EXISTS (SELECT col_id
FROM TABLE(SELECT b.nested_column
FROM table_B b
WHERE tbl_id = v_tbl_id)
WHERE col_id = a.col_id);
Table_A is my target table. My goal is to delete records from table_A where table_A.col_id NOT EXISTS in table_B.nested_column.col_id and tbl_id = v_tbl_id.

Adding more on what #Ted mentioned,
you need to understand object name resolution steps and must use a table alias. This is mentioned here.
To avoid inner capture and similar problems resolving references,
Oracle Database requires you to use a table alias to qualify any
dot-notational reference to subprograms or attributes of objects.
In your case the query becomes:
DELETE FROM table_A a
WHERE tbl_id = v_tbl_id
AND a.col_id NOT IN (SELECT b.col_id
FROM table_B b
WHERE (SELECT tb.tbl_id
FROM TABLE (b.nested_column) tb) =a.tbl_id);

I think the below will put you in the right path:
select t.primary_id, nt.*
from table_b t, table (t.nested_column) nt
For any further clarifications please don't hesitate to contact me again here.
Ted

Related

Oracle SQL Subquery - Usage of NOT EXISTS

I used a query to find a list of Primary Keys. One Primary key per each ForiegnKey in a table by using below query.
select foreignKey, min(primaryKey)
from t
group by foreignKey;
Let us say this is the result : 1,4,5
NOw I have another table - Table B that has list of all Primary keys. It has 1,2,3,6,7,8,9
I want a write a query using the above query So that I get a subset of the original query(above) that does not exist in Table B. I want 4 and 5 back with the new query.
Use a having clause:
select foreignKey, min(primaryKey)
from t
group by foreignKey
having min(primarykey) not in (select pk from b);
You should also be able to express this as not exists:
having not exists (select 1
from b
where b.pk = min(t.primaryKey)
)

Create a field in Firebird which displays data from another table

I didn't find a working solution for creating a "lookup column" in a Firebird database.
Here is an example:
Table1: Orders
[OrderID] [CustomerID] [CustomerName]
Table2: Customers
[ID] [Name]
When I run SELECT * FROM ORDERS I want to get OrderID, CustomerID and CustomerName....but CustomerName should automatically be computed by looking for the "CustomerID" in the "ID" column of "Customer" Table, returning the content of the "Name" column.
Firebird has calculated fields (generated always as/computed by), and these allow selecting from other tables (contrary to an earlier version of this answer, which stated that Firebird doesn't support this).
However, I suggest you use a view instead, as I think it performs better (haven't verified this, so I suggest you test this if performance is important).
Use a view
The common way would be to define a base table and an accompanying view that gathers the necessary data at query time. Instead of using the base table, people would query from the view.
create view order_with_customer
as
select orders.id, orders.customer_id, customer.name
from orders
inner join customer on customer.id = orders.customer_id;
Or you could just skip the view and use above join in your own queries.
Alternative: calculated fields
I label this as an alternative and not the main solution, as I think using a view would be the preferable solution.
To use calculated fields, you can use the following syntax (note the double parentheses around the query):
create table orders (
id integer generated by default as identity primary key,
customer_id integer not null references customer(id),
customer_name generated always as ((select name from customer where id = customer_id))
)
Updates to the customer table will be automatically reflected in the orders table.
As far as I'm aware, the performance of this option is less than when using a join (as used in the view example), but you might want to test that for yourself.
FB3+ with function
With Firebird 3, you can also create calculated fields using a trigger, this makes the expression itself shorter.
To do this, create a function that selects from the customer table:
create function lookup_customer_name(customer_id integer)
returns varchar(50)
as
begin
return (select name from customer where id = :customer_id);
end
And then create the table as:
create table orders (
id integer generated by default as identity primary key,
customer_id integer not null references customer(id),
customer_name generated always as (lookup_customer_name(customer_id))
);
Updates to the customer table will be automatically reflected in the orders table. This solution can be relatively slow when selecting a lot of records, as the function will be executed for each row individually, which is a lot less efficient than performing a join.
Alternative: use a trigger
However if you want to update the table at insert (or update) time with information from another table, you could use a trigger.
I'll be using Firebird 3 for my answer, but it should translate - with some minor differences - to earlier versions as well.
So assuming a table customer:
create table customer (
id integer generated by default as identity primary key,
name varchar(50) not null
);
with sample data:
insert into customer(name) values ('name1');
insert into customer(name) values ('name2');
And a table orders:
create table orders (
id integer generated by default as identity primary key,
customer_id integer not null references customer(id),
customer_name varchar(50) not null
)
You then define a trigger:
create trigger orders_bi_bu
active before insert or update
on orders
as
begin
new.customer_name = (select name from customer where id = new.customer_id);
end
Now when we use:
insert into orders(customer_id) values (1);
the result is:
id customer_id customer_name
1 1 name1
Update:
update orders set customer_id = 2 where id = 1;
Result:
id customer_id customer_name
1 2 name2
The downside of a trigger is that updating the name in the customer table will not automatically be reflected in the orders table. You would need to keep track of these dependencies yourself, and create an after update trigger on customer that updates the dependent records, which can lead to update/lock conflicts.
No need here a complex lookup field.
No need to add a persistant Field [CustomerName] on Table1.
As Gordon said, a simple Join is enough :
Select T1.OrderID, T2.ID, T2.Name
From Customers T2
Join Orders T1 On T1.IDOrder = T2.ID
That said, if you want to use lookup Fields (as we do it on a Dataset) with SQL you can use some thing like :
Select T1.OrderID, T2.ID,
( Select T3.YourLookupField From T3 where (T3.ID = T2.ID) )
From Customers T2 Join Orders T1 On T1.IDOrder = T2.ID
Regards.

SQL Server : updating one column and inserting if not exist

I have a table TableKats that looks like this:
ID - int
Name - varchar
KatID - int
What I want to do is to update the column Name from another table, and if there is a name in the other table that doesn't exist in TableKats, it should insert it and give KatID a 0
Does anybody know a way to do that? Thanks
you can do it using MERGE, as your other table schema is not known assuming Name as the column in other table too
MERGE TableKats T
USING ( SELECT * from TableB) AS S
ON T.Name = S.Name
WHEN NOT MATCHED THEN
INSERT ( Name, KatID)
VALUES ( S.Name, 0)
WHEN MATCHED THEN
UDPATE -- Not clear what needs to be updated.

Trying to delete when not exists is not working. Multiple columns in primary key

I am currently trying to delete from Table A where a corresponding record is not being used in Table B. Table A has Section, SubSection, Code, Text as fields, where the first three are the Primary Key. Table B has ID, Section, SubSection, Code as fields, where all four are the Primary Key. There are more columns, but they are irrelevant to this question...just wanted to point that out before I get questioned on why all columns are part of the Primary Key for Table B. Pretty much Table A is a repository of all possible data that can be assigned to a entity, Table B is where they are assigned. I want to delete all records from table A that are not in use in Table B. I have tried the following with no success:
DELETE FROM Table A
WHERE NOT EXISTS (SELECT * from Table B
WHERE A.section = B.section
AND A.subsection = B.subsection
AND A.code = b.code)
If I do a Select instead of a delete, I get the subset I am looking for, but when I do a delete, I get an error saying that there is a syntax error at Table A. I would use a NOT IN statement, but with multiple columns being part of the Primary Key, I just don't see how that would work. Any help would be greatly appreciated.
In sql server,when using not exists, you need to set an alias for the table to be connected, and in the delete statement, to specify the table to delete rows from.
DELETE a FROM Table_A a
WHERE NOT EXISTS (SELECT * from Table_B b
WHERE a.section = b.section
AND a.subsection = b.subsection
AND a.code = b.code)
Please try :
DELETE FROM Table A
WHERE NOT EXISTS (SELECT 1 from Table B
WHERE A.section = B.section
AND A.subsection = B.subsection
AND A.code = b.code)
1 is just a placeholder, any constant/single non-null column will work.
Try something like this:
delete from Table_A
where (section, subsection, code) not in (select section,
subsection,
code
from Table_B)

Inserting a Row in a Table from Select Query

I'm using two Stored Procedures and two Table in My Sql Server.
First Table Structure.
Second Table Structure.
When a Customer books a Order then the Data will be Inserted in Table 1.
I'm using a Select Query in another Page Which Selects the Details from the Second Table.
If a row with a billno from first table is not Present in Second Table I want to Insert into the Second Table with some Default Values in the Select Query. How can I do this
??
If you want to insert in the same query, you will have to create a stored procedure. There you'll query if row exists in second table, and, if not, insert a new entity in second table.
Your code should look something like this:
-- --------------------------------------------------------------------------------
-- Routine DDL
-- Note: comments before and after the routine body will not be stored by the server
-- --------------------------------------------------------------------------------
DELIMITER $$
CREATE DEFINER=`table`#`%` PROCEDURE `insertBill`(IN billNo int, val1 int, val2 int, val3 int)
BEGIN
DECLARE totalres INT DEFAULT 0;
select count(*) from SECOND_TABLE where Bill_Number = billNo INTO totalres;
IF totalres < 1 THEN
INSERT into SECOND_TABLE values(val1,val2,val3);
END IF;
END
Val1,val2 and val3 are the valuest to be inserted into second table.
Hope this helps.
What you do is to LEFT JOIN the two tables and then select only the ones where the second table had no row to join, meaning the bill number were missing.
In the example below, you can replace #default_inform_status and #default_response_status with your default values.
INSERT INTO second_table (Bill_Number, Rest_Inform_Status, Rest_Response_Status)
SELECT ft.Bill_Number, #default_inform_status, #default_response_status
FROM first_table ft
LEFT JOIN second_table st
ON st.Bill_Number = ft.Bill_number
WHERE st.Bill_Number IS NULL
If it is possible to have duplicates of the same Bill_Number in the first table, you should also add a DISTINCT after the SELECT. But considering the fact that it is a primary key, this is no issue for you.