Update column from other table column with relation - postgresql

I have 2 tables
sale_order_line
id| order_id| date_order
1 | 5 | null
2 | 6 | null
3 | 6 | null
and
sale_oder
id| date_order
5 | '2020-08-25'
6 | '2020-09-28'
How can I construct query that will update all date_order in the sale_order_line table based on order_id
Desired output would be
id| order_id| date_order
1 | 5 | '2020-08-25'
2 | 6 | '2020-09-28'
3 | 6 | '2020-09-28'

As documented in the manual you can use a FROM clause in the UPDATE statement:
update sale_order_line sol
set date_order = so.date_order
from sale_order so
where so.id = sol.order_id;

Besides using Postgres' update join syntax, you could also use a correlated subquery here:
UPDATE sale_order_line sol
SET date_order = (SELECT so.date_order FROM sale_order so WHERE so.id = sol.order_id);

Related

SQL select the max from each group and given them different lables

For the following tables:
-- People
id | category | count
----+----------+-------
1 | a | 2
1 | a | 3
1 | b | 2
2 | a | 2
2 | b | 3
3 | a | 1
3 | a | 2
I know that I can find the max count for each id in each category by doing:
SELECT id, category, max(count) from People group by category, id;
With result:
id | category | max
----+----------+-------
1 | a | 3
1 | b | 2
2 | a | 2
2 | b | 3
3 | a | 2
But what if now I want to label the max values differently, like:
id | max_b_count | max_a_count
----+-------------+------------
1 | 2 | 3
2 | 3 | 2
3 | Null | 2
Should I do something like the following?
WITH t AS (SELECT id, category, max(count) from People group by category, id)
SELECT t.id, t.count as max_a_count from t where t.category = 'a'
FULL OUTER JOIN t.id, t.count as max_b_count from t where t.category = 'b'
on t.id;
It looks weird to me.
This is the exact use case why the filter_clause was added to the Aggregate Expressions
With filter_clause you may limit which row you aggregate
aggregate_name ( * ) [ FILTER ( WHERE filter_clause ) ]
Your example
SELECT id,
max(count) filter (where category = 'a') as max_a_count,
max(count) filter (where category = 'b') as max_b_count
from People
group by id
order by 1;
id|max_a_count|max_b_count|
--+-----------+-----------+
1| 3| 2|
2| 2| 3|
3| 2| |
This is one way you can do it:
with T as (select id, category, max(count_ab) maks
from people
group by id, category
order by id)
select t3.id
, (select t1.maks from T t1 where category = 'b' and t1.id = t3.id) max_b_count
, (select t2.maks from T t2 where category = 'a' and t2.id = t3.id) max_a_count
from T t3
group by t3.id
order by t3.id
Here is a demo
Also, as you can see, I have changed the name of the column count to count_ab because it is not a good practice to use keywords as columns names.

LEFT JOIN in Postgres when there is a WHERE clause [duplicate]

This question already has answers here:
Left Outer Join doesn't return all rows from my left table?
(3 answers)
Closed 9 months ago.
I've been using PosgreSQL almost daily for over 11 years now, and today I wrote what I though was a very simple query with a LEFT JOIN that doesn't behave the way that I expected. I'm lucky I caught the bug, but it has me concerned that there is something fundamental here that I a missing. Please look at the following to be able reproduce.
CREATE TEMP TABLE tbl_a(date date);
INSERT INTO tbl_a VALUES ('2022-01-01'), ('2022-01-02'), ('2022-01-03'), ('2022-01-04');
CREATE TEMP TABLE sale(date date, item_id int);
INSERT INTO sale VALUES ('2022-01-02', 2), ('2022-01-03', 2), ('2022-01-04', 3);
When I run the following query I get the results I expect with a LEFT JOIN
SELECT t.*, s.item_id FROM tbl_a AS t LEFT JOIN sale AS s ON t.date = s.date;
+------------+---------+
| date | item_id |
+------------+---------+
| 2022-01-01 | NULL |
| 2022-01-02 | 2 |
| 2022-01-03 | 2 |
| 2022-01-04 | 3 |
+------------+---------+
I get every record in tbl_a and since I have no sale records for 2022-01-01, I get a NULL.
However, when I add a WHERE to the query I get an unexpected result.
SELECT t.*, s.item_id FROM tbl_a AS t LEFT JOIN sale AS s ON t.date = s.date WHERE s.item_id = 2;
+------------+---------+
| date | item_id |
+------------+---------+
| 2022-01-02 | 2 |
| 2022-01-03 | 2 |
+------------+---------+
Note: there is no record for 2022-01-01 or 2022-01-04.
However, if I rewrite the query with a CTE, I get the results I expect.
WITH s AS (select * from sale WHERE item_id = 2) SELECT t.*, s.item_id FROM tbl_a AS t LEFT JOIN s ON t.date = s.date ORDER BY t.date;
+------------+---------+
| date | item_id |
+------------+---------+
| 2022-01-01 | NULL |
| 2022-01-02 | 2 |
| 2022-01-03 | 2 |
| 2022-01-04 | NULL |
+------------+---------+
My question is why do the above two queries yield different results.
Note:
SELECT version();
+-----------------------------------------------------------------------------------------------------------------------------------+
| version |
+-----------------------------------------------------------------------------------------------------------------------------------+
| PostgreSQL 13.7 (Ubuntu 13.7-1.pgdg20.04+1) on x86_64-pc-linux-gnu, compiled by gcc (Ubuntu 9.4.0-1ubuntu1~20.04.1) 9.4.0, 64-bit |
+-----------------------------------------------------------------------------------------------------------------------------------+
Thats due to the order of execution from postgres.
Whenever you run the 1st query you are joining both tables then filtering it with the where item_id = 2).
In the second query you are filtering tbl_a then joining the result with b.
The equivalent of the 1st query would be something like:
WITH s AS
(select * from sale)
SELECT t.*, s.item_id
FROM tbl_a AS t
LEFT JOIN s ON t.date = s.date
WHERE s.item_id = 2
ORDER BY t.date;

SELECT DISTINCT on a ordered subquery's table

I'm working on a problem, involving these two tables.
books
isbn | title | author
------------+-----------------------------------------+------------------
1840918626 | Hogwarts: A History | Bathilda Bagshot
3458400871 | Fantastic Beasts and Where to Find Them | Newt Scamander
9136884926 | Advanced Potion-Making | Libatius Borage
transactions
id | patron_id | isbn | checked_out_date | checked_in_date
----+-----------+------------+------------------+-----------------
1 | 1 | 1840918626 | 2012-05-05 | 2012-05-06
2 | 4 | 9136884926 | 2012-05-05 | 2012-05-06
3 | 2 | 3458400871 | 2012-05-05 | 2012-05-06
4 | 3 | 3458400871 | 2018-04-29 | 2018-05-02
5 | 2 | 9136884926 | 2018-05-03 | NULL
6 | 1 | 3458400871 | 2018-05-03 | 2018-05-05
7 | 5 | 3458400871 | 2018-05-05 | NULL
the query "Make a list of all book titles and denote whether or not a copy of that book is checked out." so pretty much just the first table with a checked out column.
im trying to SELECT DISTINCT on a sub query with the checkout books first, but that doesn't work. I've researched and others say to accomplish this use a GROUP BY clause instead of DISTINCT but the examples they provide are one column queries and when more columns are added it doesn't work.
this is my closest attempt
SELECT DISTINCT ON (title)
title, checked_out
FROM(
SELECT b.title, t.checked_in_date IS NULL AS checked_out
FROM transactions t
natural join books b
ORDER BY checked_out DESC
) t;
or you can join only transactions where books are not checked in:
SELECT b.title, t.isbn IS NOT NULL AS checked_out
, t.checked_out_date
FROM books b
LEFT JOIN transactions t ON t.isbn = b.isbn AND t.checked_in_date IS NULL
ORDER BY checked_out DESC
I adjusted your attempt a little bit. Basically I changed the way your data is joined
SELECT DISTINCT ON (title)
title, checked_out
FROM(
SELECT b.title, t.checked_in_date IS NULL AS checked_out
FROM books b
LEFT OUTER JOIN transactions t USING (isbn)
ORDER BY checked_out DESC
) t;

How to order rows with linked parts in PostgreSQL

I have a table A with columns: id, title, condition
And i have another table B with information about position for some rows from table A. Table B have columns id, next_id, prev_id
How to sort rows from A based on information from table B?
For example,
Table A
id| title
---+-----
1 | title1
2 | title2
3 | title3
4 | title4
5 | title5
Table B
id| next_id | prev_id
---+-----
2 | 1 | null
5 | 4 | 3
I want to get this result:
id| title
---+-----
2 | title2
1 | title1
3 | title3
5 | title5
4 | title4
And after apply this sort, i want to sort by condition column yet.
I've already spent a lot of time looking for a solution, and hope for your help.
You have to add weights to your data, so you can order accordingly. This example uses next_id, not sure if you need to use prev_id, you don't explain the use of it.
Anyway, here's a code example:
-- Temporal Data for the test:
CREATE TEMP TABLE table_a(id integer,tittle text);
CREATE TEMP TABLE table_b(id integer,next_id integer, prev_id integer);
INSERT INTO table_a VALUES
(1,'title1'),
(2,'title2'),
(3,'title3'),
(4,'title4'),
(5,'title5');
INSERT INTO table_b VALUES
(2,1,null),
(5,4,3);
-- QUERY:
SELECT
id,tittle,
CASE -- Adding weight
WHEN next_id IS NULL THEN (id + 0.1)
ELSE next_id
END AS orden
FROM -- Joining tables
(SELECT ta.*,tb.next_id
FROM table_a ta
LEFT JOIN table_b tb
ON ta.id=tb.id)join_a_b
ORDER BY orden
And here's the result:
id | tittle | orden
--------------------------
2 | title2 | 1
1 | title1 | 1.1
3 | title3 | 3.1
5 | title5 | 4
4 | title4 | 4.1

Postgresql summing duplicate elements

In the table can exsist 2 lines that give the same information only a single column value is different. Basically the data is duplicated because of this 1 column. Can I somehow sum otherelement in such a manner that it takes this duplication into account ?
To illustrate the idea of the problem
Example:
|id|type|val1|val2|
|1 | 2 | 1 | 1 |
|1 | 3 | 1 | 1 |
|1 | 2 | 2 | 2 |
|1 | 3 | 2 | 2 |
Expected result
|id|type|val1|val2|count|
|1 |2,3 | 3 | 3 | 2 |
Actual result
|id|type|val1|val2|count|
|1 |2,3 | 6 | 6 | 4 |
In the actual data the type and val come from 2 different tables connected by 3rd table, so the query is like this:
SELECT id,
array_to_string(array_agg(DISTINCT x.type ORDER BY x.type), ','::text) AS type,
sum(y.val1) AS val1,
sum(y.val2) AS val2,
count(y.val1) AS count
FROM a
JOIN x ON x.a_id = a.id AND x.active = true
JOIN y ON y.a_id = a.id AND y.active = true
GROUP BY a.id
SOLUTION
SELECT id,
array_to_string(array_agg(DISTINCT x.type ORDER BY x.type), ','::text) AS type,
sum(distinct y.val1) AS val1,
sum(distinct y.val2) AS val2,
count(distinct y.val1) AS count
FROM a
JOIN x ON x.a_id = a.id AND x.active = true
JOIN y ON y.a_id = a.id AND y.active = true
GROUP BY a.id