Replacing/Converting 1 to A with Oracle/PLSQL - oracle

Firstly, I greatly appreciate any feedback that anyone can offer. I am using Oracle SQL Developer, Version 4.0.2.15, Build 15.21.
I know and understand that many, many similar questions have been asked, as I've searched around on stackoverflow as well as the rest of the internet. However, the corresponding answers are either too vague or too extravagant, and attempt to do things that are way over my head and not what I am trying to accomplish. I am extremely new to SQL and haven't seriously done any coding since I did Java about 12 years ago. So please understand that something simple to you, is not so simple and obvious to me.
My bare-bones endstate that I am shooting for is taking a pre-existing Oracle Table Column, which is called 'service_level', that has parameters of 1-3, and making them A-C (where A=1, B=2, C=3). The reason for this is that I have an ArcGIS gdB featureclass that has a corresponding column, called 'MaintServi', with the parameters of A-C. I am going to join them using ArcToolbox once I have converted/replaced the 1-3 to A-C, and have exported them from Oracle into an Arc gdB as another table. The reason being is that the featureclass (obviously) has geometry, but this particular Oracle table does not.
From what I have gathered I know (or think) I will need to use something like:
chr(ord('a') + 3)
^ Where I will need to use/call upon the chr/ord functions. However, due to my inexperience, I cannot think of how to properly call this without getting an error. Below is what I have for my query thus far (but without chr/ord). I just need to figure out how to correctly insert it into my query to achieve the desired results.
SELECT v_wv_wp_crew.*,
Substr(v_wv_wp_crew.winter_supp_id, 1, 6) AS CostCenter,
Substr(v_wv_wp_crew.winter_supp_id, 8, 11) AS Crew_Supp_ID
FROM v_wv_wp_crew
WHERE crew_on_road >= '13-FEB-12'
AND ( operation = 2
OR operation = 3 );
Thanks again and hopefully I have complied with the posting rules of stackoverflow.
# Mark J. Bobak -
When implementing his ideas I get either this (Like I said, i'm not sure how to insert it properly without receiving an error)
SELECT v_wv_wp_crew.*,
Substr(v_wv_wp_crew.winter_supp_id, 1, 6) AS CostCenter,
Substr(v_wv_wp_crew.winter_supp_id, 8, 11) AS Crew_Supp_ID
FROM v_wv_wp_crew
WHERE crew_on_road >= '13-FEB-12'
AND ( operation = 2
OR operation = 3 )
UNION ALL
WITH service_level as (select 1 service_level from dual
union all
select 2 service_level from dual union all
select 3 service_level from dual)
select decode(service_level,1,'A',2,'B',3,'C') from service_level;
I receive the following error:
*ORA-32034: unsupported use of WITH clause
32034. 00000 - "unsupported use of WITH clause"
*Cause: Inproper use of WITH clause because one of the following two reasons
1. nesting of WITH clause within WITH clause not supported yet
2. For a set query, WITH clause can't be specified for a branch.
3. WITH clause can't sepecified within parentheses.
Action: correct query and retry
Error at Line: 14 Column: 25
Or I receive an output of only 3 rows (A, B, C) if I run the query separately - sorry I don't have enough reputation to post the image yet.

You can use the DECODE() function. Something like this should work:
with list_of_digits as (select 1 col_a from dual
union all
select 2 col_a from dual
union all
select 3 col_a from dual
union all
select 4 col_a from dual)
select decode(col_a,1,'A',2,'B',3,'C','Other') from list_of_digits;
Using your query, try this:
WITH service_level as (select 1 service_level from dual
union all
select 2 service_level from dual union all
select 3 service_level from dual)
select decode(service_level,1,'A',2,'B',3,'C') from service_level
union all
SELECT v_wv_wp_crew.*,
Substr(v_wv_wp_crew.winter_supp_id, 1, 6) AS CostCenter,
Substr(v_wv_wp_crew.winter_supp_id, 8, 11) AS Crew_Supp_ID
FROM v_wv_wp_crew
WHERE crew_on_road >= '13-FEB-12'
AND ( operation = 2
OR operation = 3 );

ord isn't an Oracle function. The equivalent Oracle function is ASCII. However, even substituting in the correct function, I don't see how that gets you what you want.
It seems most likely that you just want to add a column (I'd use case to translate the values):
SELECT v_wv_wp_crew.*,
Substr(v_wv_wp_crew.winter_supp_id, 1, 6) AS CostCenter,
Substr(v_wv_wp_crew.winter_supp_id, 8, 11) AS Crew_Supp_ID,
case service_level
when '1' then 'a'
when '2' then 'b'
when '3' then 'c'
end as service_level_alpha
FROM v_wv_wp_crew
WHERE crew_on_road >= '13-FEB-12'
AND ( operation = 2
OR operation = 3 );
If you want to return this column as service_level, then you'll need to return the full list of columns instead of using the asterisk.
Since this is a straight-forward character swap, you could use translate to really streamline the operation: translate(service_level,'123','abc'). However, I vastly prefer case over either decode or translate for readability

Related

Allow multiple values from SSRS in oracle

I have a query that gets contract_types 1 to 10. This query is being used in an SSRS report to filter out a larger dataset. I am using -1 for nulls and -2 for all.
I would like to know how we would allow multiple values - does oracle concatenate the inputs together so '1,2,3' would be passed in? Say we get select -1,0,1 in SSRS, how could we alter the bottom query to return values?
My query to get ContractTypes:
SELECT
ContractType,
CASE WHEN ContractType = -2 THEN 'All'
WHEN ContractType = -1 THEN'Null'
ELSE to_Char(ContractType)
END AS DisplayFigure
FROM ContractTypes
which returns
ContractType DisplayFig
-1 Null
0 0
1 1
2 2
3 3
4 4
5 5
6 6
7 7
8 8
9 9
10 10
This currently is only returning single values or all, not muliple values:
SELECT *
FROM Employee
WHERE NVL(CONTRACT_TYPE, -1) = :contract_type or :contract_type = -2
I'm assuming we want to do something like:
WHERE NVL(CONTRACT_TYPE, -1) IN (:contract_type)
But this doesn't seem to work.
Data in Employee
Name ContractType
Bob 1
Sue 0
Bill Null
Joe 2
In my report, I want to be able to select contract_type as -1(null),0,1 using the 'allow muliple values' checkbox. At the moment, I can only select either 'all' using my -2 value, or single contract types.
My input would be: contract type = -1,1,2
My output would be Bill, Bob, Joe.
This is how I'm executing my code
I use SSRS with Oracle a lot so I see where you're coming from. Thankfully, they work pretty well together.
First make sure the parameter is set to allow multiple values. This adds a Select All option to your dropdown so you don't have to worry about adding a special case for "All". You'll want to make sure the dataset for the parameter has a row with -1 as the Value and a friendly description for the Label.
Next, the WHERE clause would be just as you mentioned:
WHERE NVL(CONTRACT_TYPE, -1) IN (:contract_type)
SSRS automatically populates the values. There is no XML or string manipulation needed. Keep in mind that this will not work with single-value parameters.
If for some reason this still doesn't work as expected in your environment, there is another workaround you can use which is more universal and works even with ODBC connections.
In the dataset parameter properties, use an expression like this to concatenate the values into a single, comma-separated string:
="," + Join(Parameters!Parameter.Value, ",") + ","
Then use an expression like this in your WHERE clause:
where :parameter like '%,' + Column + ',%'
Obviously, this is less efficient because it most likely won't be using an index, but it works.
I don't know SSRS, but - if I understood you correctly, you'll have to split that comma-separated values list into rows. Something like in this example:
SQL> select *
2 from dept
3 where deptno in (select regexp_substr('&&contract_type', '[^,]+', 1, level)
4 from dual
5 connect by level <= regexp_count('&&contract_type', ',') + 1
6 );
Enter value for contract_type: 10,20,40
DEPTNO DNAME LOC
---------- -------------------- --------------------
20 RESEARCH DALLAS
10 ACCOUNTING NEW YORK
40 OPERATIONS BOSTON
SQL>
Applied to your code:
select *
from employee
where nvl(contract_type, -1) in (select regexp_substr(:contract_type, '[^,]+', 1, level)
from dual
connect by level <= regexp_substr(:contract_type, ',') + 1
)
If you have the comma separated list of numbers and then if you like to split it then, the below seems simple and easy to maintain.
select to_number(column_value) from xmltable(:val);
Inputs: 1,2,3,4
Output:
I guess I understood your problem. If I am correct the below should solve your problem:
with inputs(Name, ContractType) as
(
select 'Bob', 1 from dual union all
select 'Sue', 0 from dual union all
select 'Bill', Null from dual union all
select 'Joe', 2 from dual
)
select *
from inputs
where decode(:ContractType,'-2',-2,nvl(ContractType,-1)) in (select to_number(column_value) from xmltable(:ContractType))
Inputs: -1,1,2
Output:
Inputs: -2
Output:

How to make a select on different table in function of an argument?

In function of myVariable, this select is made from a different table.
select
*
from
table(decode(myVariable,1,sys.odcinumberlist(1,2),sys.odcinumberlist(3)))
result
if myVAriable =1 then 1,2
else 3
I would like to to make the same thing but instead of having sys.odcinumberlist, I would like to have the list of all integer inferior than 100.
I have tried a lot of way to do that. This for instance, but none has worked
select *
from
table(decode(myvariable,1,table(select level from dual connect by level<100),sys.odcinumberlist(3)))
wanted result:
myvariable =1 then 1,2,3....99
else 3
You have to cast this subquery as odcinumberlist:
select * from
table(decode(myvariable, 1,
cast(multiset(
select level from dual connect by level<100)
as sys.odcinumberlist),
sys.odcinumberlist(3)))

How can a query expanding a delimited list be optimized?

I'm new to Oracle and recently ran into the following query. I'm trying to understand what it's doing and hopefully rewrite it to optimize it. In this example, :NameList would be a comma separated list (like: "Bob,Bill,Fred") and then :N_NameList would be the number of tokens (in above example, 3)
SELECT ... FROM
(
SELECT
REGEXP_SUBSTR(:NameList,'[^,]+',1,LEVEL, 'i') Name
FROM DUAL CONNECT BY LEVEL <= :N_NameList
) x
INNER JOIN PEOPLE ppl
ON ppl.Name LIKE x.Name
...
From what I can tell, it expands out the delimited list into unique rows and then joins it with the following tables for each name, but I'm not sure if that's all it's doing. If that is the case, is there a better way to accomplish this?
You could try this instead:
select ...
from people ppl
where instr (','||:NameList||',',
','||ppl.name||',') > 0;
is there a better way to accomplish this?
Well, you could get rid of N_NameList because you can easily count number of tokens. This doesn't mean that it is a better way, it's just a different option. To be honest, it is probably slower option than yours as I have to calculate something that you entered as a parameter.
As this example is based on SQLPlus, I've used & instead of : for substitution variables. && means that it'll "remember" previously entered value (otherwise, I should type NameList twice.
Your current query:
SQL> select regexp_substr('&namelist', '[^,]+', 1, level, 'i') name
2 from dual
3 connect by level <= &n_namelist;
Enter value for namelist: Bob,Bill,Fred
Enter value for n_namelist: 3
Bob
Bill
Fred
Calculated N_NameList (using REGEXP_COUNT):
SQL> select regexp_substr('&&namelist', '[^,]+', 1, level, 'i') name
2 from dual
3 connect by level <= regexp_count('&&namelist', ',') + 1;
Enter value for namelist: Bob,Bill,Fred
Bob
Bill
Fred

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)

Is it possible to check whether a value is in the list of items in a single Oracle Decode Function?

I would like to know that if I can compare a value with a list of items in an decode function. Basically I want to know that if is it possible to make a decode statement's 'search' value a list. For example,
decode(task_id, (1,2,3), 3 * task_time)
This piece of code won't compile though. Is this the only option for this case then (without using case-when) or are there alternative ways of doing this?
decode(task_id, 1, 3 * task_time,
2, 3 * task_time,
3, 3 * task_time)
I am using Oracle 10gR2. Any help is much appreciated.
If a single list of values is sufficient, you can turn it into a CASE and IN clause:
case when task_id in (1, 2, 3) then 3 * task_time else null end
I don't think its possible to use a list with decode in this way. Per the docs:
DECODE compares expr to each search value one by one. If expr is equal
to a search, then Oracle Database returns the corresponding result. If
no match is found, then Oracle returns default
So task_id is compared with a search value one by one. If search value was a list, you couldn't compare with a single value.
I found a solution :)
select
decode(
task_id,
(select task_id from dual where task_id in (1,2,3)),
3*task_time)
decode ( (taskid-1)*(taskid-2)*(taskid-3), 0, 3 * tasktime ) could do what you want
Here's a working example:
with a as (
select 1 taskid, 11 tasktime from dual union all
select 2 taskid, 11 tasktime from dual union all
select 3 taskid, 11 tasktime from dual union all
select 4 taskid, 11 tasktime from dual
)
select
taskid,
decode (
(taskid-1) *
(taskid-2) *
(taskid-3) ,
0, 3 * tasktime
) decoded
from a;
you can use union all:
select 3 * task_time from your_table where task_id in (1,2,3)
union all
select task_time from your_table where task_id not in (1,2,3)
but why ?
In if condition :
IF vVal in (3,1,2) THEN
dbms_output.put_line('FOUND');
ELSE
dbms_output.put_line('NOT FOUND');
END IF;
I've seen instr(...) and static strings used as a way to quickly determine whether a value is one of multiple you're looking for as a condition for what value you return. You may need to choose a delimiter, but with limited datasets you can even omit it. It avoids using case-when, subqueries, and PL/SQL. As far as I know, there is no shorter way to do this:
decode(instr('123', taskid), 0, null, taskid * 3)
It's also very convenient when you want to set exceptions (for instance returning taskid without multiplication if it equals 1):
decode(instr('12345', taskid), 0, null, 1, taskid, taskid * 3)

Resources