Join a view to another table - postgresql

Is it possible to join a view with another table in SQL? If so, how?
I have a query on Oracle db which has specific fields. I need to re create the same query on PostgreSQL but some of the data in the PostgreSQL query are coming from a view... And that view has missing information. It's a pretty complex view, so I don't want to NOT use it for now.
For example, in Oracle I do this:
SELECT
d.dos_id,
trunc(d.dos_creation, 'MM') as Cohorte,
sum(v.ver_etude + v.ver_direct) as encaissé
from t_dossier d
left outer join v_versement v
on v.dos_id = d.dos_id
In the Postgres one, I'm using a view. But the view does not return "dos_id" so I cannot explicitly join v_versement with the view.
Is there a way to force a view to return specific fields at runtime which weren't there when creating the view?

You can't force it
to return specific fields at runtime which weren't there when creating
the view
You can create or replace it with limitation:
https://www.postgresql.org/docs/current/static/sql-createview.html
CREATE OR REPLACE VIEW is similar, but if a view of the same name
already exists, it is replaced. The new query must generate the same
columns that were generated by the existing view query (that is, the
same column names in the same order and with the same data types), but
it may add additional columns to the end of the list. The calculations
giving rise to the output columns may be completely different.
example:
t=# create view v2 as select now();
CREATE VIEW
Time: 36.488 ms
t=# create or replace view v2 as select now(),current_user;
CREATE VIEW
Time: 8.551 ms
t=# create or replace view v2 as select now()::text,current_user;
ERROR: cannot change data type of view column "now" from timestamp with time zone to text
Time: 0.430 ms

I guess I had not realised that I can actually use the view without creating it...
So I edited the SQL statement that makes up the view, added the fields that I needed and used the code of the view without having to create a new view (creating a new view would mean outsourcing it to another company, which would cost us money..)
Thanks :)

Related

How to add a column to a table using combined columns from the same table?

I needed basic help on how to combine columns into one new column in the same table. I have done the below as a SELECT command and it works fine. I just don't know how to add it to the table permanently so that it becomes part of the table.
SELECT *, concat(z41, z42, z43, z44) AS option_3,
concat(z411, z412, z413, z421, z422, z423, z431, z432, z433, z434, z444,z443, z442, z441) AS option_4,
concat(z4211, z4212, z4213, z4214, z4215, z4311, z4312, z4313, z4314, z4431, z4432, z4433, z4434, z4421, z4422, z4423, z4424, z4425, z4426) AS option_5
FROM combined_full
Like others have mentioned, you are probably better off using a view. But if you really need this computed data in column then you can do this:
ALTER TABLE combined_full ADD COLUMN option_3 varchar,
ADD COLUMN option_4 varchar,
ADD COLUMN option_5 varchar;
UPDATE combined_full
SET option_3 = concat(z41, z42, z43, z44),
option_4 = concat(z411, z412, z413, z421, z422, z423, z431, z432, z433, z434, z444,z443, z442, z441),
option_5 = concat(z4211, z4212, z4213, z4214, z4215, z4311, z4312, z4313, z4314, z4431, z4432, z4433, z4434, z4421, z4422, z4423, z4424, z4425, z4426);
When adding new rows to the table, you should either also enter values for these three new columns, or create an insert trigger so that the values are automatically calculated as you do above.
"so that it becomes part of the table" - you can't. Unfortunately Postgres (as of 9.6) has no (persisted) computed columns.
If the expression is not very expensive to calculate and you don't need an index on it, I would suggest to create a view that contains the expression.
Given the example in your question, this should be good enough in your case as concatenating values isn't really that expensive.
If you really think you need to persist the calculation of the expression because e.g. you want to create an index on that or you constantly use that expression in a where clause, you will need to add a regular column to the table and a trigger that updates the expression when a row is inserted or updated.

Create View vc Create View As Query; What is the difference

I am trying to create a query in Postgresql what when called will get the data at run time from the two tables and hand back to the calling program. The records need to be updateable so I hope I am doing this correctly. Where I am having confusion is the difference between:
create view workorderquery as SELECT PartsWorkOrder.*, Copiers.*
FROM PartsWorkOrder RIGHT JOIN Copiers ON PartsWorkOrder.Printer = Copiers.Copier as query;
and:
create view workorderquery as SELECT PartsWorkOrder.*, Copiers.*
FROM PartsWorkOrder RIGHT JOIN Copiers ON PartsWorkOrder.Printer = Copiers.Copier;
Do I need the 'as query' at the end? If so why do I get an error with it?

Postgres Text Search against a derived ts_vector

Somehow I've tricked myself into writing a full text search implementation on a database. I have a table that represents all the entities in my database, a table that represents all the tags, and a table representing the many to many relationship of tags to entities.
I wrote a query that groups all tag names for a given entity and concatenates them into a string, which I then transform into a ts_vector. That query looks like this:
SELECT e.id, to_tsvector(c.publicname || ' ' || string_agg(cv.name, ' '))
FROM categoryvalue cv, entitycategoryvalue ecv, entity e
WHERE ccv.categoryvalueid = cv.id AND e.id = ecv.entityid
GROUP BY e.id;
The query returns results in this schema:
id | to_tsvector
1 | tag_a, tag_b, tag_c
2 | tag_b, tag_d, tag_e
Which is exactly what I'd like to match a ts_query against. I'm a noob at SQL though, and I am wondering if there is a way I can create a table that is continually updated with the results of the query I've written?
EDIT: I documented my eventual solution and system in this blog post http://tech.pro/tutorial/1142/building-faceted-search-with-postgresql
I think you want a VIEW.
CREATE VIEW my_tsearch_table AS
SELECT e.id, to_tsvector(c.publicname || ' ' || string_agg(cv.name, ' '))
FROM categoryvalue cv, entitycategoryvalue c=ecv, entity e
WHERE ccv.categoryvalueid = cv.id AND e.id = ecv.celebrityid
GROUP BY e.id;
Note, however, that you cannot add indexes to a view. This may be a problem with full-text search, as it's quite expensive to generate all those tsvectors for every search.
If you need to index that table, you are looking for a materialized view.
PostgreSQL does not support automatically maintained materialized views; you can't just CREATE MATERIALIZED VIEW and then add some indexes to it.
What you can do is manually maintain a materialized view using an ordinary table, an ordinary view created with your query, and some trigger functions.
Add a trigger function on each table that contributes to the view, and have that trigger function update (or insert into, or delete from, as appropriate) the materialized view based on updates made to that table. This can be complicated to get right in a concurrent environment, though, and it can be prone to lock-ordering deadlocks.
An alternative to a trigger-maintained view is to live with the materialized view getting a little out of date. Periodically create a new table, copy your ordinary view into the new table, add the desired indexes, then drop the old materialized view table and rename the new one to replace it.
See also:
http://wiki.postgresql.org/wiki/Materialized_Views
http://tech.jonathangardner.net/wiki/PostgreSQL/Materialized_Views

SQL Views - no variables?

Is it possible to declare a variable within a View? For example:
Declare #SomeVar varchar(8) = 'something'
gives me the syntax error:
Incorrect syntax near the keyword 'Declare'.
You are correct. Local variables are not allowed in a VIEW.
You can set a local variable in a table valued function, which returns a result set (like a view does.)
http://msdn.microsoft.com/en-us/library/ms191165.aspx
e.g.
CREATE FUNCTION dbo.udf_foo()
RETURNS #ret TABLE (col INT)
AS
BEGIN
DECLARE #myvar INT;
SELECT #myvar = 1;
INSERT INTO #ret SELECT #myvar;
RETURN;
END;
GO
SELECT * FROM dbo.udf_foo();
GO
You could use WITH to define your expressions. Then do a simple Sub-SELECT to access those definitions.
CREATE VIEW MyView
AS
WITH MyVars (SomeVar, Var2)
AS (
SELECT
'something' AS 'SomeVar',
123 AS 'Var2'
)
SELECT *
FROM MyTable
WHERE x = (SELECT SomeVar FROM MyVars)
EDIT: I tried using a CTE on my previous answer which was incorrect, as pointed out by #bummi. This option should work instead:
Here's one option using a CROSS APPLY, to kind of work around this problem:
SELECT st.Value, Constants.CONSTANT_ONE, Constants.CONSTANT_TWO
FROM SomeTable st
CROSS APPLY (
SELECT 'Value1' AS CONSTANT_ONE,
'Value2' AS CONSTANT_TWO
) Constants
#datenstation had the correct concept. Here is a working example that uses CTE to cache variable's names:
CREATE VIEW vwImportant_Users AS
WITH params AS (
SELECT
varType='%Admin%',
varMinStatus=1)
SELECT status, name
FROM sys.sysusers, params
WHERE status > varMinStatus OR name LIKE varType
SELECT * FROM vwImportant_Users
also via JOIN
WITH params AS ( SELECT varType='%Admin%', varMinStatus=1)
SELECT status, name
FROM sys.sysusers INNER JOIN params ON 1=1
WHERE status > varMinStatus OR name LIKE varType
also via CROSS APPLY
WITH params AS ( SELECT varType='%Admin%', varMinStatus=1)
SELECT status, name
FROM sys.sysusers CROSS APPLY params
WHERE status > varMinStatus OR name LIKE varType
Yes this is correct, you can't have variables in views
(there are other restrictions too).
Views can be used for cases where the result can be replaced with a select statement.
Using functions as spencer7593 mentioned is a correct approach for dynamic data. For static data, a more performant approach which is consistent with SQL data design (versus the anti-pattern of writting massive procedural code in sprocs) is to create a separate table with the static values and join to it. This is extremely beneficial from a performace perspective since the SQL Engine can build effective execution plans around a JOIN, and you have the potential to add indexes as well if needed.
The disadvantage of using functions (or any inline calculated values) is the callout happens for every potential row returned, which is costly. Why? Because SQL has to first create a full dataset with the calculated values and then apply the WHERE clause to that dataset.
Nine times out of ten you should not need dynamically calculated cell values in your queries. Its much better to figure out what you will need, then design a data model that supports it, and populate that data model with semi-dynamic data (via batch jobs for instance) and use the SQL Engine to do the heavy lifting via standard SQL.
What I do is create a view that performs the same select as the table variable and link that view into the second view. So a view can select from another view. This achieves the same result
How often do you need to refresh the view? I have a similar case where the new data comes once a month; then I have to load it, and during the loading processes I have to create new tables. At that moment I alter my view to consider the changes.
I used as base the information in this other question:
Create View Dynamically & synonyms
In there, it is proposed to do it 2 ways:
using synonyms.
Using dynamic SQL to create view (this is what helped me achieve my result).

Execute Trigger on View?

I am not too familiar with database triggers and/or views. I am currently using PostgreSQL and HSQL; although the database is not too important. I am just wondering if any database offers something like this:
I have an (example) table like this:
CREATE TABLE DUMMY_TABLE (ID INTEGER, NUMBER INTEGER);
I created a view like this:
CREATE VIEW DUMMY_VIEW AS SELECT * FROM DUMMY_TABLE WHERE NUMBER > 5;
I insert a couple of entities:
INSERT INTO DUMMY_TABLE VALUES(1,2);
INSERT INTO DUMMY_TABLE VALUES(1,10);
so of course the DUMMY_VIEW only contains VALUES(1,10) when I call
SELECT * FROM DUMMY_VIEW
So now what I want to do is add a trigger to the DUMMY_VIEW that is called whenever an entity is inserted that has NUMBER > 5.
I have tried adding triggers directly to the DUMMY_VIEW in both HSQL and PostgreSQL; but they say that triggers cannot be added to views.
Is this (or a functionally similar solution) possible?
It should be noted that PostgreSQL 9.1+ supports triggers on views. See WAITING FOR 9.1 – TRIGGERS ON VIEWS for a brief look at this.
Yes, triggers cannot be placed on views directly. What you should do is place a trigger on the base table and check to see if the new NUMBER row has a value greater than 5.
Note: a view is only a stored select statement, so it does not really hold data. That is why one cannot check to see whether data is being inserted, deleted or updated in a view structure.
I think you have to put the trigger on the table, not the view.
The trigger could use a query on the view so that you are DRY.
Is there any other reason the trigger needs to be on the view and not the table?
An example in response to the comment
-- Create function
CREATE FUNCTION doWhatIwant() RETURNS trigger AS '
BEGIN
IF NEW.number > 5 THEN
do_stuff
END IF;
RETURN NEW;
END;
' LANGUAGE plpgsql;
-- Create trigger
CREATE TRIGGER yourTrigger AFTER INSERT ON dummy_table
FOR EACH ROW EXECUTE PROCEDURE doWhatIwant();
I'm not sure what you want to achieve.
A trigger executes code on data change. A view is a (let's say) "callable sub-set of data". It is virtually non-existent, unless you select from it. It can't contain a trigger, because it contains nothing.
So basically you want a trigger on the base table.
This is possible if you add the trigger to the table with the same condition as the view.
The trigger body should have something like:
if (inserted.NUMBER > 5) {
do something;
}
//do nothing if inserted.NUMBER is not > 5
HSQLDB 2.x supports both updatable views and trigger-updatable views.
Your view example is updatable by itself. Therefore you can insert / delete / update rows using the view instead of the table. This will not allow rows containing NUMBER <= 5 in inserts and updates.
You can also define triggers on the view. These triggers are defined with INSTEAD OF INSERT, INSTEAD OF UPDATE or INSTEAD OF DELETE. In the body of the trigger, you can check for the values and either throw an exception for invalid input, or insert the row into the base table.
see http://hsqldb.org/doc/2.0/guide/triggers-chapt.html