Making multiple rows out of single row, SAP HANA Calculation View - hana

I have this below table
TYPE ID VERSION
A WXYZ a#bbb#aaa
A ABCD cc#qq
I want output like this
Type ID VERSION
A WXYZ a
A WXYZ bb
A WXYZ aaa
A ABCD cc
A ABCD qq
It is possible to do using Cursors, I have done it, is it possible to do it without Cursor?

create table v_test (type varchar(5), id varchar(10) , versionn varchar(100));
insert into v_test values ('A', 'WXYZ' , 'a#bbb#aaa');
insert into v_test values ('A', 'ABCD' , 'cc#qq');
select * from v_test
Select * from
( select "ID" "KEY", "ELEMENT_NUMBER" "ORD" ,
SUBSTR_REGEXPR('(?<=^|#)([^#]*)(?=#|$)' IN "V_TEST"."VERSIONN" OCCURRENCE "SERIES"."ELEMENT_NUMBER" GROUP 1) "VERSIONN"
from v_test,
SERIES_GENERATE_INTEGER(1, 1, 10 ) "SERIES" -- replace 10 with your max. number of values in CSV-Field
)
where "VERSIONN" is not null
order by "KEY", "ORD"

Related

Dynamically unpivot table within Oracle

I have a table with several hundred columns that I need to unpivot. All of the columns that need to be unpivoted start with 'SIM_'. I know how to do this statically (example below), but I'd like a dynamic solution - as the number of columns that need be be unpivot is both long and may change over time.
SELECT
*
FROM
(SELECT
ID,
NAME,
SIM_1,
SIM_2,
SIM_3
FROM
SAMPLE_TABLE
) T UNPIVOT(SIM_RESULT FOR SIM IN (SIM_1, SIM_2, SIM_3))
Maybe you could (re)create a view when there is a need to refresh the list of columns. This way you will have the accurate list of columns and a way to show real data. It does the unpivoting of all columns (like SIM_%) in the time of execution.
/* sample table and data
CREATE TABLE AA_TST
(ID NUMBER(6), SIM_A VARCHAR2(20), SIM_B VARCHAR2(20), SIM_C VARCHAR2(20), SIM_D VARCHAR2(20), SIM_E VARCHAR2(20));
INSERT INTO AA_TST VALUES(1, 'A', 'B', 'C', 'D', 'E');
INSERT INTO AA_TST VALUES(2, 'AA', 'BB', 'CC', 'DD', 'EE');
INSERT INTO AA_TST VALUES(3, 'AAA', 'BBB', 'CCC', 'DDD', 'EEE');
*/
Declare
col_list VarChar2(1000);
myViewSQL VarChar2(1000);
Begin
SELECT LISTAGG(column_name, ', ') WITHIN GROUP (ORDER BY column_name)
INTO col_list
FROM all_tab_columns
WHERE table_name = 'AA_TST' And
column_name LIKE 'SIM_%';
--
myViewSQL := 'SELECT VALUE_NAME, VALUE_OF FROM AA_TST UNPIVOT (VALUE_OF FOR VALUE_NAME IN(' || col_list || '))';
execute immediate 'CREATE or replace VIEW AA_TST_VIEW AS ' || myViewSQL;
End;
/
SELECT * FROM AA_TST_VIEW;
--
-- R e s u l t
--
-- anonymous block completed
-- VALUE_NAME VALUE_OF
-- ---------- --------------------
-- SIM_A A
-- SIM_B B
-- SIM_C C
-- SIM_D D
-- SIM_E E
-- SIM_A AA
-- SIM_B BB
-- SIM_C CC
-- SIM_D DD
-- SIM_E EE
-- SIM_A AAA
-- SIM_B BBB
-- SIM_C CCC
-- SIM_D DDD
-- SIM_E EEE
--
-- 15 rows selected

How can I disable entering the same values in two fields at the same time on a table in Oracle?

Let´s say I create this table:
CREATE TABLE MYTABLE (
id INT NOT NULL AUTO_INCREMENT,
Field1 VARCHAR(30),
Field2 NUMBER(10),
);
Then I will insert this values:
INSERT INTO MYTABLE VALUES(null, 'Value', 10);
What I want is to be able to do both of these inserts:
INSERT INTO MYTABLE VALUES(null, 'Value', 5);
/* This works as there isn´t a row with both Field1='Value' and Field2=5 at the same time */
INSERT INTO MYTABLE VALUES(null, 'Something', 10);
/* This works as there isn´t a row with both Field1='Something' and Field2=10 at the same time */
But I don´t want to be able to do this (repeat both the Field1 and Field2 values together):
INSERT INTO MYTABLE VALUES(null, 'Value', 10);
/* This doesn´t work as there is a row with both Field1='Value' and Field2=10 at the same time */
How can I achieve this behaviour in Oracle? I thought about using ASSERTIONS but they are not yet implemented in Oracle.
I don´t want to be able to do this (repeat both the Field1 and Field2 values together)
You can use a COMPOUND trigger:
CREATE OR REPLACE TRIGGER mytable__not_repeat_f1_and_f2
FOR UPDATE OR INSERT ON MyTable
COMPOUND TRIGGER
TYPE MyTable_Fields_Type IS RECORD(
rid ROWID,
field1 MyTable.Field1%TYPE,
field2 MyTable.Field2%TYPE
);
TYPE MyTable_Fields_Table_Type IS TABLE OF MyTable_Fields_Type;
fields MyTable_Fields_Table_Type := MyTable_Fields_Table_Type();
AFTER EACH ROW IS
BEGIN
fields.EXTEND;
fields(fields.COUNT) := MyTable_Fields_Type(
:NEW.ROWID,
:NEW.Field1,
:NEW.Field2
);
END AFTER EACH ROW;
AFTER STATEMENT IS
num_field1 PLS_INTEGER;
num_field2 PLS_INTEGER;
BEGIN
FOR i IN 1 .. fields.COUNT LOOP
SELECT COUNT( CASE WHEN Field1 = fields(i).Field1 THEN 1 END ),
COUNT( CASE WHEN Field2 = fields(i).Field2 THEN 1 END )
INTO num_field1,
num_field2
FROM MyTable
WHERE ROWID != fields(i).RID;
IF num_field1 > 0 AND num_field2 > 0 THEN
RAISE_APPLICATION_ERROR( -20000, 'Cannot have duplicate Field1 and Field2' );
END IF;
END LOOP;
END AFTER STATEMENT;
END;
/
Then, for the table:
CREATE TABLE MYTABLE (
id INT
GENERATED ALWAYS AS IDENTITY
PRIMARY KEY,
Field1 VARCHAR2(30),
Field2 NUMBER(10)
);
If you do:
INSERT INTO MyTable ( Field1, Field2 )
SELECT 'a', 1 FROM DUAL UNION ALL
SELECT 'b', 2 FROM DUAL UNION ALL
SELECT 'c', 3 FROM DUAL;
That works but then trying to do:
INSERT INTO MyTable ( Field1, Field2 ) VALUES ( 'b', 3 );
Would raise the exception:
ORA-20000: Cannot have duplicate Field1 and Field2
ORA-06512: at "SCHEMA_NAME.MYTABLE__NOT_REPEAT_F1_AND_F2", line 33
ORA-04088: error during execution of trigger 'SCHEMA_NAME.MYTABLE__NOT_REPEAT_F1_AND_F2'
But:
INSERT INTO MyTable ( Field1, Field2 ) VALUES ( 'b', 4 );
Would work since this doesn't repeat a Field1 and a Field2 value together.
db<>fiddle here
If you want each column to be unique, you can just use unique constraints:
CREATE TABLE MYTABLE (
id INT NOT NULL AUTO_INCREMENT,
Field1 VARCHAR(30) UNIQUE,
Field2 NUMBER(10) UNIQUE
);
Create a compound unique index containing both columns so that the combination can't be inserted more than once, whether it be at the same time or at different times.
I don't think there's any way to do this reliably. If I understand correctly, you want to reject an inserted row if Field1 and Field2 both already exist in the table, but not necessarily in the same row.
So if your table looks like:
Field1 Field2
------ ------
Value 5
Something 10
I should be able to insert ('Apple', 7), right? Then when i try to insert ('Value', 7), it fails because there are rows with both those values already.
But I could do them in the reverse order: insert ('Value', 7) and then ('Apple', 7)
So what happens if I do this?
WITH mydata AS (
SELECT 'Apple' AS field1, 7 AS field2 FROM dual UNION ALL
SELECT 'Value', 7 FROM dual
INSERT INTO mytable
SELECT null, field1, field2 FROM mydata
Does that succeed or fail? There's no way to know. You are not guaranteed that the database actions happen in the order in which you think they're going to happen.
You'll run into the same problem if two sessions try to insert these two rows, then commit.

How to create column based on previous rows?

I have the following table:
id type
1 NULL
2 A
3 NULL
4 NULL
5 A
6 NULL
7 B
8 A
9 B
10 NULL
I want to create a column where each row takes the current status if exist if not take the status from the previous one.
Basically want to get this:
id type new_type
1 NULL NULL -- firs one no previous so it can only be null
2 A A -- current type A so take it
3 NULL A -- no type so take the new_type of previous
4 NULL A
5 A A
6 NULL A
7 B B
8 A A
9 B B
10 NULL B
I know I need window function here but I don't know how a window function can reference a column that is "in progress" basically the window function need to reference both type and new_type but new_type doesn't exist yet.. it's the output.
How can this be done in SQL / Presto?
Presto has comprehensive support for window functions. Here, you can use lag() with the ignore nulls option to replace null values in column type:
select
id,
type,
coalesce(
type,
lag(type ignore nulls) over(order by id)
) new_type
from mytable
Needs a cursor, especially if the id is not guarantee to be sequential and without gaps.
This will run in MS-SQL:
-- stage sample data
drop table if exists oldTable
create table oldTable (id int, old_type nvarchar(1))
go
insert into oldTable values (1, null), (2, 'A'), (3, null), (4, null), (5, 'A'), (6, null), (7, 'B'), (8, 'A'), (9, 'B'), (10, null)
go
-- get new table ready
drop table if exists newTable
create table newTable (
id int,
old_type nvarchar(1),
new_type nvarchar(1)
)
GO
-- prepare for lots of cursing
declare #the_id int
declare #the_type nvarchar(1)
declare #running_type nvarchar(1)
declare mycursor cursor for
select
id, old_type
from
oldTable
-- do a barrel roll
open mycursor
fetch mycursor into #the_id, #the_type
while ##ERROR = 0 and ##FETCH_STATUS = 0
begin
set #running_type = COALESCE(#the_type, #running_type)
insert into newTable(id, old_type, new_type)
values (#the_id, #the_type, #running_type)
fetch mycursor into #the_id, #the_type
end
close mycursor
deallocate mycursor
go
-- verify results
select * from newTable
go
How can this be done in SQL
For example, it can be
SELECT t1.id,
t1.type,
( SELECT t2.type
FROM sourcetable t2
WHERE t2.id <= t1.id
AND t2.type IS NOT NULL
ORDER BY id DESC
LIMIT 1 ) new_type
FROM sourcetable t1

Insert 1000 rows in single sql statment or procedure

How should i write a single sql statement or a stored procedure,
To insert 1000 values in 1000 rows and same column with each column having different values (among those 1000)
Here is the query i wrote,
INSERT INTO a_b values
(
(SELECT max(a_b_id) + 1 from a_b),
1111,
(SELECT s_id FROM a_b WHERE s_id in ('0','1','2','3','4')),
0,
1,
sysdate,
sysdate,
0,
1,
null
);
like say, i have 1000 s_id's i want select them one by one and insert them in one particular column, each time creating a new row.
EX, in first row s_id should be 0 then in second row it should be 1 like that goes on till thousand, attached an image of sample database i am working with.
You can use connect by for this:
INSERT INTO a_b (s_id, col2, col3, ....)
select level, --<< this value will change for every row
1111,
sysdate,
... more columns ...
from dual
connect by level <= 1000;
you can use cross apply to get 1000 rows along with 1000 other columns to insert 1000 rows as below:
insert into a_b (column names...)
select (max(a_b_id) over()) +1 as MaxId, s_id from a_b a cross apply (select 0, 1,SYSDATETIME, SYSDATETIME, 0, 1, null) b where a.s_id('111','222')--condition
The below is a syntax error. You will never get something like that to work.
create table fff
( id int not null
);
insert fff values (select 1,7777,select 3, select 3);
So you need to break it up into chunks
DROP PROCEDURE IF EXISTS uspRunMe;
DELIMITER $$
CREATE PROCEDURE uspRunMe()
BEGIN
insert into a_b select max(a_b_id) + 1 from a_b;
insert into a_b values (1111);
insert into a_b SELECT s_id FROM a_b WHERE s_id in ('0','1','2','3','4');
insert into a_b values (0,1);
insert into a_b select sysdate,sysdate;
insert into a_b values (0,1,null);
END;$$
DELIMITER ;
Test it:
call uspRunMe();
The above is for MySQL. You have a few db engines tagged here.

How to know the column name from a table based on the column values

I am working in Informix and I want to know if there is a simple way to know the tabname/colname by its possible column values.
For example:
table1
Register 1
==========
id 1
col1 3
col2 Y
Register 2
==========
id 2
col1 43
col2 X
Register 3
==========
id 2
col1 0
col2 Z
Register 4
==========
id 2
col1 23
col2 F
table2
Register 1
==========
id 1
col1 X
col2 Y
Register 2
==========
id 2
col1 X
col2 X
Register 3
==========
id 2
col1 Z
col2 Z
Register 4
==========
id 2
col1 X
col2 X
table3
Register 1
==========
id 1
col1 ASX
With this database, if I want to know the colnames and their related tabnames of the database that contain X, Y and Z (amoung other values).
It could be something like this:
select tabname, colname
where ('X','Y','Z') in colnamevalues --this has been invented by me
And this should return the following values:
table1.col2
table2.col1
table2.col2
--Note that the columns fetched contains also other values
--different from 'X', 'Y' and 'Z' but T didn't fix in this case
--the whole list of values, only some of them
I have queried for other Q&A but all of them look to use some functions of other databases such as Oracle or SQL Server and I don't understand them very well.
You can get all the tables that exist on a database by querying the systables:
SELECT tabname
FROM systables
WHERE tabtype = 'T' --get only tables
AND tabid > 99; --skip catalog tables
You can join it to the syscolumns table to get the columns:
SELECT t.tabname, c.colname
FROM systables t
INNER JOIN syscolumns c ON (c.tabid = t.tabid)
WHERE t.tabtype = 'T' AND t.tabid > 99;
And if you know the type of values you can even filter it. Example if you're looking for "strings":
SELECT t.tabname, c.colname
FROM systables t
INNER JOIN syscolumns c ON (c.tabid = t.tabid)
WHERE t.tabtype = 'T' AND t.tabid > 99
AND MOD(c.coltype,256) IN (
0, --CHAR
13, --VARCHAR
15, --NCHAR
16, --NVARCHAR
40, --LVARCHAR
43 --LVARCHAR
);
The next example works, but it really should be optimized and bullet proof, but can get you kick off.
When I have time I get another look at it and check what can be optimized and put some error handling.
Another way to do it is scripting, what OS are you running?
Schema creation:
CREATE TABLE tab1(
id INT,
col1 CHAR(3),
col2 CHAR(3)
);
INSERT INTO tab1 VALUES (1, 3, 'Y');
INSERT INTO tab1 VALUES (2, 43, 'X');
INSERT INTO tab1 VALUES (2, 0, 'Z');
INSERT INTO tab1 VALUES (2, 23, 'F');
CREATE TABLE tab2(
id INT,
col1 CHAR(3),
col2 CHAR(3)
);
INSERT INTO tab2 VALUES (1, 'X', 'Y');
INSERT INTO tab2 VALUES (2, 'X', 'X');
INSERT INTO tab2 VALUES (2, 'Z', 'Z');
INSERT INTO tab2 VALUES (2, 'X', 'X');
CREATE TABLE tab3(
id INT,
col1 CHAR(3)
);
INSERT INTO tab3 VALUES (1, 'ASX');
Sample function:
CREATE FUNCTION get_columns()
RETURNING LVARCHAR(257) AS col;
DEFINE stmt VARCHAR(255);
DEFINE tab_name VARCHAR(128,0);
DEFINE tab_id INTEGER;
DEFINE col_name VARCHAR(128,0);
DEFINE o_tname VARCHAR(128,0);
DEFINE o_cname VARCHAR(128,0);
CREATE TEMP TABLE out_table(
t_name VARCHAR(128,0),
c_name VARCHAR(128,0)
);
CREATE TEMP TABLE tab_v (
col1 VARCHAR(255)
);
INSERT INTO tab_v VALUES ('X');
INSERT INTO tab_v VALUES ('Y');
INSERT INTO tab_v VALUES ('Z');
FOREACH tables FOR
SELECT tabname, tabid
INTO tab_name, tab_id
FROM systables
WHERE tabid > 99 AND tabtype = 'T'
FOREACH column FOR
SELECT colname
INTO col_name
FROM syscolumns
WHERE tabid = tab_id
AND MOD(coltype,256) IN (
0, --CHAR
13, --VARCHAR
15, --NCHAR
16, --NVARCHAR
40, --LVARCHAR
43 --LVARCHAR
)
LET stmt = "INSERT INTO out_table "||
"SELECT '"||tab_name||"', '"||col_name||"' "||
"FROM "||tab_name||" "||
"WHERE EXISTS (SELECT 1 FROM tab_v v WHERE v.col1 = "||col_name||");";
EXECUTE IMMEDIATE stmt;
END FOREACH
END FOREACH
FOREACH out FOR
SELECT UNIQUE t_name, c_name
INTO o_tname, o_cname
FROM out_table
RETURN o_tname||"."||o_cname WITH RESUME;
END FOREACH
DROP TABLE out_table;
DROP TABLE tab_v;
END FUNCTION;
EXECUTE FUNCTION get_columns();