How to execute an update statement with where clause having select statement - postgresql

I have a table (student) with the following schema in postgres 9.5:
id | marks | division | class
1 90 A 1
2 90 B 2
3 90 B 1
I want to update divisions of student with class 1 and marks = 90 to "A".
I know I can simply use update student set division='A' where class =1 and marks=90
But this to understand how to use a select statement returning multiple rows in the query. Somethig like:
update student set division ='A' where id=(select id from student where class=1 and marks=90)
I am new to postgres. Some pointers will help.

You don't need a sub-select:
update student
set division = 'A'
where class = 1
and marks = 90;
But if you insist on using a sub-select you need an IN:
update student
set division = 'A'
where id in (select id
from student
where class = 1
and marks = 90);

Related

Postgres SQL Query sum + count in one query

I need help to get an result.
perscarcountoffset
person
0
1
0
1
I need a Total from a count of the lines where is person 1 + sum of the cell perscarcountoffset
select SUM((select sum(perscarcountoffset) from table where person = 1) + (select count(*) from table where person = 1)) from table where person = 1;
Thanks for any idea.
Try to create a query in postgresql. This works but it gives me 4 as result. But it must be only 2.
This returns 4 because you're actually computing
(select sum(perscarcountoffset) from table where person = 1) + (select count(*) from table where person = 1)
for each row in table (where person = 1), then summing that. So you're getting 2+2.
This is because anything outside of the aggregation method (i.e. the outer SUM() here) is per-row, and the inner sub-selects returns 2 for both rows.
The query you want doesn't need to be this complicated, this should do:
SELECT SUM(perscarcountoffset) + COUNT(*)
FROM table
WHERE person = 1;

Update multiple fields in a table

I have been trying to update multiple fields value in transsit column in the table tracking_details.
I have a table named tracking_details and which consist of two columns id and transsit
Example tracking_details table:-
id | transsit
------------------
1 | rec
2 | rec
3 | sen
4 |
5 | rec
Here, I would like to change rec to received where id equal 1,2 and 5
And what I have done so far is:
UPDATE tracking_details
SET transsit = 'received'
WHERE id = (select id from tracking_details where transsit='rec');
Then I got the following error:
ERROR: more than one row returned by a subquery used as an expression
********** Error **********
ERROR: more than one row returned by a subquery used as an expression
SQL state: 21000
UPDATE tracking_details
SET transsit = 'received'
WHERE id IN (select id from tracking_details where transsit='rec');
For multiple row update use IN instead of = because = will always have only one row or simply you can use join.
UPDATE tracking_details a
JOIN tracking_details b
ON a.id=b.id
SET a.transsit = 'received'
Where b.transsit='rec';
Furthurmore looking at query you do it using single query
UPDATE tracking_details
SET transsit = 'received'
WHERE transsit='rec';
You can simply do an UPDATE with a WHERE clause:
UPDATE tracking_details
SET transsit = 'received'
WHERE transsit = 'rec';

Does the returning clause always execute first?

I have a many-to-many relation representing containers holding items.
I have a primary key row_id in the table.
I insert four rows: (container_id, item_id) values (1778712425160346751, 4). These rows will be identical except the aforementioned unique row_id.
I subsequently execute the following query:
delete from contains
where item_id = 4 and
container_id = '1778712425160346751' and
row_id =
(
select max(row_id) from contains
where container_id = '1778712425160346751' and
item_id = 4
)
returning
(
select count(*) from contains
where container_id = '1778712425160346751' and
item_id = 4
);
Now I expected to get 3 returned from this query, but I got a 4. Getting a 4 is the desired behavior, but it is not what was expected.
My question is: can I always expect that the returning clause executes before the delete, or is this an idiosyncrasy of certain versions or specific software?
The use of a query in returning section is allowed but not documented. For the documentation:
output_expression
An expression to be computed and returned by the DELETE command after each row is deleted. The expression can use any column names of the table named by table_name or table(s) listed in USING. Write * to return all columns.
It seems logical that the query sees the table in a state before deleting, as the statement is not completed yet.
create temp table test as
select id from generate_series(1, 4) id;
delete from test
returning id, (select count(*) from test);
id | count
----+-------
1 | 4
2 | 4
3 | 4
4 | 4
(4 rows)
The same concerns update:
create temp table test as
select id from generate_series(1, 4) id;
update test
set id = id+ 1
returning id, (select sum(id) from test);
id | sum
----+-----
2 | 10
3 | 10
4 | 10
5 | 10
(4 rows)

DB2 - update increment based on timestamp

After a complex operation (some database merge-ing) I have a table that needs to be updated based on timestamp.
JobsTable
Id Time_stamp Resource RunNumber
121 1 A 1
122 2 A 1
123 3 B 1
124 4 B 1
125 5 A 2
The point is to Update the RunNumber column incrementally for each resource based on timestamp. So in the end the expected result is:
Id Time_stamp Resource RunNumber
121 1 A 1
122 2 A 2 //changed
123 3 B 1
124 4 B 2 //changed
125 5 A 3 //changed
I tried doing this in multiple ways. Since DB2 update does not support Join or With statements I tried something like:
update JOBSTABLE JT
SET RunNumber =
(SELECT RunNumber
FROM (Select ID, ROW_NUMBER() OVER (ORDER BY TIME_STAMP ) RunNumber from JobsTable, ORDER BY TIME_STAMP) AS AAA
WHERE AAA.ID = JT.ID)
WHERE ID = ?
Error:
Assignment of a NULL value to a NOT NULL column "TBSPACEID=6, TABLEID=16, COLNO=2" is not allowed.. SQLCODE=-407, SQLSTATE=23502, DRIVER=3.64.82 SQL Code: -407, SQL State: 23502
Is this even possible? (I am aiming at doing this operation in a single query rather than using Cursors, etc..)
Thank you
Firstly, your subselect has a syntax error, which tells me it's not the exact statement that you are trying to run. The error message is pretty clear -- in your actual statement the subselect sometimes returns NULL.
Secondly, you should probably be numbering rows within a partition by resource.
Thirdly, you could probably do with a single subselect anyway -- this is based on the statement you published:
update JOBSTABLE JT
SET RunNumber =
(SELECT ROW_NUMBER() OVER (partition by resource ORDER BY TIME_STAMP )
from JobsTable where id = JT.ID)

How to find the last descendant (that matches other criteria) in a linear “ancestor-descendant” relationship

This question is based on the following question, but with an additional requirement: PostgreSQL: How to find the last descendant in a linear "ancestor-descendant" relationship
Basically, what I need is a Postgre-SQL statement that finds the last descendant in a linear “ancestor-descendant” relationship that matches additional criteria.
Example:
Here the content of table "RELATIONSHIP_TABLE":
id | id_ancestor | id_entry | bool_flag
---------------------------------------
1 | null | a | false
2 | 1 | a | false
3 | 2 | a | true
4 | 3 | a | false
5 | null | b | true
6 | null | c | false
7 | 6 | c | false
Every record within a particular hierarchy has the same "id_entry"
There are 3 different “ancestor-descendant” relationships in this example:
1. 1 <- 2 <- 3 <- 4
2. 5
3. 6 <- 7
Question PostgreSQL: How to find the last descendant in a linear "ancestor-descendant" relationship shows how to find the last record of each relationship. In the example above:
1. 4
2. 5
3. 7
So, what I need this time is the last descendant by "id_entry" whose "bool_flag" is set to true. In the example above:
1. 3
2. 5
3. <empty result>
Does anyone know a solution?
Thanks in advance :)
QStormDS
Graphs, trees, chains, etc represented as edge lists are usually good uses for recursive common table expressions - i.e. WITH RECURSIVE queries.
Something like:
WITH RECURSIVE walk(id, id_ancestor, id_entry, bool_flag, id_root, generation) AS (
SELECT id, id_ancestor, id_entry, bool_flag, id, 0
FROM RELATIONSHIP_TABLE
WHERE id_ancestor IS NULL
UNION ALL
SELECT x.id, x.id_ancestor, x.id_entry, x.bool_flag, walk.id_root, walk.generation + 1
FROM RELATIONSHIP_TABLE x INNER JOIN walk ON x.id_ancestor = walk.id
)
SELECT
id_entry, id_root, id
FROM (
SELECT
id, id_entry, bool_flag, id_root, generation,
max(CASE WHEN bool_flag THEN generation END ) OVER w as max_enabled_generation
FROM walk
WINDOW w AS (PARTITION BY id_root ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
) x
WHERE generation = max_enabled_generation;
... though it feels like there really should be a better way to do this than tracking how many generations we've walked down each path.
If id_entry is common for all members of a tree, you can avoid needing to track id_root. You should create a UNIQUE constraint on (id_entry, id) and a foreign key constraint on FOREIGN KEY (id_entry, id_ancestor) REFERENCES (id_entry, id) to make sure that the ordering is consistent, then use:
WITH RECURSIVE walk(id, id_ancestor, id_entry, bool_flag, generation) AS (
SELECT id, id_ancestor, id_entry, bool_flag, 0
FROM RELATIONSHIP_TABLE
WHERE id_ancestor IS NULL
UNION ALL
SELECT x.id, x.id_ancestor, x.id_entry, x.bool_flag, walk.generation + 1
FROM RELATIONSHIP_TABLE x INNER JOIN walk ON x.id_ancestor = walk.id
)
SELECT
id_entry, id
FROM (
SELECT
id, id_entry, bool_flag, generation,
max(CASE WHEN bool_flag THEN generation END ) OVER w as max_enabled_generation
FROM walk
WINDOW w AS (PARTITION BY id_entry ROWS BETWEEN CURRENT ROW AND UNBOUNDED FOLLOWING)
) x
WHERE generation = max_enabled_generation;
Since this gives you a table of final descendents matched up with root parents, you can just filter with a regular WHERE clause now, just append AND bool_flag. If you instead want to exclude chains that have bool_flag set to false at any point along the way, you can add WHERE bool_value in the RECURSIVE query's join.
SQLFiddle example: http://sqlfiddle.com/#!12/92a64/3
WITH RECURSIVE tail AS (
SELECT id AS opa
, id, bool_flag FROM boolshit
WHERE bool_flag = True
UNION ALL
SELECT t.opa AS opa
, b.id, b.bool_flag FROM boolshit b
JOIN tail t ON b.id_ancestor = t.id
)
SELECT *
FROM boolshit bs
WHERE bs.bool_flag = True
AND NOT EXISTS (
SELECT * FROM tail t
WHERE t.opa = bs.id
AND t.id <> bs.id
AND t.bool_flag = True
);
Explanation: select all records that have the bool_flag set,
EXCEPT those that have offspring (direct or indirect) that have the bool_flag set, too. This effectively picks the last record of the chain that has the flag set.