rewriting query in PostgreSQL - sql

After a db schema change, what was a column is now computed in stored procedure. Is it possible to make this change seamless for application programs?
So that when a program sends a query like
SELECT id,
value
FROM table
...it instead gets a result of
SELECT id,
compute_value() AS value
FROM table
I thought I could use a RULE, but it is not possible to create SELECT rule on existing table.
So the only other option seems to me to create a new table and a view with the name of the existing one. Which, because of the need for INSERT/UPDATE triggers for the view is too complicated. Then I'd rather update all the client applications.

If you know you want to return value, you use a function rather than a stored procedure. Then you'd reference it like:
SELECT id,
your_function_name(parameter) AS value
FROM TABLE
There's an example under "SQL Functions on Composite Types" in the documentation.
Creating a view using the statement above is ideal if your application needs the computed value constantly, otherwise I wouldn't bother.

Related

If not exist clause SQL statement

so I found this sql query in a project I am succeeding. This is the first time I encountering this clause/statement. I understand that this is to look if the table exist before creating one and that Object_ID is the table name that is to be created.
My questions are:
Does sysobject mean the database?
What is the Object property?
I know that it is not the columns inside the table to be created.
The columns are : dtb_color_id and description.
can someone explain this to me. please?
IF NOT EXISTS(SELECT * FROM SYSOBJECTS WHERE ID = OBJECT_ID('DTB_COLOR') AND OBJECTPROPERTY(ID,'ISUserTable') = 1)
BEGIN
.......some query I understand
END
sysobjects, OBJECTPROPERTY and OBJECT_ID are used in Microsoft SQL Server. They are part of the SQL Server DMVs and system functions/procedures used to query and manipulate the metadata.
sys.sysobjects is simply the list of all objects (tables, views, SPs, functions, etc) on the server in the active database. Please note, that sys.sysobjects is deprecated and is only available for backward compatibility. Use sys.objects instead
https://learn.microsoft.com/en-us/sql/relational-databases/system-dynamic-management-views/system-dynamic-management-views?view=sql-server-ver16
It has (as far as I know) no meaning in MySQL, unless somebody specifically created them.
You can also use INFORMATION_SCHEMA which is available in MySQL too (however slightly different in different RDBMS).
MSSQL INFORMATION_SCHEMA: https://learn.microsoft.com/en-us/sql/relational-databases/system-information-schema-views/system-information-schema-views-transact-sql?view=sql-server-ver16
MySQL INFORMATION_SCHEMA: https://dev.mysql.com/doc/refman/8.0/en/information-schema.html
SQL Server has no CREATE TABLE IF NOT EXISTS construct, a variation of the mentioned condition is commonly used to imitate that.
This is a way in SQL Server to check if a table exists in the active database and to perform actions according to the result, like creating the table.
OBJECTPROPERTY simply checks (in this case) if the table is a user created one.
https://learn.microsoft.com/en-us/sql/t-sql/functions/objectproperty-transact-sql?view=sql-server-ver16
I would remove the OBJECTPROPERTY condition in case the part you understand is a CREATE TABLE statement. You don't want to create a table which has a similar name to any system table/view, also you don't want to execute the CREATE TABLE if there is a VIEW with the same name (table creation will fail)
Yes sysobject means database.
The OBJECTPROPERTY() function returns information about schema-
scoped objects in the current database. Use this to check if an
object is a table, view, stored procedure, etc. You can also use
it to check if a table has a primary key, foreign key, foreign
key reference, etc.
For more details : https://learn.microsoft.com/en-us/sql/t-sql/functions/objectpropertyex-transact-sql?view=sql-server-ver16
In this scenario it is used to check whether it is user table or
not. The result of the ISUserTable property is 1 when it is user
table otherwise returns 0.
Here the following steps are followed:
First, it executes the select statement inside the IF Exists
If the select statement returns a value that condition is TRUE for IF Exists
It starts the code inside a begin statement
DTB_COLOR - May be a stored procedure

Query from multiple tables dynamically

I want to query an object from DB that exists in any one of the tables. I am not sure about the table name that a particular object belongs to. For e.g. let's say my DB consists of various tables like Domestic_Passengers, Asian_Passengers, US_Passengers. And this table list may increase as well like in the future we may add the UK_Passengers table too.
So, I want to query something like
SELECT * FROM
(SELECT table_name FROM user_tables where table_name like '%PASSENGER')
WHERE NAME LIKE 'John%'
Is this possible?
That's a very bad database design.
I would suggest a view like this:
CREATE OR REPLACE VIEW PASSENGERS AS
SELECT * FROM Domestic_Passengers
UNION ALL
SELECT * FROM Asian_Passengers
UNION ALL
SELECT * FROM US_Passengers;
And then select from this view.
If this is not possible, then you need to run dynamic SQL in PL/SQL package. But this involves some code.
The best answer depends on a lot of details, such if you can create database objects, how static are the tables, and how will this query be consumed.
If you can create schema objects, and the list of tables is somewhat stable, then Wernfried's answer of building a view is probably best.
If you can create schema objects, but the list of tables is very dynamic, and your application understands ref cursors, you should probably create a function that creates the SELECT and returns it through a ref cursor, like in this answer.
If you cannot create schema objects, then you're limited to the DBMS_XMLGEN/XMLTABLE trick. In a single query, build a string for the SELECT statement you want, run it through DBMS_XMLGEN to create an XMLType, and then use XMLTABLE to transform the XML back into rows and columns. This approach is slow and ugly, but it's the only way to have dynamic SQL in SQL without creating any custom PL/SQL objects. See my answer here for an example.

Update a Temporary Table before showing?

I'd like to find out how to update a temporary table before I show the query. This is to avoid making permanent changes to the database.
So far I got the following:
WITH
new_salary AS
(SELECT ID,NAME,DEPT_NAME,SALARY FROM INSTRUCTOR WHERE DEPT_NAME='Comp. Sci.')
SELECT
*
FROM
new_salary
WHERE
DEPT_NAME='Comp. Sci.';
Now here is where it ends. I want to update this temporary table and show the updated version of that table as to avoid changing the actual database. All my attempts at using the UPDATE clause have failed so I am kind of dumbfounded :/
This part I am currently trying to do is not part of homework. It's just me who doesn't want to have to re-do the database over and over.
How would I go about doing this?
I guess you have two options:
You make a procedure, which first checks whether it needs to update the table. After calling that you execute the query.
You create a pipelined function, which does the checking and returning of the data. You could integrate this into the select like this (pipelined function name called pipelined_function_name):
select *
from table(pipelined_function_name)
;

How to keep "*" in VIEW output clause so that columns track table changes?

I'm creating an Oracle view like this :
SELECT * FROM TABLE;
When I create the view, I notice that oracle changes the view query to something like :
SELECT FIELD1, FIELD2,... FROM TABLE;
My problem is that if I change the TABLE structure, add a new field for instance, the changes are not taken into consideration in the view. I have then to recreate the view, and regrant privileges on this view to the users/roles.
Is there any way to make a view mode generic and keep it as the form of : SELECT * FROM TABLE ?
Thanks.
You cannot define a simple view that would automatically change its structure when the underlying table changes. When new columns are added, you'll need to touch the view. You'll almost certainly need to do as #GordonLinoff suggests and do a CREATE OR REPLACE when the table changes. Given that changes to tables should be rare and should involve proper change control, touching the view as part of the change should be a relatively simple step.
If you're really determined to avoid having to touch the view, there are some alternatives. I generally wouldn't recommend these because they are very likely to increase rather than decrease the complexity of maintaining your system. But if you have a third party system that is generating DDL to add columns on an unpredictable basis, maybe it makes sense.
You could create a DDL trigger that fires in response to statements that ALTER the table and that submits a job using dbms_job that re-creates the view. That's quite a few moving pieces but it's generally workable.
Alternately, instead of a view, you could create a pipelined table function that returns a variable number of columns. That's going to be really complicated but it's also pretty slick. There aren't many places that I'd feel comfortable using that approach simply because there aren't many people that can look at that code and have a chance of maintaining it. But the code is pretty slick.
The * is evaluated when the view is created, not when it is executed. In fact, Oracle compiles views for faster execution. It uses the compiled code when the view is referenced. It does not just do a text substitution into the query.
The proper syntax for changing a view is:
create or replace view v_table as
select *
from table;
I have face this same issue and created a procedure which accepts the name of the table and creates the view:
str := 'create or replace view xyz.'|| tablename_in ||'_v as select * from '|| tablename_in;
execute immediate str;
Then in Toad (not sure if you use Toad), in the schema browser, right click on the table name, and then select 'Custom Queries' --> 'Edit Custom Queries' and then have it call your procedure as:
exec view_create<ObjectList>
Then you can right click on the table name and in one click create the view as Toad will pass the name of the table into the procedure.
Also in the procedure you may want to recompile the schema, so after the view is created do:
sys.utl_recomp.recomp_parallel(4, 'XYZ');
If you develop a script of some sort which can semi automate, it makes things easy.
Hope this helps...

Can dynamic SQL be called from a trigger in Oracle?

I have a dozen tables of whom I want to keep the history of the changes. For every one I created a second table with the ending _HISTO and added fields modtime, action, user.
At the moment before I insert, modify or delete a record in this tables I call ( from my delphi app ) a oracle procedure that copies the actual values to the histo table and then do the operation.
My procedure generates a dynamic sql via DBA_TAB_COLUMNS and then executes the generated ( insert into tablename_histo ( fields s ) select fields, sysdate, 'acition', userid from table_name
I was told that I can not call this procedure from a trigger because it has to select the table the trigger is triggered on. Is this true ? Is it possible to implement what I need ?
Assuming you want to maintain history using triggers (rather than any of the other methods of tracking history data in Oracle-- Workspace Manager, Total Recall, Streams, Fine_Grained Auditing etc.), you can use dynamic SQL in the trigger. But the dynamic SQL is subject to the same rules that static SQL is subject to. And even static SQL in a row-level trigger cannot in general query the table that the trigger is defined on without generating a mutating table exception.
Rather than calling dynamic SQL from your trigger, however, you can potentially write some dynamic SQL that generates the trigger in the first place using the same data dictionary tables. The triggers themselves would statically refer to :new.column_name and :old.column_name. Of course, you would have to either edit the trigger or re-run the procedure that dynamically creates the trigger when a new column gets added. Since you, presumably, need to add the column to both the main table and the history table, however, this generally isn't too big of a deal.
Oracle does not allow a trigger to execute a SELECT against the table on which the trigger is defined. If you try it you'll get the dreaded "mutating table" error (ORA-04091), and while there are ways to get around that error they add a lot of complexity for little value. If you really want to build a dynamic query every time your table is updated (IMO this is a bad idea from the standpoint of performance - I find that metadata queries are often slow, but YMMV) it should end up looking something like
strAction := CASE
WHEN INSERTING THEN 'INSERT'
WHEN UPDATING THEN 'UPDATE'
WHEN DELETING THEN 'DELETE'
END;
INSERT INTO TABLENAME_HISTO
(ACTIVITY_DATE, ACTION, MTC_USER,
old_field1, new_field1, old_field2, new_field2)
VALUES
(SYSDATE, strAction, USERID,
:OLD.field1, :NEW.field1, :OLD.field2, :NEW.field2)
Share and enjoy.