Fill column from other data in row being inserted - sql

I am trying to come up with a trigger that fills a column of a row that I insert/update with a score that depends on the the numeric values on other columns of that same row.
For example
+------+------+------+------+------+------+
| col1 | col2 | col3 | col4 | col5 | col6 |
+------+------+------+------+------+------+
| 1 | 2 | 1 | 3 | 1 | |
+------+------+------+------+------+------+
This is the row I want to insert, I would like to fill col6 with a score calculated using the values of the other columns
(100 - avg(col1:col5)/4*100)
Can I do this through a trigger or procedure? Should I do this before or after the insert?

From Oracle 11g you can use a virtual column:
Oracle Setup:
CREATE TABLE table_name (
col1 NUMBER,
col2 NUMBER,
col3 NUMBER,
col4 NUMBER,
col5 NUMBER,
col6 NUMBER GENERATED ALWAYS AS ( 100 - (col1+col2+col3+col4+col5)*5 ) VIRTUAL
);
INSERT INTO table_name ( col1, col2, col3, col4, col5 )
VALUES ( 1, 2, 1, 3, 1 );
Query:
SELECT * FROM table_name;
Output:
COL1 COL2 COL3 COL4 COL5 COL6
---- ---- ---- ---- ---- ----
1 2 1 3 1 60

A trigger would look like.
CREATE OR REPLACE TRIGGER ti
BEFORE UPDATE OR INSERT ON yourtable
FOR EACH ROW
BEGIN
:new.col6 :=
( 100
- (:new.col1 + :new.col2 + :new.col3 + :new.col4 + :new.col5)/ 5 * 100);
end;

I did it this way to avoid the possible null values that might exist. Thank you all for the precious help.
create or replace TRIGGER SCORE_SYMP_TRG
BEFORE INSERT OR UPDATE ON KOOS
FOR EACH ROW
DECLARE
BEGIN
:NEW.SCORE_SYMP := 100 - ROUND(((NVL(:NEW.S1,0) + NVL(:NEW.S2,0) + NVL(:NEW.S3,0) + NVL(:NEW.S4,0) + NVL(:NEW.S5,0) + NVL(:NEW.R6, 0) + NVL(:NEW.R7, 0))/(
NVL((NVL(:NEW.S1,0)/NVL(:NEW.S1,1)),0) + NVL((NVL(:NEW.S2,0)/NVL(:NEW.S2,1)),0) + NVL((NVL(:NEW.S3,0)/NVL(:NEW.S3,1)),0) + NVL((NVL(:NEW.S4,0)/NVL(:NEW.S4,1)),0)
+ NVL((NVL(:NEW.S5,0)/NVL(:NEW.S5,1)),0) + NVL((NVL(:NEW.R6,0)/NVL(:NEW.R6,1)),0) + NVL((NVL(:NEW.R7,0)/NVL(:NEW.R7,1)),0)) /4)*100,0);
END;

Related

Scatter multiple rows having duplicate columns to single unique row in postgresql

how to scatter multiple duplicate rows into one row in sql/postgresql.
For example --->
lets i am getting 3 rows of
col1 col2 col3
-------------------
11 test rat
11 test cat
11 test test
I want something like this
col1 col2 col3 col4
------------------------
11 test rat cat
Its the same thing like groupby in lodash. But how do I achieve the same in postgresql query?
You're looking for crosstab
postgres=# create table ab (col1 text, col2 text, col3 text);
CREATE TABLE
postgres=# insert into ab values ('t1','test','cat'),('t1','test','rat'),('t1','test','test');
INSERT 0 3
postgres=# select * from crosstab('select col1,col2,col3 from ab') as (col1 text, col2 text, col3 text, col4 text);
col1 | col2 | col3 | col4
------+------+------+------
t1 | cat | rat | test
(1 row)
Disclosure: I work for EnterpriseDB (EDB)

Update column value in all rows of a table on mod(rownum,10) = number

I have a table tab1 that looks like this:
col1 | col2 | col3
------|------|------
abc | 100 | text
abc | 100 | text
abc | 100 | text
... | ... | ...
I need to update col2 value in each row like this:
update tab1
set col2 = 1,23
when mod(rownum,10) = 1;
update tab1
set col2 = 12,34
when mod(rownum,10) = 2;
update tab1
set col2 = 123,45
when mod(rownum,10) = 3;
and etc. until when mod(rownum,10) = 9.
But obviously this query doesn't work, and the reason is that rownum always returns 1 in this situation, afaik. However, I've got the correct last digits for each row number with select mod(rownum,10) as lastDig from tab1 query. But I don't understand how to use the result of this select for my update when conditions.
Could you please provide an example of a query that will do the job in this situation? Do I need to use a subquery or select in a temporary table? Please explain. I'm a junior frontend guy, but I need to create a demo table this way. I believe, pl/sql is v10, as well as PL/SQL Developer.
Result wanted looks like this:
col1 | col2 | col3
------|-------|------
abc | 1.23 | text
abc | 12.34 | text
abc | 123.45| text
... | ... | ...
You could use CASE expression or DECODE:
update tab1
set col2 = CASE mod(rownum,10) WHEN 1 THEN 1.23
WHEN 2 THEN 12.34
WHEN 3 THEN 123.45
-- ...
ELSE col2
END
-- WHERE ...
UPDATE tab1
SET col2 = DECODE(mod(rownum,10), 1, 1.23, 2, 12.34, 3, 123.45, ..., col2)
-- WHERE ...;
DBFiddle Demo
You have not told us if there is a specific order in which you want to treat rows as 1,2,3 .. If there is indeed an order, then ROWNUM is unreliable and may not work, you would need row_number() with a specific order by column. That can be combined with a MERGE statement.
MERGE INTO tab1 tgt USING (
SELECT
CASE mod( ROW_NUMBER() OVER(
ORDER BY
col1 -- the column which is in order and unique
),10)
WHEN 1 THEN 1.23
WHEN 2 THEN 12.34
WHEN 3 THEN 123.45
--..
--.. 9
ELSE col2
AS col2
FROM
tab1 t
)
src ON ( tgt.rowid = src.rowid ) --use primary key/unique key if there is one instead of rowid
WHEN MATCHED THEN UPDATE SET tgt.col2 = src.col2;
Demo

db2 update multiple field for a record

I have a table with 1 row and 2 columns: col1 and col2,
--------------------------------------------------
| col1 | col2 |
--------------------------------------------------
| 1 | 2 |
--------------------------------------------------
now I want to update the row record:
db2 "update myschema.mytable set col1=3, col2=col1"
I expect the result to be col1=3 and col2=3, but it gives me col1=3,col2=1
how do I update one column base on a newly updated column?
Try This:
DECLARE #A INT
SELECT #A = Col1 FROM Mytable
UPDATE MyTable SET Col1 = 4, Col2 = #A

Oracle column number increase

I would like to know how to increase the row number by 1 in Column 1 when Column 2 value changes in Oracle
What I am looking for is to achieve this :
COL1 COL2 COL3 |
1 2000 xx |
1 2000 xy |
1 2000 xyz |
2 3020 x |
2 3020 xiii |
3 5666666 ueueu
Any idea ?
I think you are looking for a window function:
select row_number() over (partition by col2 order by col3) as col1,
col2,
col3
from the_table;
If you want to increase col1 value after updating col2 on table t_ then you can use trigger.
CREATE OR REPLACE TRIGGER upcol1
AFTER UPDATE ON t_ FOR EACH ROW
WHEN (old.col2 != new.col2)
BEGIN
UPDATE t_ SET col1=:new.col1+1
WHERE col2=:new.col2 AND col3=:new.col3;
END;

Oracle: any way to transform data this way?

Is there any way to map the first table to the second table with an SQL query or, if too complicated, a PL/SQL block?
Original
--------------------------------------
| col1 | col2 | col3 | col4 |
--------------------------------------
| key | case 1 | case 2 | case 3 |
| value1 | v1c1 | v1c2 | v1c3 |
| value2 | v2c1 | v2c2 | v2c3 |
--------------------------------------
Target
-----------------------------
| key | case | result |
-----------------------------
| value1 | case 1 | v1c1 |
| value1 | case 2 | v1c2 |
| value1 | case 3 | v1c3 |
| value2 | case 1 | v2c1 |
| value2 | case 2 | v2c2 |
| value2 | case 3 | v2c3 |
-----------------------------
The original table can have a variable number of columns, and 'key' is a hardcoded string and is always in column 1 of the original table. No other row has “key” in column 1, so this row is a unique pivot.
Thank you
If dynamic sql is allowed, then it is possible to have all your requirements fullfilled using one query:
SELECT col1 as "key"
,extractvalue(dbms_xmlgen.getXMLType('select "' || tc.Column_Name ||
'" as v from Original where col1 = ''key''')
,'/ROWSET/ROW/V') "case"
,extractvalue(dbms_xmlgen.getXMLType('select "' || tc.Column_Name ||
'" as v from Original where col1 = ''' ||
replace(col1, '''', '''''') || '''')
,'/ROWSET/ROW/V') "result"
FROM Original
,(SELECT Column_Name
FROM All_Tab_Columns tc
WHERE tc.Owner = 'YOURSCHEMA'
and tc.Table_Name = 'ORIGINAL'
and Column_Name != 'COL1'
ORDER BY tc.COLUMN_ID) tc
WHERE col1 != 'key'
ORDER BY "key"
,"case"
Some more details as requested:
dbms_xmlgen.getXMLType returns an XmlType instance which is basically the result of the supplied query string as XML.
The format is ROWSET for the root node and ROW for each row. Every column will be an element as well.
The 2 selects that I am creating are only returning one value and to makes things easier, I gave them a column alias "V" so that I know which value to pick from the XML.
extractValue is a function that returns the result of an XPath expression from an XmlType.
'/ROWSET/ROW/V' returns the first V node, from the first ROW node that resides under the root node ROWSET.
<ROWSET><ROW><V>Abc</V></ROW></ROWSET>
The original table can have a variable
number of columns
Really?
The straightforward way is to select and union the parts you want.
select col1 as key, 'case1' as case, col2 as result
from test
where col1 <> 'key'
union all
select col1 as key, 'case2' as case, col3 as result
from test
where col1 <> 'key'
union all
select col1 as key, 'case3' as case, col4 as result
from test
where col1 <> 'key'
Straightforward, but not dynamic.
Later . . .
Based on your comment . . . although I don't think it's necessary.
select col1 as key, (select col2 from test where col1='key') as case, col2 as result
from test
where col1 <> 'key'
union all
select col1 as key, (select col3 from test where col1='key') as case, col3 as result
from test
where col1 <> 'key'
union all
select col1 as key, (select col4 from test where col1='key') as case, col4 as result
from test
where col1 <> 'key'
Oracle 11 also supports UNPIVOT, which I haven't used.
I don't know which parts can change, but this should be a start for you. If the column names can change (key, case 1, etc.) you will have to have another query to get the correct column names. If you have questions feel free to ask:
declare
v_query VARCHAR2(5000);
v_case VARCHAR2(255);
v_colcount PLS_INTEGER;
begin
-- Get number of columns
select count(*)
INTO v_colcount
from user_tab_columns
where table_name = 'T1';
-- Build case statement to get correct value for result column
v_case := 'case';
for i in 1 .. v_colcount-1
loop
v_case := v_case||' when rn = '||to_char(i)||' then col'||to_char(i+1);
end loop;
v_case := v_case||' end result';
-- Build final query
v_query := 'select col1 key, ''case ''||rn case, '||v_case||'
from t1
cross join (
select rownum rn
from dual
connect by level <= '||to_char(v_colcount-1)||'
) cj
where col1 <> ''key''
order by key, case';
-- Display query (would probably be replaced with an insert using execute immediate)
dbms_output.put_line(v_query);
end;
This produces the following query (which assumes your original table is called t1):
select col1 key, 'case '||rn case, case when rn = 1 then col2 when rn = 2 then col3 when rn = 3 then col4 end result
from t1
cross join (
select rownum rn
from dual
connect by level <= 3
) cj
where col1 <> 'key'
order by key, case
Try this:
with data as
(select level l from dual connect by level <= 3)
select col1,
'case' || l as "case",
decode(l,1,col2,2,col3,3,col4) as "values"
from myTable, data
order by 1,2;
Cheers