Loop in the loop? PostgreSQL - postgresql

I have one query.
Here:
do
$$
declare
_command text := 'update "T_Status_Furnace_MS" set "Temp_Min" = (select min(%2$I) from "Table_1_Furnace_1"
where "ID_TimeStamp_FK" > (select max("ID_TimeStamp_FK" ) -10 from "Table_1_Furnace_1")) where "P_ID" = %1$s';
begin for i in 1..100 loop execute format(_command , i , 't' || i);
end loop;
end
$$;
This query find on the mentioned table the last 10 row, find the minimum and the another table " Status" insert the minimum value.
But. If the last 10 row have 1000 or more that case check only the minimum after the 1000.
Example:
ID | values
100 | 950
99 | 950
98 | 900
97 | 920
96 | 1000
95 | 800
94 | 790
93 | 750
92 | 700
91 | 800
90 | 800
That case only check the minimum after the 1000 ( in the example only check the 96-97-98-99-100 ID because we have 1000 on the 96 ID )
This is possible? How? Use curzors? Or loop in the loop?
Thank you

Related

how to SET value to 0 IF dta is not found in query POSTGRESQL

i have table like this
id | name | planned_amount | actual | avail
1 ABC 100 123 100
2 DEF 200 345 200
3 uytuyn 9000 311 300
4 oiui 890 200 200
above data output from query like this:
select a.name,
e.planned_amount,
dd.sum as actual,
e.planned_amount - dd.sum as avail
from crossovered_budget_lines as e,
account_analytic_account as a,
(select analytic_account_id, sum(debit+credit)
from account_move_line
where date between '2021-01-01' and '2021-05-01'
group by analytic_account_id) as dd
where e.analytic_account_id = a.id
and dd.analytic_account_id = .id
limit 5
when range data from table account_move_line is null ( no output data ), i want to output like this, column actual give 0 value :
id | name | planned_amount | actual | avail
1 ABC 100 0 100
2 DEF 200 0 200
3 uytuyn 9000 0 300
4 oiui 890 0 200
how to query to produce the output above ? iam stuck , thanks

Pivot sum in PostgreSQL

I'm using PosgreSQL 4.2. And I have the following code:
SELECT ID, Num1, Num2 FROM Tab1
WHERE ID IN(1,2,3,4.....50);
Returns results as:
ID | Num1 | Num2
----+--------------------
1 | 100 | 0
2 | 50 | 1
3 | 30 | 2
4 | 110 | 3
5 | 33 | 4
6 | 46 | 5
7 | 36 | 6
8 | 19 | 7
9 | 20 | 8
10 | 31 | 9
11 | 68 | 10
12 | 123 | 11
13 | 588 | 0
14 | 231 | 1
15 | 136 | 2
I want to Pivot sum to return result with pairs of number in IN clause, and result return like this:
| ID | Meaning
-------------------------
Num1 | 150 | 1+2(num1)
Num2 | 1 | 1+2(num2)
Num1 | 140 | 3+4(num1)
Num2 | 5 | 3+4(num2)
Num1 | 79 | 5+6(num1)
Num2 | 9 | 5+6(num2)
.........................
How can I do that?
PostgreSQL 4.2. No you must have another application ans confusing your application versions:
In 1996, the project was renamed to PostgreSQL to reflect its support
for SQL. The online presence at the website PostgreSQL.org began on
October 22, 1996.[26] The first PostgreSQL release formed version 6.0
on January 29, 1997.
Wikipedia. You might want to run the query: select version();
However, any supported version is sufficient for this request. Actually the SQL necessary to supply the necessary summations is simple once you understand LEAD (LAG) functions. The LEAD function permits access to the following row. Using that then the query:
select id, nid, n1s, n2s
from ( select id
, coalesce(lead(id) nid
, num1 + coalesce(lead(num1) over (order by id),0) n1s
, num2 + coalesce(lead(num2) over (order by id),0) n2s
, row_number() over() rn
from tab1
) i
where rn%2 = 1;
Provides all the necessary data for the presentation layer to format the the result as desired. However, that's not the result requested, that requires some SQL gymnastics.
We begin the gymnastics show by wrapping the above into an CTE adding a little setup for the show to follow. The main event then breaks the results into 2 sets in order to add the syntactical sugar tags before bringing then back together. So for the big show:
with joiner(id,nid,n1s,n2s,rn) as
( select *
from ( select id
, coalesce(lead(id) over (order by id),0)
, num1 + coalesce(lead(num1) over (order by id),0)
, num2 + coalesce(lead(num2) over (order by id),0)
, row_number() over() rn
from tab1
) i
where rn%2 = 1
)
select "Name","Sum","Meaning"
from (select 'Num1' as "Name"
, n1s as "Sum"
, concat(id::text, case when nid = 0
then null
else '+' || nid::text
end
) || ' (num1)'as "Meaning"
, rn
from joiner
union all
select 'Num2'
, n2s
, concat(id::text, case when nid = 0
then null
else '+' || nid::text
end
) || ' (num2)'
, rn
from joiner
) j
order by rn, "Name"
See fiddle. Note: I used "Sum" instead of ID for the column title, as it indicates the id not at all. That is just in the "Meaning" column.

postgres how to get desired result

select count(*), templates.id as template_id,
templates.name,
checklists.status
from templates
left join checklists on checklists.template_id = templates.id and checklists.organization_id = 134 and checklists.company_id = 193
where templates.organization_id = 134
and (start_date between '2015-01-01' and '2020-01-01') and checklists.is_archived ='False'
group by templates.id, templates.name, checklists.status
it results
count | template_id | name | status
-------+-------------+-----------------+----------------
21 | 157 | asdsd | awaiting_reply
1 | 157 | asdsd | completed
36 | 157 | asdsd | pending
note here count is total number of checklist having status given in result
i.e for template_id 157, count is 21 having status awaiting reply
I want result should be reflected as
count | template_id | name | complete count | total count
-------+-------------+-----------------+----------------------+-------------------
21 | 157 | asdsd | 1 + 58
how to do it.
I can suggest the following updated version:
SELECT
t.template_id,
t.name,
COUNT(*) FILTER (WHERE c.status = 'completed') AS "complete count",
COUNT(*) AS "total count"
FROM templates t
LEFT JOIN checklists c
ON c.template_id = t.id AND
c.organization_id = 134 AND
c.company_id = 193
WHERE
t.organization_id = 134 AND
start_date BETWEEN '2015-01-01' AND '2020-01-01' AND
c.is_archived = 'False'
GROUP BY
t.template_id,
t.name;
If i understand well, based on your expected result, you want 3 counts :
1 for status = awaiting_reply (= 21 in your example 'count')
1 for status = completed (= 1 in your example 'complete count')
1 for total count (= 58 in your example 'total count')
I think you could achieve this with the following query :
select
templates.id as template_id,
templates.name,
SUM(checklists.status = 'awaiting_reply') AS 'count',
SUM(checklists.status = 'completed') AS 'complete_count',
COUNT(1) AS 'total_count'
from templates
left join checklists
on checklists.template_id = templates.id
and checklists.organization_id = 134
and checklists.company_id = 193
where templates.organization_id = 134
and (start_date between '2015-01-01' and '2020-01-01')
and checklists.is_archived ='False'
group by templates.id, templates.name

T-SQL: Combining rows based on another table

I am seeking to alter a table content based on information of another table using a stored procedure. To make my point (and dodge my rusty English skills) I created the following simplification.
I have a table with fragment amounts of the form
SELECT * FROM [dbo].[obtained_fragments] ->
fragment amount
22 42
76 7
101 31
128 4
177 22
212 6
and a table that lists all possible combinations to combine these fragments to other fragments.
SELECT * FROM [dbo].[possible_combinations] ->
fragment consists_of_f1 f1_amount_needed consists_of_f2 f2_amount_needed
1001 128 1 22 3
1004 151 1 101 12
1012 128 1 177 6
1047 212 1 76 4
My aim is to alter the first table so that all possible fragment combinations are performed, leading to
SELECT * FROM [dbo].[obtained_fragments] ->
fragment amount
22 30
76 3
101 31
177 22
212 5
1001 4
1047 1
In words, combined fragments are added to the table based on [dbo].[possible_combinations], and the amount of needed fragments is reduced. Depleted fragments are removed from the table.
How do I achieve this fragment transformation in an easy way? I started writing a while loop, checking if sufficient fragments are available, inside of a for loop, interating through the fragment numbers. However, I am unable to come up with a functional amount check and begin to wonder if this is even possible in T-SQL this way.
The code doesn't have to be super efficient since both tables will always be smaller than 200 rows.
It is important to note that it doesn't matter which combinations are created.
It might come in handy that [f1_amount_needed] always has a value of 1.
UPDATE
Using the solution of iamdave, which works perfectly fine as long I don't touch it, I receive the following error message:
Column name or number of supplied values does not match table definition.
I barely changed anything really. Is there a chance that using existing tables with more than the necessary columns instead of declaring the tables (as iamdave did) makes this difference?
DECLARE #t TABLE(Binding_ID int, Exists_of_Binding_ID_2 int, Exists_of_Pieces_2 int, Binding1 int, Binding2 int);
WHILE 1=1
BEGIN
DELETE #t
INSERT INTO #t
SELECT TOP 1
k.Binding_ID
,k.Exists_of_Binding_ID_2
,k.Exists_of_Pieces_2
,g1.mat_Binding_ID AS Binding1
,g2.mat_Binding_ID AS Binding2
FROM [dbo].[vwCombiBinding] AS k
JOIN [leer].[sandbox5] AS g1
ON k.Exists_of_Binding_ID_1 = g1.mat_Binding_ID AND g1.Amount >= 1
JOIN [leer].[sandbox5] AS g2
ON k.Exists_of_Binding_ID_2 = g2.mat_Binding_ID AND g2.Amount >= k.Exists_of_Pieces_2
ORDER BY k.Binding_ID
IF (SELECT COUNT(1) FROM #t) = 1
BEGIN
UPDATE g
SET Amount = g.Amount +1
FROM [leer].[sandbox5] AS g
JOIN #t AS t
ON g.mat_Binding_ID = t.Binding_ID
INSERT INTO [leer].[sandbox5]
SELECT
t.Binding_ID
,1
FROM #t AS t
WHERE NOT EXISTS (SELECT NULL FROM [leer].[sandbox5] AS g WHERE g.mat_Binding_ID = t.Binding_ID);
UPDATE g
SET Amount = g.Amount - 1
FROM [leer].[sandbox5] AS g
JOIN #t AS t
ON g.mat_Binding_ID = t.Binding1
UPDATE g
SET Amount = g.Amount - t.Exists_of_Pieces_2
FROM [leer].[sandbox5] AS g
JOIN #t AS t
ON g.mat_Binding_ID = t.Binding2
END
ELSE
BREAK
END
SELECT * FROM [leer].[sandbox5]
You can do this with a while loop that contains several statements to handle your iterative data updates. As you need to make changes based on a re-assessment of your data each iteration this has to be done in a loop of some kind:
declare #f table(fragment int,amount int);
insert into #f values (22 ,42),(76 ,7 ),(101,31),(128,4 ),(177,22),(212,6 );
declare #c table(fragment int,consists_of_f1 int,f1_amount_needed int,consists_of_f2 int,f2_amount_needed int);
insert into #c values (1001,128,1,22,3),(1004,151,1,101,12),(1012,128,1,177,6),(1047,212,1,76,4);
declare #t table(fragment int,consists_of_f2 int,f2_amount_needed int,fragment1 int,fragment2 int);
while 1 = 1
begin
-- Clear out staging area
delete #t;
-- Populate with the latest possible combination
insert into #t
select top 1 c.fragment
,c.consists_of_f2
,c.f2_amount_needed
,f1.fragment as fragment1
,f2.fragment as fragment2
from #c as c
join #f as f1
on c.consists_of_f1 = f1.fragment
and f1.amount >= 1
join #f as f2
on c.consists_of_f2 = f2.fragment
and f2.amount >= c.f2_amount_needed
order by c.fragment;
-- Update fragments table if a new combination can be made
if (select count(1) from #t) = 1
begin
-- Update if additional fragment
update f
set amount = f.amount + 1
from #f as f
join #t as t
on f.fragment = t.fragment;
-- Insert if a new fragment
insert into #f
select t.fragment
,1
from #t as t
where not exists(select null
from #f as f
where f.fragment = t.fragment
);
-- Update fragment1 amounts
update f
set amount = f.amount - 1
from #f as f
join #t as t
on f.fragment = t.fragment1;
-- Update fragment2 amounts
update f
set amount = f.amount - t.f2_amount_needed
from #f as f
join #t as t
on f.fragment = t.fragment2;
end
else -- If no new combinations possible, break the loop
break
end;
select *
from #f;
Output:
+----------+--------+
| fragment | amount |
+----------+--------+
| 22 | 30 |
| 76 | 3 |
| 101 | 31 |
| 128 | 0 |
| 177 | 22 |
| 212 | 5 |
| 1001 | 4 |
| 1047 | 1 |
+----------+--------+

code works to a point

The following code looks good to me but works to a point. The function should display the grade levels of students based on exam performance but it does not run the last two else statements and so, if a student scores lower than 50 the function still displays "pass".
CREATE OR REPLACE FUNCTION stud_Result(integer,numeric) RETURNS text
AS
$$
DECLARE
stuNum ALIAS FOR $1;
grade ALIAS FOR $2;
result TEXT;
BEGIN
IF grade >= 70.0 THEN SELECT 'distinction' INTO result FROM student,entry
WHERE student.sno = entry.sno AND student.sno = stuNum;
ELSIF grade >=50.0 OR grade <=70.0 THEN SELECT 'pass' INTO result FROM student,entry
WHERE student.sno = entry.sno AND student.sno = stuNum;
ELSIF grade >0 OR grade< 50.0 THEN SELECT 'fail' INTO result FROM student,entry
WHERE student.sno = entry.sno AND student.sno = stuNum;
ELSE SELECT 'NOT TAKEN' INTO result FROM student,entry
WHERE student.sno = entry.sno AND student.sno = stuNum;
END IF;
RETURN result;
END;$$
LANGUAGE PLPGSQL;
Can anyone point me to the problem?
This is a PostgreSQL gotcha that has tripped me up as well. You need to replace your ELSE IFs with ELSIF.
You're seeing that error because each successive ELSE IF is being interpreted as starting a nested IF block, which expects its own END IF;.
See the documentation on conditionals for more information on the proper syntax.
Your logic in the conditionals is a bit strange. You have these:
grade >= 70.0
grade >= 50.0 OR grade <= 70.0
grade > 0 OR grade < 50.0
Note that zero satisfies the second condition as do a lot of other values that you don't want in that branch of the conditonal. I think you want these:
grade >= 70.0
grade >= 50.0 AND grade <= 70.0
grade > 0 AND grade < 50.0
You also seem to be using your SELECTs to check if the person is in the course but if the grade is given and they're not in the course, you will end up with a NULL result. Either the "in the course" check should be outside your function or you should convert a NULL result to 'NOT TAKEN' before returning.
This looks like homework so I'm not going to be anymore explicit than this.
Generally, I don't think it is a good idea to hide data in code. Data belongs in tables:
SET search_path='tmp';
-- create some data
DROP TABLE tmp.student CASCADE;
CREATE TABLE tmp.student
( sno INTEGER NOT NULL
, grade INTEGER
, sname varchar
);
INSERT INTO tmp.student(sno) SELECT generate_series(1,10);
UPDATE tmp.student SET grade = sno*sno;
DROP TABLE tmp.entry CASCADE;
CREATE TABLE tmp.entry
( sno INTEGER NOT NULL
, sdate TIMESTAMP
);
INSERT INTO tmp.entry(sno) SELECT generate_series(1,10);
-- table with interval lookup
DROP TABLE tmp.lookup CASCADE;
CREATE TABLE tmp.lookup
( llimit NUMERIC NOT NULL
, hlimit NUMERIC
, result varchar
);
INSERT INTO lookup (llimit,hlimit,result) VALUES(70, NULL, 'Excellent'), (50, 70, 'Passed'), (30, 50, 'Failed')
;
CREATE OR REPLACE FUNCTION stud_result(integer,numeric) RETURNS text
AS $BODY$
DECLARE
stunum ALIAS FOR $1;
grade ALIAS FOR $2;
result TEXT;
BEGIN
SELECT COALESCE(lut.result, 'NOT TAKEN') INTO result
FROM student st, entry en
LEFT JOIN lookup lut ON (grade >= lut.llimit
AND (grade < lut.hlimit OR lut.hlimit IS NULL) )
WHERE st.sno = en.sno
AND st.sno = stunum
;
RETURN result;
END; $BODY$ LANGUAGE PLPGSQL;
-- query joining students with their function values
SELECT st.*
, stud_result (st.sno, st.grade)
FROM student st
;
But wait: you can just as well do without the ugly function:
-- Plain query
SELECT
st.sno, st.sname, st.grade
, COALESCE(lut.result, 'NOT TAKEN') AS result
FROM student st
LEFT JOIN lookup lut ON ( 1=1
AND lut.llimit <= st.grade
AND ( lut.hlimit > st.grade OR lut.hlimit IS NULL)
)
JOIN entry en ON st.sno = en.sno
;
Results:
sno | grade | sname | stud_result
-----+-------+-------+-------------
1 | 1 | | NOT TAKEN
2 | 4 | | NOT TAKEN
3 | 9 | | NOT TAKEN
4 | 16 | | NOT TAKEN
5 | 25 | | NOT TAKEN
6 | 36 | | Failed
7 | 49 | | Failed
8 | 64 | | Passed
9 | 81 | | Excellent
10 | 100 | | Excellent
(10 rows)
sno | sname | grade | result
-----+-------+-------+-----------
1 | | 1 | NOT TAKEN
2 | | 4 | NOT TAKEN
3 | | 9 | NOT TAKEN
4 | | 16 | NOT TAKEN
5 | | 25 | NOT TAKEN
6 | | 36 | Failed
7 | | 49 | Failed
8 | | 64 | Passed
9 | | 81 | Excellent
10 | | 100 | Excellent
(10 rows)
Get rid of the function altogether and use a query:
SELECT
s.*,
CASE e.grade
WHEN >= 0 AND < 50 THEN 'failed'
WHEN >= 50 AND < 70 THEN 'passed'
WHEN >= 70 AND <= 100 THEN 'excellent'
ELSE 'not taken'
END
FROM
student s,
entry e
WHERE
s.sno = e.sno;