Birt cross tab - disable column grouping - birt

When I use a crosstab;
col1 | col2 | col3
a | 22 | 33
| 77 | 44
| 32 | 655
b | 22 | 67
c | 90 | 23
How can I force the cross tab to repeat 'a' on each line?

So the solution is to create some sort of concatenated response, and base the 'group' on that column but then never display it;
i.e.
select col1 || col2 as concat_column, col1, col2, col3 from database
The group is then based on concat_column, with col1 and col2 nested in it. In the crosstab, the width of the first column is then set to 0.
It seems like you have to set the report to 'fixed layout' to fully hide column the 0 width column.

Related

How to create an oracle procedure to find out the distinct records from duplicate and update

I want to know the how can i create an oracle procedure to find out the distinct records from whole table and insert into same table by updating value of another column
Suppose the table name is temp and below is the structure of the table
| id | address | key |ver_id
| 1 | 242 Street | 123 | 1
| 2 | 242 Street | 123 |2
| 3 | 242 Street | 123 |3
| 4 | 242 Long St | 456 |4
So,how to write a oracle procedure to select 1 record from first 3 rows for above columns which is duplicate and insert that distinct record at the end of the table by updating ver_id as -1 like below:
| id | address | key |ver_id**
| 1 | 242 Street | 123 | 1
| 2 | 242 Street | 123 |2
| 3 | 242 Street | 123 |3
| 4 | 242 Long St | 456 |4
| 5 | 242 Street | 123 |-1
I have tried to write the simple procedure to find out the duplicates from table
create or replace PROCEDURE demo (
key1 IN VARCHAR2
) AS
CURSOR c_temp IS
SELECT
*
FROM
temp
WHERE
key = key1;
r_temp c_temp%ROWTYPE;
BEGIN
OPEN c_temp;
LOOP
FETCH c_temp INTO r_temp;
EXIT WHEN c_temp%notfound;
dbms_output.put_line('id: '
|| r_temp.id
|| ' address: '
|| r_temp.address);
END LOOP;
CLOSE c_temp ;
END;
But above procedure is just fetch the records with duplicate records, but i need help to write the procedure to select distintct records from duplicate and insert into same table with different ver_id as -1
You didn't say how is id column supposed to get its value, so I just created a sequence.
This is sample data:
SQL> select * from temp order by id;
ID ADDRESS KEY VER_ID
---------- ----------- ---------- ----------
1 242 street 123 1
2 242 street 123 2
3 242 street 123 3
4 242 long st 456 4
SQL> create sequence seq_temp start with 5;
Sequence created.
You don't need PL/SQL procedure for what you're doing; a SQL statement is enough:
data CTE finds duplicates (their rn value is larger than 1)
data2 fetches distinct rows. Why? Because you can't use a sequence along with distinct
the final select composes values that should be inserted
So:
SQL> insert into temp (id, address, key, ver_id)
2 with data as
3 (select t.*,
4 row_number() over (partition by address, key order by id) rn
5 from temp t
6 ),
7 data2 as
8 (select distinct d.address, d.key
9 from data d
10 where d.rn > 1
11 )
12 select seq_temp.nextval, address, key, -1
13 From data2;
1 row created.
Result:
SQL> select * from temp order by id;
ID ADDRESS KEY VER_ID
---------- ----------- ---------- ----------
1 242 street 123 1
2 242 street 123 2
3 242 street 123 3
4 242 long st 456 4
5 242 street 123 -1 --> here it is
SQL>

index part of column with flexible number of character

I want to index part of a column with value like this : #aaa/453 . it means that the value in this column consist of four parts , symbol character / number .
in our query we just have Numerical section so we want to have an index on this part.
the number of character in each part is changeable .
please help me
Here's an example: table contains values similar to what you described.
SQL> create table test (col varchar2(20));
Table created.
SQL> insert into test
2 select '#aaa/453' from dual union all
3 select '$bcdxyz/35' from dual union all
4 select '#gf/203' from dual;
3 rows created.
In order to select rows from the table, one option is to use such a query:
SQL> select * from test
2 where regexp_substr(col, '\d+$') = '35';
COL
--------------------
$bcdxyz/35
So, let's create a function-based index:
SQL> create index i1test on test (regexp_substr(col, '\d+$'));
Index created.
What does the explain plan say?
SQL> explain plan for
2 select * from test
3 where regexp_substr(col, '\d+$') = '35';
Explained.
SQL> select * from table(dbms_xplan.display);
PLAN_TABLE_OUTPUT
----------------------------------------------------------------------------------------------
Plan hash value: 210954056
--------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
--------------------------------------------------------------------------------------
| 0 | SELECT STATEMENT | | 1 | 54 | 2 (0)| 00:00:01 |
| 1 | TABLE ACCESS BY INDEX ROWID| TEST | 1 | 54 | 2 (0)| 00:00:01 |
|* 2 | INDEX RANGE SCAN | I1TEST | 1 | | 1 (0)| 00:00:01 |
--------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
2 - access( REGEXP_SUBSTR ("COL",'\d+$')='35')
Note
-----
- dynamic sampling used for this statement (level=2)
18 rows selected.
SQL>
Looks like it might help. Try it, see how it behaves.

A scenario for combining multiple rows in oracle

I have a table with vaues like
MY_ID(NUMBER) COL_1(NUMBER) COL_2(NUMBER) COL_3(VARCHAR2)
11 1001 NULL GT
11 NULL 1002 TG
11 NULL 1003 TG2
12 1004 NULL GT
12 NULL 1006 TG
12 NULL 1005 TG2
My expected result is
MY_ID(NUMBER) COL_1(NUMBER) COL_2(NUMBER) COL_3(VARCHAR2)
11 1001 1003 TG2
12 1004 1006 TG
I can use MAX for numbers, but how about the Varchar2?
How can I combine multiple rows like this?
MAX aggregate functions can be used for varchar too, see this example:
SELECT my_id, max( col_1 ), max( col_2 ), max( col_3 )
FROM Table1
GROUP BY my_id;
Demo: http://sqlfiddle.com/#!4/406e8d7/6
| MY_ID | MAX(COL_1) | MAX(COL_2) | MAX(COL_3) |
|-------|------------|------------|------------|
| 11 | 1001 | 1003 | TG2 |
| 12 | 1004 | 1006 | TG2 |
However, in your question is stated that for the record 12 must be returned value TG instead TG2. I'm guessing that you want to return not the maximum column value of COL_3, but the value from the record for which the maximum value from column COL2 exists. In such a case you can use a query like this:
SELECT my_id, max( col_1 ), max( col_2 ),
max( col_3 ) KEEP (DENSE_RANK LAST ORDER BY col_2 NULLS FIRST)
FROM Table1
GROUP BY my_id;
Demo: http://sqlfiddle.com/#!4/406e8d7/6
| MY_ID | MAX(COL_1) | MAX(COL_2) | MAX(COL_3)KEEP(DENSE_RANKLASTORDERBYCOL_2NULLSFIRST) |
|-------|------------|------------|------------------------------------------------------|
| 11 | 1001 | 1003 | TG2 |
| 12 | 1004 | 1006 | TG |

PL/SQL ORACLE query as Result

How to write SQL Query for below condition
Result must be in PL/SQL Query:
MY_TABLE and data is Like bellow:
| sl. no | col1 | col2 | col3 |col4 | col5 ---col30| col41|col42|....col50
+------ +------+------ +------ +------+
| 1001 | 50 | 101 | 12 | 40 |
| 1002 | 30 | 250 | 80 | |
| 1003 | 40 | 150 | 90 | |
| 1004 | 50 | 250 | 20 | |
| 1005 | 70 | 300 | 30 | 50 |
| 1006 | 80 | 400 |
col1, col2,col3,...col30
I want to retrieve col1..to col30 data(value) against sl.no (where condition is sl.no) if col data is available. Oracle 9I
as row wise
only in PL/SQL oracle 9i
result like:
1001 | 50
1001 | 101
1001 | 12
1001 | 40
1002 | 30
1002 |250
1002 | 80
1003 | 40
1003 | 150
1003 | 90
1004 | 50
1004 | 250
1004 | 20
1005 |70
1005 |300
1005 |30
1005 |50
1006 |80
1006 |400
select *
from (
select no, decode( rn, 1, col1, 2, col2, 3, col3, 4, col4, 5, col5, ...., 30, col30) col
from table_name, (select rownum rn from all_objects where rownum <= 30 )
)
where col is not null
or
select *
from (
select no, decode( rn, 1, col1, 2, col2, 3, col3, 4, col4, 5, col5, ...., 30, col30) col
from table_name, (select rownum rn from dual connect by rownum <= 30 )
)
where col is not null
the second one is better but I don't remember will it works in 9i or not
To unpivot the pre-11g way, you would need to do something like:
WITH my_table AS (SELECT 1001 sl_no, 50 col1, 101 col2, 12 col3, 40 col4 FROM dual UNION ALL
SELECT 1002 sl_no, 30 col1, 250 col2, 80 col3, NULL col4 FROM dual UNION ALL
SELECT 1003 sl_no, 40 col1, 150 col2, 90 col3, NULL col4 FROM dual UNION ALL
SELECT 1004 sl_no, 50 col1, 250 col2, 20 col3, NULL col4 FROM dual UNION ALL
SELECT 1005 sl_no, 70 col1, 300 col2, 30 col3, 50 col4 FROM dual UNION ALL
SELECT 1006 sl_no, 80 col1, 400 col2, NULL col3, NULL col4 FROM dual),
dummy AS (SELECT LEVEL lvl
FROM dual
CONNECT BY LEVEL <= 4 -- number of columns to unpivot
),
results AS (SELECT mt.sl_no,
d.lvl,
CASE WHEN d.lvl = 1 THEN 'COL1'
WHEN d.lvl = 2 THEN 'COL2'
WHEN d.lvl = 3 THEN 'COL3'
WHEN d.lvl = 4 THEN 'COL4'
END col_name,
CASE WHEN d.lvl = 1 THEN col1
WHEN d.lvl = 2 THEN col2
WHEN d.lvl = 3 THEN col3
WHEN d.lvl = 4 THEN col4
END col_value
FROM my_table mt
CROSS JOIN dummy d)
SELECT sl_no,
col_name,
col_value
FROM results
WHERE col_value IS NOT NULL
ORDER BY sl_no, lvl;
SL_NO COL_NAME COL_VALUE
---------- -------- ----------
1001 COL1 50
1001 COL2 101
1001 COL3 12
1001 COL4 40
1002 COL1 30
1002 COL2 250
1002 COL3 80
1003 COL1 40
1003 COL2 150
1003 COL3 90
1004 COL1 50
1004 COL2 250
1004 COL3 20
1005 COL1 70
1005 COL2 300
1005 COL3 30
1005 COL4 50
1006 COL1 80
1006 COL2 400
The dummy subquery is used to generate a set of rows that is the same number as the number of columns to be unpivoted. In your example, it's 4.
We then cross join that to the table - this means each row in the table is duplicated 4 times - one for each column to be unpivoted.
Once we have that, we say "display the first column to be unpivoted on the first row, the second column on the second row, the third column on the third row, etc..."
I've also included a column which lists the column name that the corresponding value came from; I know you didn't ask for this info in your question, but it's usually required, and easy enough to produce.
Once we have unpivoted the columns, we then remove any values that are null.

oracle merge performance

MERGE INTO table1 t1
USING
(SELECT column1
FROM table2 join table3 on...
WHERE table2.column5 = 'xyz') t2
ON (t1.column1 = t2.column1 AND t1.column2 = somevalue
AND t1.column3 = someothervalue)
WHEN not matched
THEN
INSERT ...
The "on" part will reject most of the rows, but does merge query all the rows that are inside "using" first? In that case that part will be running uselessly most of the times because 95% of the rows will not match t1.column2 = somevalue
AND t1.column3 = someothervalue. Or is Oracle smart enough to not do that?
Yes, Oracle will rewrite the query to join table1 to the using view query. so if t1.column2 = somevalue AND t1.column3 = someothervalue is selective and Oracle realises this, you should see in the plan that the query will drive from TABLE1 and then join into the tables in the using view. just run an explain plan to check it. i.e.
set linesize 200 pagesize 200
explain plan for
merge....;
select * from table(dbms_xplan.display());
and you should see that Oracle has done this for you.
eg:
SQL> create table table1(id number primary key, t2_id number, str varchar2(20), notes varchar2(20));
Table created.
SQL> create table table2(id number primary key, notes varchar2(20));
Table created.
SQL>
SQL> insert into table1
2 select rownum, rownum, case mod(rownum, 100) when 0 then 'ONE' else 'TWO' end, null
3 from dual connect by level <=1000000;
1000000 rows created.
SQL>
SQL> insert into table2
2 select rownum, dbms_random.string('x', 10)
3 from dual connect by level <=1000000;
1000000 rows created.
SQL>
SQL> create index table1_idx on table1(str);
Index created.
SQL> exec dbms_stats.gather_table_stats(user, 'TABLE1', method_opt=>'for all indexed columns size skewonly');
PL/SQL procedure successfully completed.
SQL> exec dbms_stats.gather_table_stats(user, 'TABLE2');
PL/SQL procedure successfully completed.
so ill add t1.str = 'ONE' when is very selective:
SQL> explain plan for
2 merge into table1 t1
3 using (select * from table2 t where t.id > 1000) t2
4 on (t2.id = t1.t2_id and t1.str = 'ONE')
5 when matched then update
6 set t1.notes = t2.notes;
Explained.
SQL> #explain ""
Plan hash value: 2050534005
---------------------------------------------------------------------------------------------
| Id | Operation | Name | Rows | Bytes | Cost (%CPU)| Time |
---------------------------------------------------------------------------------------------
| 0 | MERGE STATEMENT | | 441 | 11025 | 929 (5)| 00:00:12 |
| 1 | MERGE | TABLE1 | | | | |
| 2 | VIEW | | | | | |
|* 3 | HASH JOIN | | 441 | 12348 | 929 (5)| 00:00:12 |
|* 4 | TABLE ACCESS BY INDEX ROWID| TABLE1 | 441 | 5733 | 69 (2)| 00:00:01 |
|* 5 | INDEX RANGE SCAN | TABLE1_IDX | 8828 | | 21 (0)| 00:00:01 |
|* 6 | TABLE ACCESS FULL | TABLE2 | 994K| 14M| 848 (4)| 00:00:11 |
-------------------------------------------------------------------------------------------
Predicate Information (identified by operation id):
---------------------------------------------------
   3 - access("T"."ID"="T1"."T2_ID")
   4 - filter("T1"."T2_ID">1000)
   5 - access("T1"."STR"='ONE')
   6 - filter("T"."ID">1000)
you can see its applied the index on table1
INDEX RANGE SCAN | TABLE1_IDX
thus removing a lot of rows scanned on table1 (hash join was more appropriate considering my table, but in your case you may see a nested loop approach on the join step).

Resources