How to compute the sum of multiple columns in PostgreSQL - postgresql

I would like to know if there's a way to compute the sum of multiple columns in PostgreSQL.
I have a table with more than 80 columns and I have to write a query that adds each value from each column.
I tried with SUM(col1, col2, col3 etc) but it didn't work.

SELECT COALESCE(col1,0) + COALESCE(col2,0)
FROM yourtable

It depends on how you'd like to sum the values. If I read your question correctly, you are looking for the second SELECT from this example:
template1=# SELECT * FROM yourtable ;
a | b
---+---
1 | 2
4 | 5
(2 rows)
template1=# SELECT a + b FROM yourtable ;
?column?
----------
3
9
(2 rows)
template1=# SELECT SUM( a ), SUM( b ) FROM yourtable ;
sum | sum
-----+-----
5 | 7
(1 row)
template1=# SELECT SUM( a + b ) FROM yourtable ;
sum
-----
12
(1 row)
template1=#

Combined the current answers and used this to get total SUM:
SELECT SUM(COALESCE(col1,0) + COALESCE(col2,0)) FROM yourtable;

SELECT(
SELECT SUM(t.f)
FROM (VALUES (yourtable.col1), (yourtable.col2), (yourtable.col3)) t(f)
)
FROM yourtable;

Related

PostgreSQL: Find percentages of total_films_rented

The code below gives me the following results
Early: 7738
Late: 6586
On Time: 1720
How would I take this a step further and add a third column that finds the percentages?
Here is a link to the ERD and database set-up: https://www.postgresqltutorial.com/postgresql-sample-database/
WITH
t1
AS
(
SELECT *, DATE_PART('day', return_date - rental_date) AS days_rented
FROM rental
),
t2
AS
(
SELECT rental_duration, days_rented,
CASE WHEN rental_duration > days_rented THEN 'Early'
WHEN rental_duration = days_rented THEN 'On Time'
ELSE 'Late'
END AS rental_return_status
FROM film f, inventory i, t1
WHERE f.film_id = i.film_id AND t1.inventory_id = i.inventory_id
)
SELECT rental_return_status, COUNT(*) AS total_films_rented
FROM t2
GROUP BY 1
ORDER BY 2 DESC;
You can use a window function with one CTE table (instead of 2):
WITH raw_status AS (
SELECT rental_duration - DATE_PART('day', return_date - rental_date) AS days_remaining
FROM rental r
JOIN inventory i ON r.inventory_id=i.inventory_id
JOIN film f on f.film_id=i.film_id
)
SELECT CASE WHEN days_remaining > 0 THEN 'Early'
WHEN days_remaining = 0 THEN 'On Time'
ELSE 'Late' END AS rental_status,
count(*),
(100*count(*))/sum(count(*)) OVER () AS percentage
FROM raw_status
GROUP BY 1;
rental_status | count | percentage
---------------+-------+---------------------
Early | 7738 | 48.2298678633757168
On Time | 1720 | 10.7205185739217153
Late | 6586 | 41.0496135627025679
(3 rows)
Disclosure: I work for EnterpriseDB (EDB)
Use a window function to get the sum of the count column (sum(count(*)) over ()), then just divide the count by that (count(*)/sum(count(*)) over ()). Multiply by 100 to make it a percentage.
psql (12.1 (Debian 12.1-1))
Type "help" for help.
testdb=# CREATE TABLE faket2 AS (
SELECT 'early' AS rental_return_status UNION ALL
SELECT 'early' UNION ALL
SELECT 'ontime' UNION ALL
SELECT 'late');
SELECT 4
testdb=# SELECT
rental_return_status,
COUNT(*) as total_films_rented,
(100*count(*))/sum(count(*)) over () AS percentage
FROM faket2
GROUP BY 1
ORDER BY 2 DESC;
rental_return_status | total_films_rented | percentage
----------------------+--------------------+---------------------
early | 2 | 50.0000000000000000
late | 1 | 25.0000000000000000
ontime | 1 | 25.0000000000000000
(3 rows)

How to get the result of table contains numeric and strings using where condition in postgres

I have a table like below
When I select item_no>'1623G' from above table
I want to print the result below
1623H | 1623I | 1666 | 1674 | 1912 | 1952 | 1953
I am trying below command
select * from t where substring(item_no,'([0-9]+)') :: int > 1623G
But it's not giving result
please help
I would go the regexp way:
demo:db<>fiddle
WITH cte AS (
SELECT
item_no,
regexp_replace(item_no, '\D', '', 'g')::int AS digit,
regexp_replace(item_no, '\d', '', 'g') AS nondigit,
regexp_replace('200a', '\D', '', 'g')::int AS compare_digit,
regexp_replace('200a', '\d', '', 'g') AS compare_nondigit
FROM t
)
SELECT
item_no
FROM
cte
WHERE
(digit > compare_digit) OR (digit = compare_digit AND nondigit > compare_nondigit)
Splitting both values (the row value and the comparing one) into its both parts (digits and non-digits) and compare each part separately.
I am curious if there is better solution.
You can use CONVERT_TO as:
testdb1=# CREATE TABLE t (item_no varchar(20));
CREATE TABLE
testdb1=# INSERT INTO t VALUES('2'),('20'),('200'),('200a'),('200b'),('200c'),('2000');
INSERT 0 7
testdb1=# SELECT * FROM t;
item_no
---------
2
20
200
200a
200b
200c
2000
(7 rows)
testdb1=# select * from t where substring(convert_to(item_no,'SQL_ASCII')::text,3)::int > substring(convert_to('2a','SQL_ASCII')::text,3)::int;
item_no
---------
200
200a
200b
200c
2000
(5 rows)
testdb1=# select * from t where substring(convert_to(item_no,'SQL_ASCII')::text,3)::int > substring(convert_to('150','SQL_ASCII')::text,3)::int;
item_no
---------
200
200a
200b
200c
2000
(5 rows)

Refer to current row in window function

Is it possible to refer to the current row in a window partition? I want to do something like the following:
SELECT min(ABS(variable - CURRENT.variable)) over (order by criterion RANGE UNBOUNDED PRECEDING)
That is, i want to find in the given partition the variable which is closest to the current value. Is is possible to do something like that?
As an example, from:
criterion | variable
1 2
2 4
3 2
4 7
5 6
We would obtain:
null
2
0
3
1
Thanks
As far as I know, this cannot be done with window functions.
But it can be done with a self join:
SELECT a.id,
a.variable,
min(abs(a.variable - b.variable))
FROM mydata a
LEFT JOIN mydata b
ON (b.criterion < a.criterion)
GROUP BY a.id, a.variable
ORDER BY a.id;
If I understand correctly:
with t (v) as (values (-5),(-2),(0),(1),(3),(10))
select v,
least(
v - lag(v) over (order by v),
lead(v) over (order by v) - v
) as closest
from t
;
v | closest
----+---------
-5 | 3
-2 | 2
0 | 1
1 | 1
3 | 2
10 | 7
Hope this could help you (pay attention for performance problems).
I tried this in MSSQL (at bottom you'll find POSTGRESQL version):
CREATE TABLE TX (CRITERION INT, VARIABILE INT);
INSERT INTO TX VALUES (1,2), (2,4),(3,2),(4,7), (5,6);
SELECT CRITERION, MIN_DELTA FROM
(
SELECT TX.CRITERION
, MIN(ABS(B.TX2_VAR - TX.VARIABILE)) OVER (PARTITION BY TX.CRITERION) AS MIN_DELTA
, RANK() OVER (PARTITION BY TX.CRITERION ORDER BY ABS(B.TX2_VAR - TX.VARIABILE) ) AS MIN_RANK
FROM TX
CROSS APPLY (SELECT TX2.CRITERION AS TX2_CRIT, TX2.VARIABILE AS TX2_VAR FROM TX TX2 WHERE TX2.CRITERION < TX.CRITERION) B
) C
WHERE MIN_RANK=1
ORDER BY CRITERION
;
Output:
CRITERION MIN_DELTA
----------- -----------
2 2
3 0
4 3
5 1
POSTGRESQL Version (tested on Rextester http://rextester.com/VMGJ87600):
CREATE TABLE TX (CRITERION INT, VARIABILE INT);
INSERT INTO TX VALUES (1,2), (2,4),(3,2),(4,7), (5,6);
SELECT * FROM TX;
SELECT CRITERION, MIN_DELTA FROM
(
SELECT TX.CRITERION
, MIN(ABS(B.TX2_VAR - TX.VARIABILE)) OVER (PARTITION BY TX.CRITERION) AS MIN_DELTA
, RANK() OVER (PARTITION BY TX.CRITERION ORDER BY ABS(B.TX2_VAR - TX.VARIABILE) ) AS MIN_RANK
FROM TX
LEFT JOIN LATERAL (SELECT TX2.CRITERION AS TX2_CRIT, TX2.VARIABILE AS TX2_VAR FROM TX TX2 WHERE TX2.CRITERION < TX.CRITERION) B ON TRUE
) C
WHERE MIN_RANK=1
ORDER BY CRITERION
;
DROP TABLE TX;
Output:
criterion variabile
1 1 2
2 2 4
3 3 2
4 4 7
5 5 6
criterion min_delta
1 1 NULL
2 2 2
3 3 0
4 4 3
5 5 1

PostgreSQL - How to get the previous(lag) calculated value

I would like to get the previous(lag) calculated value?
id | value
-------|-------
1 | 1
2 | 3
3 | 5
4 | 7
5 | 9
What I am trying to achieve is this:
id | value | new value
-------|-------|-----------
1 | 1 | 10 <-- 1 * lag(new_value)
2 | 3 | 30 <-- 3 * lag(new_value)
3 | 5 | 150 <-- 5 * lag(new_value)
4 | 7 | 1050 <-- 7 * lag(new_value)
5 | 9 | 9450 <-- 9 * lag(new_value)
What I have tried:
SELECT value,
COALESCE(lag(new_value) OVER () * value, 10) AS new_value
FROM table
Error:
ERROR: column "new_value" does not exist
Similar to Juan's answer but I thought I'd post it anyway. It at least avoids the need for the ID column and doesn't have the empty row at the end:
with recursive all_data as (
select value, value * 10 as new_value
from data
where value = 1
union all
select c.value,
c.value * p.new_value
from data c
join all_data p on p.value < c.value
where c.value = (select min(d.value)
from data d
where d.value > p.value)
)
select *
from all_data
order by value;
The idea is to join exactly one row in the recursive part to exactly one "parent" row. While the "exactly one parent" can be done with a derived table and a lateral join (which surprisingly does allow the limit). The "exactly one row" from the "child" in the recursive part can unfortunately only be done using the sub-select with a min().
The where c.value= (...) wouldn't be necessary if it was possible to use an order by and limit in the recursive part as well, but unfortunately that is not supported in the current Postgres version.
Online example: http://rextester.com/WFBVM53545
My bad, this isnt that easy as I thought. Got a very close result but still need some tunning.
DEMO
WITH RECURSIVE t(n, v) AS (
SELECT MIN(value), 10
FROM Table1
UNION ALL
SELECT (SELECT min(value) from Table1 WHERE value > n),
(SELECT min(value) from Table1 WHERE value > n) * v
FROM t
JOIN Table1 on t.n = Table1.value
)
SELECT n, v
FROM t;

Postgresql: Select value difference between values in Integer column

My question is simple. Say I have the following column:
order_in_group
integer
------
1
2
3
5
6
9
I would like the query result to be the difference between the current and next values which is bigger then 1:
value1 value2 difference
integer integer integer
------- ------- -------
3 5 2
6 9 3
Any help will be great.
Try this:
with q(i) as (
select unnest(array[1,2,3,5,6,9])
)
select prev, curr, curr- prev diff
from (
select i curr, lag(i) over (order by i) prev
from q
) s
where curr > prev+ 1;
prev | curr | diff
------+------+------
3 | 5 | 2
6 | 9 | 3
(2 rows)
You should be able to just use LAG to get the previous row to compare with;
WITH cte AS (
SELECT order_in_group value2,
LAG(order_in_group) OVER (ORDER BY order_in_group) value1
FROM mytable
)
SELECT value1, value2, value2-value1 difference
FROM cte
WHERE value2-value1 > 1;