I get an Sql syntax error with Liquibase when I want to add a new column with conditional value - postgresql

Context
Goal
I want to add a new column in a table.
I also want all existing columns to have a default value, depending on another column's value in another table.
What I have
I have created a new changelog file :
Issue
When I do a clean/install, I get the following error :
What have I done wrong ?

Can you try changing your SQL update statement with the below one and check if it works:
update assignment set location = 'SITE_TSN' from requirement where (requirement.id = assignment.requirement_id and requirement.location = 'INTRAMUROS');
In postgres when we want to update data in a table based on values in another table, we can use PostgreSQL UPDATE JOIN syntax :
UPDATE t1
SET t1.c1 = new_value
FROM t2
WHERE t1.c2 = t2.c2;
To join to another table in the UPDATE statement, you specify the joined table in the FROM clause and provide the join condition in the WHERE clause. The FROM clause must appear immediately after the SET clause.
For each row of table t1, the UPDATE statement examines every row of table t2. If the value in the c2 column of table t1 equals the value in the c2 column of table t2, the UPDATE statement updates the value in the c1 column of the table t1 the new value (new_value).
For a detailed example of this JOIN syntax, please visit this link.

Related

Why is "select table_name from table_name" valid [duplicate]

This question already has an answer here:
What are differences between SQL queries?
(1 answer)
Closed 4 years ago.
This syntax is valid for PostgreSQL:
select T from table_name as T
T seems to become a CSV list of values from all columns in table_name. select T from table_name as T works, and, for that matter, select table_name from table_name. Where is this syntax documented, and what is the datatype of T?
This syntax is not in SQL Server, and (AFAIK) does not exist in any other SQL variant.
If you create a table, Postgres creates a type with the same name in the background. The table is then essentially a "list of that type".
Postgres also allows to reference a complete row as a single "record" - a value built from multiple columns. Those records can be created dynamically through a row constructor.
Each row in a the result of a SELECT statement is implicitly assigned a TYPE - if the row comes from a single table, it's the table's type. Otherwise it's an anonymous type.
When you use the table name in a place where a column would be allowed it references the full row as a single record. If the table is aliased in the select, the type of that record is still the table's type.
So the statement:
select T
from table_name as T;
returns a result with a single column which is a record (of the table's type) containing each column of the table as a field. The default output format of a record is a comma separated list of the values enclosed in parentheses.
Assuming table_name has three columns c1, c2 and c3 the following would essentially do the same thing:
select row(c1, c2, c3)
from table_name;
Note that a record reference can also be used in comparisons, e.g. finding rows that are different between two tables can be done in the following manner
select *
from table_one t1
full outer join table_two t2 on t1.id = t2.id
where t1 <> t2;

update a column field using inner join two tables in postgres and I am getting an ERROR: table name "TblFacultyMaster" specified more than once

UPDATE "TblFacultyMaster"
SET "TblFacultyMaster".teacher_id = teacher_details.teacher_id
FROM teacher_details
INNER JOIN "TblFacultyMaster" ON "TblFacultyMaster"."IMR"= teacher_details.primary_reg_no
WHERE ("TblFacultyMaster"."IMR" = teacher_details.primary_reg_no);
I am getting this error
ERROR: table name "TblFacultyMaster" specified more than once
Quote from the manual
Note that the target table must not appear in the from_list, unless you intend a self-join (in which case it must appear with an alias in the from_list).
(Emphasis mine)
So simple remove the inner join. You also need to remove the table prefix of the column to be updated on the left hand side of the SET:
UPDATE "TblFacultyMaster"
SET teacher_id = teacher_details.teacher_id
FROM teacher_details
WHERE "TblFacultyMaster"."IMR" = teacher_details.primary_reg_no;
As #a_horse_with_no_name mentioned from the tutorial, the target table should not appear in the from list. You can however achieve this by aliasing the table in the from_list. I have done this before, and it works.

Update with leftjoin requires to join the update table itself

I want to update a table with values from another table, which not always exist. So I need to left join the other table. The only way I found is this:
UPDATE lessonentity update
SET title=a.test
FROM lessonentity l
LEFT JOIN (SELECT 'hoho1' test) a ON(true)
where l.lessonid=48552
AND update.lessonid=l.lessonid
My question: Is it possible to left-join another table, without inner-joining (where) the updating-table again?
Yes, but not using an explicit join. In your case, this is sufficient given that a has only one row:
UPDATE lessonentity le
SET title = a.test
FROM (SELECT 'hoho1' test) a
WHERE le.lessonid = 48552;
Normally, there would be an additional condition in the WHERE, connecting a and le, but that is not necessary in this case because the table has a single row.

Updating for each row in a table

I have this query here which returns an error because of too many rows returned:
UPDATE tmp_rsl2 SET comm_percent=( SELECT c2.comm_percent
FROM tmp_rsl2 t1
INNER JOIN gn_salesperson g1 ON t1.sales_person=g1.sales_person
INNER JOIN comm_schema c1 ON g1.comm_schema=c1.comm_schema
INNER JOIN comm_schema_dt c2 ON c1.comm_schema_id=c2.comm_schema_id AND (t1.balance_amount::numeric <= (COALESCE(c2.value_amount,0)) );`
Basically for each row of the comm_percent column, I want to update all of them using the subquery SELECT statement. I imagine using a FOR loop or something but I'd like to hear ideas or to know a proper way to do this.
The error TOO_MANY_ROWS is about assigning a value to a variable, that can only take '1' (one) value, whereas the SELECT query is returning more than one.
Without a reference schema, its difficult to give an SQL that'd work (not to say that the issue lies with the Schema), but you need to ensure that the value assigned to comm_percent from the SELECT statement returns only 1 row. A very blind attempt at how it 'might' work in your case (given below), but again without knowing the schema its difficult to gauge whether it'd work.
UPDATE tmp_rsl2
SET comm_percent = c2.comm_percent
FROM gn_salesperson g1 ON
INNER JOIN comm_schema c1 ON g1.comm_schema = c1.comm_schema
INNER JOIN comm_schema_dt c2 ON c1.comm_schema_id = c2.comm_schema_id
AND (tmp_rsl2.balance_amount::NUMERIC <= (COALESCE(c2.value_amount, 0)))
WHERE tmp_rsl2.sales_person = g1.sales_person
UPDATE
As per below comments, have given an unrelated SQLFiddle example that should give an idea of how to perform an UPDATE of all rows of a table looking up corresponding values from another table.

Updating a row in postgres from another row

I have two tables one called details and the other called c_details. Both tables are exact the same except for different table names
No I a row with data in both of these tables
Is it possible to update the row in details with the row in c_details
Eg.
update details SET (Select * from c_details)?
You have to describe explicitly the list of columns to be updated, as well as the key to match columns between the two tables.
The syntax for updating one table from another table is described in detail in the UPDATE chapter of the PostgreSQL documentation.
UPDATE
details
SET
name = c.name,
description = c.description
FROM
c_details AS c
WHERE
c.id=details.id;
If you need to UPDATE FROM SELECT and SET a field based on an aggregate function (MIN) then then SQL should read:
UPDATE
details
SET
name = (
SELECT
MIN(c.create_at)
FROM
c_details AS c
WHERE
c.type = 4
)
WHERE
c.id = details.id;
Use this sql query:
INSERT INTO details SELECT * FROM c_details