Postgresql data calculation - postgresql

Im trying to do some calculations using postgres, but no sucess so far. My query goes something like this:
select ....,
(select json_agg(data_table)
from (..... HERE GOES DE RESULT OF THE CALCULATION + a lot of business and data.... ) as data_table)
from foo
So i gonna exemplify with a table:
create temp table tbdata (id smallint, parent_id smallint, value numeric(25,2));
insert into tbdata values(1, null, 100), (2, 1, 50), (3, 1, 49), (4, 3, 20), (5, 3, 29);
select * from tbdata;
I need to calculate the difference between the sum of the siblings and the parent value. Example:
ID 2(50) + ID 3(49) = 99
ID 1(parent) = 100
so i need to add 1 to any of the childs (lets say 3), the result gonna be:
ID 2(50) + ID 3(49 + 1) = 100
ID 1(parent) = 100
After that, my ID3 have changed, so i need to update any of his childs:
ID 4(20) + ID 5(29) = 49
ID 3(parent) = 50
then again, updating value of ID 5 with the difference (50 - 49)
ID 4(20) + ID 5(29 + 1) = 50
ID 3(parent) = 50
I tried using recursive queries, windows function, and cte, but i always stuck in something. I was able to do using a function with a loop, but i dont want to do that.
Theres any way i can do it with a single SQL?

Related

how to get a average of values except negative numbers in postgres

I have a table which has has a column that i need to take the average but in my average i want to exclude minues numbers
im sure this im doing is wrong,but how can i do it?
SELECT power_curve_quality where m_turbine_id='192.168.30.82'
and m_date>='2020-08-01' and m_date<'2020-09-01'
FROM wh.t_statistics_daily_ext
EXCEPT select power_curve_quality<0
FROM wh.t_statistics_daily_ext
where m_turbine_id='192.168.30.82' and m_date>='2020-08-01' and m_date<'2020-09-01'
If you want the negative observations in the divisor, then this will do it:
select avg(greatest(power_curve_quality, 0))
from t_statistics_daily_ext
where . . .
If you want to exclude negative observations altogether, then use this:
select avg(power_curve_quality)
from t_statistics_daily_ext
where . . .
and power_curve_quality >= 0
Something like:
create table avg(id int, fld_1 int);
insert into avg values(1, 2), (2, -1), (3, 4), (4, -4), (5, 3);
select avg(fld_1) from avg where fld_1 >= 0;
Just add power_curve_quality >= 0 into the WHERE clause like so:
SELECT
avg(power_curve_quality)
FROM
t_statistics_daily_ext
WHERE
power_curve_quality >= 0
and m_turbine_id = '192.168.30.82'
and m_date >= '2020-08-01'
and m_date < '2020-09-01'
;

postgresql: "...where X IN <array type column values>" syntax?

I'm having problem with using the values of an array column in a where clause. Complete example to reproduce:
create type public.genre_type as enum ('scifi', 'fantasy', 'crime', 'horror', 'classics');
create table public.reader_profile(
id integer,
fave_genres genre_type ARRAY
);
create table public.books(
id serial not null,
title text,
genre_type public.genre_type
);
insert into public.reader_profile(id, fave_genres) values (1, array['crime', 'horror']::public.genre_type[]);
insert into public.reader_profile(id, fave_genres) values (2, array['fantasy', 'scifi']::public.genre_type[]);
insert into public.reader_profile(id, fave_genres) values (3, array['scifi', 'classics']::public.genre_type[]);
insert into public.books(title, genre_type) values ('gone with the wind', 'classics');
insert into public.books(title, genre_type) values ('Foundation', 'scifi');
insert into public.books(title, genre_type) values ('Dune', 'scifi');
-- THE FOLLOWING FAILS!!!
select * from public.books
where genre_type in (
select fave_genres from public.reader_profile where id = 2
);
I've tried ...where genre_type = ANY() per other stackoverflow answers as well as ...where genre_type <# () and I can't get anything to work! It seems the inner query (which works) is being return as an array type and not a list of values or something. Any help appreciated!!
I agree with #Hogan that this seems doable with a JOIN but the syntax you are looking for is the following:
SELECT *
FROM books
WHERE genre_type = ANY(ARRAY(SELECT fave_genres FROM reader_profile WHERE id = 2))
;
Demo
Can I suggest using a join instead?
select *
from public.books b
join public.reader_profile fg on b.genre_type = ANY(rp.fave_genres) and fg.id = 2

TSQL - Generating Quarter output

I have a table with 3 columns X, Date and Qtr as attached.
I need to get an output as mentioned in attachment (data taken only for 8 quarters)
create table mQtr
( X varchar(5), XDate date, Qtr int);
insert into mQtr
values ('X1', '2011-11-01', 4), ('X2', '2011-10-01', 4),
('X3', '2011-09-01', 3), ('X4', '2011-08-01', 3),
('X5', '2011-07-01', 3), ('X6', '2011-06-01', 2),
('X7','2011-05-01',2), ('X8','2011-04-01',2),
('X9','2011-03-01',1), ('X10','2011-02-01',1),
('X11','2011-01-01',1), ('X12','2010-12-01',4),
('X13','2010-11-01',4), ('X14','2010-10-01',4),
('X15','2010-09-01',3), ('X16','2010-08-01',3),
('X17','2010-07-01',3), ('X18','2010-06-01',2),
('X19','2010-05-01',2), ('X20','2010-04-01',2),
('X21','2010-03-01',1), ('X22','2010-02-01',1),
('X23','2010-01-01',1), ('X24','2009-12-01',4)
What I am expecting the following result set based on Qtr value, I would like to generate sum of X values. Output should be some like this:
Qtr mOutput
----------------
1 x1+x2
2 x3+x4+x5
3 x6+x7+x8
4 x9+x10+x11
5 x12+x13+x14
6 x15+x16+x17
7 x18+x19+x20
8 x21+x22+x23
Please help me to frame the query.
Thanks,
Naveen
You can use JOIN and GROUP BY with HAVING count(*) = 3 in statement. This Link can be useful

PostgreSQL, advanced swapping

I get some help on swapping certain data in rows of same table here.
Unfortunately I can't apply those solution in practice because I represent a problem too weak so with offered solutions I can't get expected results.
For that I improve examples and make it very easy to use and for try at the same time more likely my concrete situation with hope that this post will not be treated as duplicate or offensive.
Creating a table:
DROP TABLE IF EXISTS kalksad1;
CREATE TABLE kalksad1(
kalk_id int PRIMARY KEY,
brkalk integer,
brred integer,
description text
);
INSERT INTO kalksad1 VALUES
(12, 2, 5, 'text index 12 doc 2 row 5'),
(26, 2, 1, 'text index 26 doc 2 row 1'),
(30, 2, 2, 'text index 30 doc 2 row 2'),
(32, 4, 1, 'text index 32 doc 4 row 1'),
(36, 1, 1, 'text index 36 doc 1 row 1'),
(37, 1, 2, 'text index 37 doc 1 row 2'),
(38, 5, 1, 'text index 38 doc 5 row 1'),
(39, 5, 2, 'text index 39 doc 5 row 2'),
(42, 2, 3, 'text index 42 doc 2 row 3'),
(43, 2, 4, 'text index 43 doc 2 row 4'),
(46, 3, 1, 'text index 46 doc 3 row 1'),
(47, 3, 2, 'text index 47 doc 3 row 2');
What is needed?
To make a query which will swap numbers only in column 'brred' of same 'brkalk'.
Both, 'brred' and 'brkalk' are defined externally, through program.
For example purpose we will take brkalk=2, brred=3.
That mean we should swap brred value only in rows WHEN brkalk=2.
Here are two offered solution's which may be taken as reference.
Both solutions can be useful if they would work.
First one because it can swap rows no matter of order and distance and second because it swap with only first row upper or lower what is most common need.
Problem with second solution is that I don't know what it swaps but swaps first and last row instead of row 3 and 2.
That should be repaired.
First query don't work at all in new circumstances so I would like if anyone could repair it. It can be useful for swapping rows no matter of "direction" by external arguments, say swap rows 4 and 1.
Just to clarify, when I say "swap row" I mean to swap ONLY values in 'brred' column which belongs to same 'brkalk' (in this case 2).
First query:
UPDATE kalksad1 dst
SET brred=src.brred
FROM kalksad1 src
WHERE src.brkalk='2'
AND dst.kalk_id IN(2,3)
AND src.kalk_id IN(2,3)
AND dst.kalk_id <> src.kalk_id;
Second query
WITH cte1 AS (
SELECT row_number() OVER(ORDER BY kalk_id ASC) AS row_num, kalk_id, brred
FROM kalksad1
WHERE kalk_id >= 3 ORDER BY kalk_id LIMIT 2
)
UPDATE kalksad1 AS t
SET brred = COALESCE(c2.brred, t.brred)
FROM cte1 AS c1
LEFT OUTER JOIN cte1 AS c2 ON c2.row_num <> c1.row_num
WHERE t.kalk_id = c1.kalk_id AND brkalk='2';
To view data it is best to use:
SELECT * FROM kalksad1 WHERE brkalk='2' ORDER BY brred;
I would like that someone repair upper queries to become workable according to described needs or offer new solution which may be usable for that kind of swapping.
So, thanks to Roman and wildplasser I get this...
Private Function swap_row(ByVal doc_num As Integer, ByVal src_row As Integer, ByVal dest_row As Integer) As Integer
Dim affected As Integer = 0
Dim conn As NpgsqlConnection = getConnection()
Dim t As NpgsqlTransaction = conn.BeginTransaction()
Using cmd As New NpgsqlCommand( _
"UPDATE " & myKalkSadTable & " AS dst SET brred = src.brred " & _
"FROM " & myKalkSadTable & " AS src " & _
"WHERE(src.brkalk = " & doc_num.ToString & ") " & _
"AND dst.brkalk = " & doc_num.ToString & " " & _
"AND dst.brred IN (" & src_row.ToString & "," & dest_row.ToString & ") " & _
"AND src.brred IN (" & src_row.ToString & "," & dest_row.ToString & ") " & _
"AND src.kalk_id <> dst.kalk_id", conn)
affected = CInt(cmd.ExecuteNonQuery())
cmd.Dispose()
End Using
If affected = 2 then t.Commit()
t.Dispose()
conn.Close()
conn.Dispose()
Return affected
End Function
Private Sub DataGridView2_KeyDown(ByVal sender As Object, ByVal e As System.Windows.Forms.KeyEventArgs) Handles DataGridView2.KeyDown
If e.Control And e.KeyCode = Keys.Left Then
swap_row(kalkbr, selected_row, selected_row - 1)
Refreshlist(kalkbr)
End If
If e.Control And e.KeyCode = Keys.Right Then
swap_row(kalkbr, selected_row, selected_row + 1)
Refreshlist(kalkbr)
End If
...etc ...
for first one you have to filter brkalk on both dst and src:
update kalksad1 as dst set
brred = src.brred
from kalksad1 as src
where
src.brkalk = 2 and dst.brkalk = 2 and
dst.brred in (2,3) and
src.brred in (2,3) and
src.kalk_id <> dst.kalk_id;
sql fiddle demo
I think second one is too complicated, I've created it when I thought that you want to swap row with exact ID and next one

SQL: PIVOTting Count & Percentage against a column

I'm trying to produce a report that shows, for each Part No, the results of tests on those parts in terms of the numbers passed and failed, and the percentages passed and failed.
So far, I have the following:
SELECT r2.PartNo, [Pass] AS Passed, [Fail] as Failed
FROM
(SELECT ResultID, PartNo, Result FROM Results) r1
PIVOT (Count(ResultID) FOR Result IN ([Pass], [Fail])) AS r2
ORDER By r2.PartNo
This is half of the solution (the totals for passes and fails); the question is, how do I push on and include percentages?
I haven't tried yet, but I imagine that I can start again from scratch, and build up a series of subqueries, but this is more a learning exercise - I want to know the 'best' (most elegant or most efficient) solution, so I thought I'd seek advice.
Can I extend this PIVOT query, or should I take a different approach?
DDL:
CREATE TABLE RESULTS (
[ResultID] [int] NOT NULL,
[SerialNo] [int] NOT NULL,
[PartNo] [varchar](10) NOT NULL,
[Result] [varchar](10) NOT NULL);
DML:
INSERT INTO Results VALUES (1, '100', 'ABC', 'Pass')
INSERT INTO Results VALUES (2, '101', 'DEF', 'Pass')
INSERT INTO Results VALUES (3, '100', 'ABC', 'Fail')
INSERT INTO Results VALUES (4, '102', 'DEF', 'Pass')
INSERT INTO Results VALUES (5, '102', 'DEF', 'Pass')
INSERT INTO Results VALUES (6, '102', 'DEF', 'Fail')
INSERT INTO Results VALUES (7, '101', 'DEF', 'Fail')
UPDATE:
My solution, based on bluefeet's answer is:
SELECT r2.PartNo,
[Pass] AS Passed,
[Fail] as Failed,
ROUND(([Fail] / CAST(([Pass] + [Fail]) AS REAL)) * 100, 2) AS PercentFailed
FROM
(SELECT ResultID, PartNo, Result FROM Results) r1
PIVOT (Count(ResultID) FOR Result IN ([Pass], [Fail])) AS r2
ORDER By r2.PartNo
I've ROUNDed a FLOAT(rather than CAST to DECIMAL twice) because its a tiny bit more efficient, and I've also decided that we only real need the failure %age.
It sounds like you just need to add a column for Percent Passed and Percent Failed. You can calculate those columns on your PIVOT.
SELECT r2.PartNo
, [Pass] AS Passed
, [Fail] as Failed
, ([Pass] / Cast(([Pass] + [Fail]) as decimal(5, 2))) * 100 as PercentPassed
, ([Fail] / Cast(([Pass] + [Fail]) as decimal(5, 2))) * 100 as PercentFailed
FROM
(
SELECT ResultID, PartNo, Result
FROM Results
) r1
PIVOT
(
Count(ResultID)
FOR Result IN ([Pass], [Fail])
) AS r2
ORDER By r2.PartNo