Creating View with Condititional Criteria - oracle

In an Oracle Database version 11g I want to create or replace a view BADGES with the following characteristics:
(1) The view will contain two columns: EMP_ID (NUMBER(9), BADGE_NO(NUMBER(13)).
(2) The view will get data from table Public_View’s following columns: EMP_ID (NUMBER(9)), BLDG_CD(NUMBER(4)), OFFSET_ID(NUMBER(10)).
(3) In the view BADGE_NO is the concatenation of BLDG_CD and OFFSET_ID.
(4) Now the part I am having trouble with: When OFFSET_ID < 1000000 (less than 1 million), I want to left pad OFFSET_ID with 0 (zero) for a total of 7 characters. When OFFSET_ID >= 1000000 (equal to or greater than 1 million), I want to left pad OFFSET_ID with 0 (zero) for a total of 8 characters.

You can put a condition on the sign value in the length of the offset ID minus 7, SIGN(length(OFFSET_ID)-7)
SELECT DECODE(SIGN(length(OFFSET_ID)-7), -1, LPAD(OFFSET_ID, 7, '0'), LPAD(OFFSET_ID, 8, '0'))
FROM your_table;
You can also use the CASE statement if you want,(e.g. CASE SIGN(length(OFFSET_ID)-7) )

Related

Fetch substring from right side

I am using oracle. i have a column "ITEM_FINAL" consisting of a string which is a combination of 2 records.
ITEM_FINAL = ITEM_ID + ITEM
I need to segregate ITEM_FINAL and put them into 2 different columns ITEM_ID and ITEM.
However the length of the ITEM_ID is not constant, it can be 1 or 10 or 999 (till 3 digits).
ITEM is constant and it will be 5 digits always.
Eg. ITEM_FINAL = 1256789 (combination of 12 + 56789) or 256789 (combination of 2 + 56789)
I can fetch ITEM details with help of substr
select substr(ITEM_FINAL,-5) from dual;
It will give the last 5 digits (56789) which is constant. How can i fetch the remaining string from 6th position till the start irrespective of length from right hand side for ITEM_ID column
select substr(ITEM_FINAL, 1, length(ITEM_FINAL)-5) ITEM_ID,
substr(ITEM_FINAL,-5) ITEM
from dual;

Need specific number format in Oracle S9(15)V9(2)

I have a requirement to produce amount fields in zoned decimal format with this specific syntax below.
I don’t know if I need to create a function to handle this or if I can tweak the Oracle number format model. I’m thinking it might require some conditional formatting within a function due to the different requirement for number of digits between positive and negative. I will be performing this formatting on a couple of dozen data elements in the procedure so that might be another reason to use a function. Thoughts?
Requirement:
Amount should be represented by 17 characters (positive number) or 16 characters plus a “}” appended to the end (negative number).
Ex. 0.00 should show as 00000000000000000.
Ex. -935,560.00 should show as 00000000093556000}
Using Oracle 12c.
If I understood you correctly, the input is already formatted and its datatype is VARCHAR2. If that's so, then this might do the job:
SQL> with test (col) as
2 (select '0.00' from dual union all
3 select '25.34' from dual union all
4 select '-935,560.00' from dual
5 )
6 select col,
7 lpad(translate(col, 'x,.-', 'x'),
8 case when substr(col, 1, 1) = '-' then 16
9 else 17
10 end, '0') ||
11 case when substr(col, 1, 1) = '-' then '}'
12 else null
13 end result
14 from test;
COL RESULT
----------- --------------------
0.00 00000000000000000
25.34 00000000000002534
-935,560.00 0000000093556000}
SQL>
What does it do?
lines #1 - 5 - sample data
line #7 - translate removes minus sign, commas and dots
lines #7 - 10 - lpad pads the number (without characters from the previous step) with zeros up to the length of 16 (for negative values) or 17 (for positive values) characters
lines #11 - 13 - if it is a negative value, concatenate } to the end of the result string

Add blank rows between a group of mutliple rows - Oracle

Is there a possibility to add blank rows in a group of data rows dynamically ? I have the below query which fetches data in multiple rows. I want to separate, say add blank row after each 5 rows.
The query :
select php.ref_dcp_key, sum(php.group_booking), count(php.group_booking), 0
from gx_pnr_history ph, gx_pnr_his_prof php
where ph.gmpnr_loc_key = php.gmpnr_loc_key
group by php.ref_dcp_key order by php.ref_dcp_key;
#SandeepGowada, you'd have to create an outer structure for the rows and put in the 6th padding row after every 5 data rows.
I second the view of #juergend that this should really be done in the presentation layer (although often it probably requires no less code overall to do it there than in SQL - but it does frustrate the workings of anything other than a static report, like datagrids that allow re-sorting or filtering)
I've knocked together some untested code (using Oracle syntax) which shows an example of how it's done - how we prepare the base data, then build a table of placeholders for all necessary rows, then join the base data onto the placeholders in the appropriate places. I've made some of the calculations a bit more elaborate to show where the number 5 constant is being used.
Also, I haven't included any code to knock off any surplus padding rows in the final group (i.e. as it stands the number of rows returned will always be a multiple of 6, regardless of the underlying data).
Nor have I included any code regarding the required numbers_table or sequence generator.
WITH base_data AS
(
SELECT
php.ref_dcp_key
,sum(php.group_booking) AS group_booking_sum
,count(php.group_booking) AS group_booking_count
,0 AS zero_value_column
,MOD(ROW_NUMBER() OVER (ORDER BY php.ref_dcp_key) - 1, 5) AS group_line_num
,( (ROW_NUMBER() OVER (ORDER BY php.ref_dcp_key) + (5 - 1)) / 5 ) AS row_group_num
--all appearances of the number 5 constants determine the number of lines per group
FROM
gx_pnr_history /*AS*/ ph
INNER JOIN
gx_pnr_his_prof /*AS*/ php
ON (ph.gmpnr_loc_key = php.gmpnr_loc_key)
GROUP BY
php.ref_dcp_key
)
,row_structure AS
(
SELECT
MOD(number - 1, (5 + 1)) AS group_line_num
,number + ((5 + 1) - 1) / (5 + 1) AS row_group_num
FROM
number_table /*this needs to be a reference either to a numbers table, or a number sequence generator*/
WHERE
number BETWEEN 1 AND ((SELECT MAX(row_group_num) * (5 + 1) FROM base_data))
)
SELECT
ref_dcp_key
,group_booking_sum
,group_booking_count
,zero_value_column
,ROW_NUMBER() OVER (ORDER BY row_structure.row_group_num ASC, row_structure.group_line_num ASC) AS final_order
FROM
row_structure
LEFT JOIN
base_data
ON (base_data.row_group_nun = row_structure.row_group_num)
AND (base_data.group_line_num = row_structure.group_line_num)
ORDER BY
final_order

multiply records acrross a rows - pl/sql [duplicate]

In SQL there are aggregation operators, like AVG, SUM, COUNT. Why doesn't it have an operator for multiplication? "MUL" or something.
I was wondering, does it exist for Oracle, MSSQL, MySQL ? If not is there a workaround that would give this behaviour?
By MUL do you mean progressive multiplication of values?
Even with 100 rows of some small size (say 10s), your MUL(column) is going to overflow any data type! With such a high probability of mis/ab-use, and very limited scope for use, it does not need to be a SQL Standard. As others have shown there are mathematical ways of working it out, just as there are many many ways to do tricky calculations in SQL just using standard (and common-use) methods.
Sample data:
Column
1
2
4
8
COUNT : 4 items (1 for each non-null)
SUM : 1 + 2 + 4 + 8 = 15
AVG : 3.75 (SUM/COUNT)
MUL : 1 x 2 x 4 x 8 ? ( =64 )
For completeness, the Oracle, MSSQL, MySQL core implementations *
Oracle : EXP(SUM(LN(column))) or POWER(N,SUM(LOG(column, N)))
MSSQL : EXP(SUM(LOG(column))) or POWER(N,SUM(LOG(column)/LOG(N)))
MySQL : EXP(SUM(LOG(column))) or POW(N,SUM(LOG(N,column)))
Care when using EXP/LOG in SQL Server, watch the return type http://msdn.microsoft.com/en-us/library/ms187592.aspx
The POWER form allows for larger numbers (using bases larger than Euler's number), and in cases where the result grows too large to turn it back using POWER, you can return just the logarithmic value and calculate the actual number outside of the SQL query
* LOG(0) and LOG(-ve) are undefined. The below shows only how to handle this in SQL Server. Equivalents can be found for the other SQL flavours, using the same concept
create table MUL(data int)
insert MUL select 1 yourColumn union all
select 2 union all
select 4 union all
select 8 union all
select -2 union all
select 0
select CASE WHEN MIN(abs(data)) = 0 then 0 ELSE
EXP(SUM(Log(abs(nullif(data,0))))) -- the base mathematics
* round(0.5-count(nullif(sign(sign(data)+0.5),1))%2,0) -- pairs up negatives
END
from MUL
Ingredients:
taking the abs() of data, if the min is 0, multiplying by whatever else is futile, the result is 0
When data is 0, NULLIF converts it to null. The abs(), log() both return null, causing it to be precluded from sum()
If data is not 0, abs allows us to multiple a negative number using the LOG method - we will keep track of the negativity elsewhere
Working out the final sign
sign(data) returns 1 for >0, 0 for 0 and -1 for <0.
We add another 0.5 and take the sign() again, so we have now classified 0 and 1 both as 1, and only -1 as -1.
again use NULLIF to remove from COUNT() the 1's, since we only need to count up the negatives.
% 2 against the count() of negative numbers returns either
--> 1 if there is an odd number of negative numbers
--> 0 if there is an even number of negative numbers
more mathematical tricks: we take 1 or 0 off 0.5, so that the above becomes
--> (0.5-1=-0.5=>round to -1) if there is an odd number of negative numbers
--> (0.5-0= 0.5=>round to 1) if there is an even number of negative numbers
we multiple this final 1/-1 against the SUM-PRODUCT value for the real result
No, but you can use Mathematics :)
if yourColumn is always bigger than zero:
select EXP(SUM(LOG(yourColumn))) As ColumnProduct from yourTable
I see an Oracle answer is still missing, so here it is:
SQL> with yourTable as
2 ( select 1 yourColumn from dual union all
3 select 2 from dual union all
4 select 4 from dual union all
5 select 8 from dual
6 )
7 select EXP(SUM(LN(yourColumn))) As ColumnProduct from yourTable
8 /
COLUMNPRODUCT
-------------
64
1 row selected.
Regards,
Rob.
With PostgreSQL, you can create your own aggregate functions, see http://www.postgresql.org/docs/8.2/interactive/sql-createaggregate.html
To create an aggregate function on MySQL, you'll need to build an .so (linux) or .dll (windows) file. An example is shown here: http://www.codeproject.com/KB/database/mygroupconcat.aspx
I'm not sure about mssql and oracle, but i bet they have options to create custom aggregates as well.
You'll break any datatype fairly quickly as numbers mount up.
Using LOG/EXP is tricky because of numbers <= 0 that will fail when using LOG. I wrote a solution in this question that deals with this
Using CTE in MS SQL:
CREATE TABLE Foo(Id int, Val int)
INSERT INTO Foo VALUES(1, 2), (2, 3), (3, 4), (4, 5), (5, 6)
;WITH cte AS
(
SELECT Id, Val AS Multiply, row_number() over (order by Id) as rn
FROM Foo
WHERE Id=1
UNION ALL
SELECT ff.Id, cte.multiply*ff.Val as multiply, ff.rn FROM
(SELECT f.Id, f.Val, (row_number() over (order by f.Id)) as rn
FROM Foo f) ff
INNER JOIN cte
ON ff.rn -1= cte.rn
)
SELECT * FROM cte
Not sure about Oracle or sql-server, but in MySQL you can just use * like you normally would.
mysql> select count(id), count(id)*10 from tablename;
+-----------+--------------+
| count(id) | count(id)*10 |
+-----------+--------------+
| 961 | 9610 |
+-----------+--------------+
1 row in set (0.00 sec)

Adding a resetting counter (reset on a column's value change) to a PLSQL line in a view

I am using PL SQL developer and Oracle 11g. Here is my issue:
EDIT
It looks like using rank (partition by header number, line number order by date) works.
I would like to create a counter that changes each time a column has a different value than the last. I need to be able to do this in a view (non-modifiable constraint). The goal would be to use this counter to build a new unique key into that view. Thus, I need to reset the 'counter' each time a line number changes in the planning table (see below).
I have 3 tables. Under each table I have put some important columns:
Header
--headerNumber (unique)
Detail
--headerNumber (not unique)
--lineNumber (unique)
Planning lines
--headerNumber (not unique)
--lineNumber (not unique)
--some date
Into the planning lines table, there exist multiple lines, per line number as well. I want to build into a view (this is a constraint, it must be a view) the ability to build a unique number off of the line number from planning.
Here is some example data:
Header (1 row):
Header Number = 1
Detail (2 rows)
Header Number = 1, LineNumber = 1
Header Number = 1, LineNumber = 2
Planning (4 rows)
Header Number = 1, Line number = 1, date = 01/01/14
Header Number = 1, Line number = 1, date = 01/02/14
Header Number = 1, Line Number = 2, date = 01/01/14
Header Number = 1, Line Number = 2, date = 01/03/14
Into the view I want it to look like this:
HeaderNumber | Line Number | 'Counter' (What I am trying to create)
1 | 1 | 1
1 | 1 | 2
1 | 2 | 1
1 | 2 | 2
Here are some final issues I have faced:
It does not appear as if I can use row number -- The view will contain multiple headers, and more importantly, row number seems to be built out of the entire select's contents -- thus I can't trim it. Row number ends up counting 1,2,3,4 (row above).
Does anyone have an idea about how to build this into a select (to put into a view)? I know this can be done by a procedure but I really need a view.
Regards,
Seriously Confused Man
I'm not sure what you have tried exactly when you say "row number" - do you mean ROWNUM or row_number()?
Doesn't this do what you want?
select headernumber, linenumber,
row_number() over (partition by headernumber order by linenumber)
as counter
from ...;

Resources