How to assign the same value in certain block with PostgreSQL? - sql

Here , I want to assign all the value of router_index in the same block with the first row of index value in this block.
E.g. The router_index value from Row 1 to 4 should be 5,383 and from 5 to 7 should be 2,703...
I wonder if I can do it with pure PostgreSQL ?
Thanks for your help!!!!

do $$
declare
cnt integer := 0;
head_index_id varchar := '0';
cur_index_id varchar := '0';
fixed_index varchar := '10';
cnt_limit integer;
begin
select max(per_id) from detailed_router into cnt_limit;
while cnt <= cnt_limit loop
select router_index from detailed_router where per_id = cnt into cur_index_id;
if head_index_id = '0' and cur_index_id != fixed_index then
head_index_id := cur_index_id;
else
if cur_index_id = fixed_index then
-- update
update detailed_router set router_index = head_index_id where per_id = cnt;
else
select router_index from detailed_router where per_id = cnt into head_index_id;
end if;
end if;
raise notice 'cnt %', cnt;
raise notice 'cur_index_id %', cur_index_id;
raise notice 'head_index_id %', head_index_id;
cnt := cnt + 1;
end loop;
end $$

If you want "10" values to be filled with the previous non-"10" value and you have a column that specifies the ordering, you can use window functions:
select t.*,
max(router_index) over (partition by driver_index_code, grp) as imputed_router_index
from (select t.*,
count(*) filter (where router_index > 10) over (partition by driver_index_code order by <ordering column>) as grp
from t
) t;

Related

HOW TO DEAL WITH THESE ERROR ,PL/SQL Compilation unit analysis terminated & PSL-00201 : 'TABLE NAME' MUST BE DECLARED

I am creating a function in Oracle but it's not getting compiled and I am getting two errors
Error 1.
Error: PL/SQL: Compilation unit analysis terminated
Error 2.
Error(2,16): PLS-00201: identifier 'DIV_DUR_PRICE_TABLE' must be
declared
HERE IS THE CODE
CREATE OR REPLACE FUNCTION DIV_DAR(FID IN VARCHAR, DATE1 IN DATE, DATE2 IN DATE)
RETURN DIV_DUR_PRICE_TABLE PARALLEL_ENABLE AS
PRAGMA AUTONOMOUS_TRANSACTION;
CNT NUMBER;
V_RET DIV_DUR_PRICE_TABLE;
BEGIN;
EXECUTE IMMEDIATE 'DELETE from GTT_DIV_DUR_PRICE_TABLE';
Insert Into GTT_DIV_DUR_PRICE_TABLE
(select mydate, column1, column2, 0 as Final_value from tablename)
-- please refer my previous question to understand the code written ahead
declare
v_num integer := 1;
v_column1 number(8,2);
v_column2 number(8,2);
v_Final_value number(8,2);
begin
for rec in (select * from GTT_DIV_DUR_PRICE_TABLE order by mydate)
loop
if(v_num = 1) then
update tab set Final_value = column1 where mydate = rec.mydate;
else
if(rec.column2 is not null) then
update tab set Final_value =
v_Final_value * (v_column1/rec.column1) +
rec.column2 * (v_column1/v_Final_value) where mydate = rec.mydate;
else
update tab set Final_value =
v_Final_value * (rec.column1 / v_column1) where mydate = rec.mydate;
end if;
end if;
v_num:= v_num +1;
v_column1 := rec.column1;
v_column2 := rec.column2;
select final_value into v_Final_value from GTT_DIV_DUR_PRICE_TABLE
where mydate = rec.mydate;
end loop;
end;
SELECT
CAST(
MULTISET(
SELECT * FROM GTT_DIV_DUR_PRICE_TABLE order by P_DATE desc
)AS DIV_DUR_PRICE_TABLE
) INTO V_RET FROM DUAL;
COMMIT;
RETURN V_RET;
END DIV_DAR;

How to get old variable value from PL/SQL for loop?

I am trying to get old value from variable in PL/SQL for loop.
For instance:
I have l_sequnce variable with increment of 10.
for x in (select name
from test_names
order by position)
loop
-- print l_sequnce variable
-- when name is already was in loop then write same value from l_sequence as for first one
end loop;
I hope I described the problem well :)
Use ORDER BY in the SELECT statement and then you only need to check if the value has changed between the previous and current values:
DECLARE
v_name TEST_NAMES.NAME%TYPE := NULL;
l_sequence PLS_INTEGER := 0;
BEGIN
FOR x IN (select name from test_names ORDER BY name)
LOOP
IF x.name <> v_name
OR (x.name IS NULL AND v_name IS NOT NULL)
OR (x.name IS NOT NULL AND v_name IS NULL)
OR l_sequence = 0
THEN
l_sequence := l_sequence + 10;
v_name := x.name;
END IF;
DBMS_OUTPUT.PUT_LINE(l_sequence || ': ' || x.name);
END LOOP;
END;
/
Or, don't use PL/SQL and do it all in an SQL statement using the DENSE_RANK analytic function:
SELECT name,
10 * DENSE_RANK() OVER (ORDER BY name) AS seq_value
FROM test_names
ORDER BY name; -- or whatever other column you want to order by.
If you want to do it entirely in PL/SQL and cannot use ORDER BY then use an associative array:
DECLARE
TYPE t_name_seq_map IS TABLE OF PLS_INTEGER INDEX BY VARCHAR2(30);
v_names t_name_seq_map;
l_sequence PLS_INTEGER := 0;
BEGIN
FOR x IN (SELECT name FROM test_names ORDER BY position)
LOOP
IF NOT v_names.EXISTS(x.name) THEN
l_sequence := l_sequence + 10;
v_names(x.name) := l_sequence;
END IF;
DBMS_OUTPUT.PUT_LINE(v_names(x.name) || ': ' || x.name);
END LOOP;
END;
/
or:
BEGIN
FOR x IN (
SELECT name,
DENSE_RANK() OVER (ORDER BY name) AS l_sequence
FROM test_names
ORDER BY position
)
LOOP
DBMS_OUTPUT.PUT_LINE(x.l_sequence || ': ' || x.name);
END LOOP;
END;
/

PL/SQL procedure to output line the given date if not existing, latest date should be given

I have this table informationvalues with the contents:
Now I create a procedure where I need to input a date parameter which should output line the correct attr with given price. If the date doesn't exist the latest date should be selected.
The solution table for to_date('01-jan-19') would look like this:
This would be then output line in the procedure.
Should I select to correct tuple and output line it or would it be best to just bulk collect everything and then check in a for loop with an if statement what tuple I need to display.
What I have so far:
A select statement with the tuples I am looking for:
create or replace procedure print_inf_value(closingDate Date) is
cursor d1 (closingDate Date) is
select t.attr, t.dateOfValue, t.price
from (
select i.*,
row_number() over (
partition by attr
order by case when dateOfValue = closingdate then 1 else 2 end, dateOfValue desc
) rn
from InformationValues i
) t
where t.rn = 1;
BEGIN
dbms_output.put_line('Information Value ');
dbms_output.put_line('--------------------------------');
FOR d1_rec IN d1 LOOP
dbms_output.put_line(d1_rec.attr || ' ' || d1_rec.price );
END LOOP;
END;
Or a procedure where I bulk collect everything and then I need to sort out what tuple I need:
create or replace procedure print_inf_value(closingDate Date) is
TYPE d1 IS TABLE OF informationvalues%rowtype;
emps d1;
begin select * bulk collect into emps
from informationvalues;
FOR i IN 1 .. emps.COUNT LOOP
if emps(i).dateofvalue = closingDate then
dbms_output.put_line(emps(i).attr || ' ' || emps(i).price );
/*else*/
end if;
END LOOP;
END;
Both are not working right, so what am I missing to display tuple with the correct date.
Please try:
CREATE OR REPLACE PROCEDURE print_inf_value (closingDate DATE)
IS
BEGIN
DBMS_OUTPUT.put_line (RPAD ('ATTR', 20) || RPAD ('PRICE', 20));
FOR o
IN (select attr, trim(case when price < 1 then to_char(price,90.9) else to_char(price) end) price from (
select attr, price, dateofvalue,
row_number() over (partition by attr order by dateofvalue desc) rn from informationvalues
) i where dateofvalue = closingdate
or (rn = 1 and not exists (select 1 from informationvalues iv where iv.attr = i.attr and dateofvalue = closingdate) )
)
LOOP
DBMS_OUTPUT.put_line (RPAD (o.attr, 20) || RPAD ( o.price, 20));
END LOOP;
END;
Sample execution:
set serveroutput on;
begin
print_inf_value(date'2019-01-01');
end;
Output:
ATTR PRICE
age 2
electronics 0.5
gender 3
hobbies 0.5
homeAddress 7
maritalStatus 1
mobilePhone 5
musicTaste 0.1
socialContacts 1

using dynamic sql to create column for select statement

I'm writing a stored procedure for paginated results and this result can be ordered by certain values. I did have a switch case in a select statement but because it was trying to do an orderby on rownum it was very slow.
Now I am trying to use dyanmic sql to build the query outside the select but I don't know if what I am doing is possible.
Here is my SQL in Oracle SQL Developer:
create or replace PROCEDURE Sp_tsa_trainees_pagination (
schemeid IN INT,
searchval IN VARCHAR2,
pagesize IN INT DEFAULT 20,
currentpage IN INT DEFAULT 1,
--orderby IN VARCHAR2,
cursor_ OUT SYS_REFCURSOR)
AS
-- LOCAL VARIABLES
totalcount INT;
numberofpages INT;
startposition NUMBER;
endposition NUMBER;
orderby VARCHAR2(100) := 'surname asc' ;
dynamic_query VARCHAR(255) := 'row_number() over (order by t.SURNAME DESC, t.FORENAMES DESC) AS rnum';
BEGIN
-- Get total number of trainees in scheme
select COUNT(t.ORG_REGISTRATION_ID)
into totalcount FROM v_trainee t
where t.ORG_REGISTRATION_ID = schemeid
AND t.status = 'A' and LOWER(t.trainee_name) like '%' || LOWER(searchval) || '%';
-- calculate number of pages in the pagination by dividing total number of records by how many to display for each page
numberofpages := totalcount / pagesize;
-- get start position by multiplying number of records to display for each page by current page
startposition := pagesize *( currentpage-1);
-- add calculated start position by number of records to display to get end position
endposition := startposition + pagesize;
CASE orderby
WHEN 'surname desc' THEN dynamic_query := 'row_number() over (order by t.SURNAME DESC, t.FORENAMES DESC) AS rnum';
WHEN 'surname asc' THEN dynamic_query := 'row_number() over (order by t.SURNAME ASC, t.FORENAMES ASC) AS rnum';
END CASE;
OPEN cursor_ FOR
Select * from
(
SELECT
-- order by based on selection
dynamic_query rnum,
t.ORG_REGISTRATION_ID SearchId,
t.FORENAMES Forenames,
t.FORENAME Forename,
t.SURNAME Surname,
t.person_id PersonId,
t.trainee_name TraineeName,
t.STATUS Status,
t.IPD_ANNUAL_REVIEW_DATE AnnualReviewDate,
t.ANNUAL_REVIEW_STATUS AnnualReviewStatus,
t.payment_received PaymentRecieved,
t.TRAINEE_ID TraineeId,
t.IPD_SIGNUP_DATE IpdSignupDate,
t.START_DATE StartDate,
t.END_DATE EndDate,
t.LENGTH_ON_SCHEME LengthOnScheme,
t.EMPLOYEE_NUMBER EmploymentNumber,
t.SELECTED_LEVEL SelectedLevel,
t.SELECTED_LEVEL_DESCRIPTION SelectedLevelDescription,
t.ELIGIBLE_LEVEL EligibleLevel,
t.ELIGIBLE_LEVEL_DESCRIPTION EligibleLevelDescription,
sce.FORENAMES SceForenames,
sce.FORENAME SceForename,
sce.SURNAME SceSurname,
sce.mentor_name SceName,
sce.EMPLOYEE_NUMBER SceEmployeeNumber,
de.FORENAMES DeForenames,
de.FORENAME DeForename,
de.SURNAME DeSurname,
de.mentor_name DeName,
de.EMPLOYEE_NUMBER DeEmployeeNumber,
t.COMPLETED_ATTRIBUTE_LEVELS CompletedAttributeLevels,
t.ATTRIBUTE_LEVEL_COUNT AttributeLevelCount,
-- get percentage
CASE t.ATTRIBUTE_LEVEL_COUNT
WHEN 0 THEN 0
ELSE
COMPLETED_ATTRIBUTE_LEVELS / t.ATTRIBUTE_LEVEL_COUNT * 100
END percentage,
DECODE(F_ISTRAINEEGROUPMEMBER(t.ORG_REGISTRATION_ID, 'S', t.person_id),'Y','N','Y') WithoutTsaGroup,
orr.status SchemeStatus,
(select count(*) from TRAINING_GROUP_TRAINEE tgt where tgt.trainee_id = t.TRAINEE_ID) NUMBER_OF_GROUPS,
TotalCount
FROM v_trainee t
INNER JOIN org_registration orr ON t.ORG_REGISTRATION_ID = orr.id
LEFT OUTER JOIN v_mentor sce ON t.sce_id = sce.MENTOR_ID
LEFT OUTER JOIN v_mentor de ON t.de_id = de.MENTOR_ID
where t.ORG_REGISTRATION_ID = schemeid AND t.status = 'A'
and LOWER(t.trainee_name) like '%' || LOWER(searchval) || '%'
)
where rnum >= startposition and rnum <= endposition;
END;
I want to use this variable with the assigned sql:
dynamic_query rnum,
But when I execute the stored procedure I get this error:
ORA-01722: invalid number ORA-06512: at
"db.SP_TSA_TRAINEES_PAGINATION", line 46 ORA-06512: at line 13
So basically my question is can I assign a SQL to VARCHAR2 and then use it in a select statement dynamically.
You may need dynamic SQL for this. For example:
create or replace procedure testDyn(n in number, C OUT SYS_REFCURSOR) is
vDynamicPart varchar2(1000);
vSQl varchar2(1000);
begin
--
if (n = 1) then
vDynamicPart := 'count(1)';
else
vDynamicPart := 'count(null)';
end if;
--
vSQl := 'select ' || vDynamicPart || ' from dual';
open C for vSQl;
end;
If you call it
declare
n1 number;
n2 number;
C1 SYS_REFCURSOR;
C2 SYS_REFCURSOR;
begin
testDyn(1, C1);
testDyn(2, C2);
fetch C1 into n1;
fetch C2 into n2;
dbms_output.put_line('n1: ' || n1);
dbms_output.put_line('n2: ' || n2);
end;
you get:
n1: 1
n2: 0

Query doesn't update all rows

There's quite big table, more than 10 000 000 rows. It has columns OBJ_ID, DATE_OF_CHANGE, USER. And I added a new column, RECORD_ID, it is empty for now.
I need to update it so RECORD_ID should have numeric values ascending for OBJ_ID and DATE_OF_CHANGE.
I came up with this:
CREATE SEQUENCE REC_ID_SEQ
START WITH 1
INCREMENT BY 1
CACHE 100;
/
CREATE OR REPLACE TRIGGER TRG_REC_ID_SEQ
BEFORE INSERT ON T_HISTORY
FOR EACH ROW
BEGIN
:NEW.RECORD_ID := REC_ID_SEQ.NEXTVAL;
END;
/
DECLARE
O_ID NUMBER := 0;
S_DATE DATE := SYSDATE;
HIST_NUM NUMBER := 0;
LOOP_COUNT NUMBER := 0;
BEGIN
FOR O IN (SELECT ROWID ROW_ID, D.* FROM T_HISTORY D ORDER BY D.OBJ_ID, D.DATE_OF_CHANGE)
LOOP
LOOP_COUNT := LOOP_COUNT + 1;
IF O.OBJ_ID != O_ID OR O.DATE_OF_CHANGE!= S_DATE
THEN
HIST_NUM := HIST_NUM + 1;
END IF;
UPDATE T_HISTORY T SET T.RECORD_ID = HIST_NUM WHERE T.ROWID = O.ROW_ID;
O_ID := O.OBJ_ID;
S_DATE := O.DATE_OF_CHANGE;
IF LOOP_COUNT > 100000 THEN
COMMIT; LOOP_COUNT := 0;
END IF;
END LOOP;
END;
/
But when the command stops working (no errors) I see that about half of rows were not updated. How do I do this the right way?
Use MERGE command and rowid pseudocolumn as a substitute of primary key:
merge into T_HISTORY t
using (
select rownum as xx, t.*
from (
select t.*, rowid as x_rowid
from T_HISTORY t
order by OBJ_ID, DATE_OF_CHANGE
) t
) xx
on (xx.x_rowid = t.rowid )
when matched then update
set t.RECORD_ID = xx;
Live demo: http://sqlfiddle.com/#!4/aad05/2
Similar to #krokodilko's solution, using analytical function:
MERGE INTO t_history t
USING (SELECT obj_id,
date_of_change,
ROW_NUMBER () OVER (ORDER BY obj_id, date_of_change) rn
FROM t_history) r
ON (t.obj_id = r.obj_id AND t.date_of_change = r.date_of_change)
WHEN MATCHED
THEN
UPDATE SET t.record_id = r.rn;