I've a column that have a varchar2 like this: ..x...y...z..
I want to replace x to 1, y to 2 and z to 3.
Is it possible to have multiple replace after each other in select statement to replace these characters?
(select)
replace(varchar2, 'x', '1')
replace(varchar2, 'y', '2')
replace(varchar2, 'z', '3')
Or use TRANSLATE function if that fits your needs http://docs.oracle.com/cd/E11882_01/server.112/e26088/functions216.htm#SQLRF06145
Do you mean something like this:
replace(replace(replace(varchar2, 'x', '1'), 'y', '2'), 'z', '3')
Related
The following example is only used to understand why I want to do what I'm doing. But the second example is better to understand the problem.
I want to create a table. Each column represent a value that should be in the table.
WITH
FUNCTION f(arg INTEGER, colum_name VARCHAR2)
RETURN VARCHAR2
IS
BEGIN
IF arg = 0
THEN
RETURN 'this column doesn''t exist';
ELSE
RETURN colum_name;
END IF;
END;
t (a) AS (SELECT COLUMN_VALUE FROM sys.odcivarchar2list ('a', 'b', 'd'))
SELECT *
FROM (SELECT a FROM t)
PIVOT (f(COUNT (*),a)
FOR a
IN ('a', 'b', 'c', 'd'));
What it should return:
a
b
c
d
a
b
this column doesn' t exist
d
Because I need the name of the column, I can't make a subquery that takes the result of the aggregation function and uses f in the principal query.
Now the second example:
the first query counts every lines with the value 1 and then the value 2.
SELECT * FROM (SELECT 1 a FROM DUAL) PIVOT (COUNT (*) FOR a IN (1, 2));
It's working.
But this query doesn't work. count(*)+1 isn't considered as a aggreagation functon
SELECT *
FROM (SELECT 1 a FROM DUAL) PIVOT (COUNT (*) + 1 FOR a IN (1, 2));
[Error] Execution (40: 53): ORA-56902: expect aggregate function inside pivot operation
I can't do that ouside the pivot because: bold text in the first example.
to test the code:
https://dbfiddle.uk/?rdbms=oracle_18&fiddle=8ea8a78038fbadddb417c330f0d33314
----------------------------- This part was added after MTO gived an answer:
WITH
FUNCTION f(
arg IN INTEGER,
colum_name IN VARCHAR2
) RETURN VARCHAR2
IS
BEGIN
IF arg = 0 THEN
RETURN 'this column doesn''t exist';
ELSE
RETURN colum_name;
END IF;
END;
t (a) AS (
SELECT COLUMN_VALUE
FROM sys.odcivarchar2list ('a', 'b', 'd')
)
SELECT f(a, 'a') AS a,
f(b, 'b') AS b,
f(c, 'c') AS c,
f(d, 'd') AS d
FROM t
PIVOT (
COUNT (*)
FOR a IN (
'a' AS a,
'b' AS b,
'c' AS c,
'd' AS d
)
);
It's working but:
But In the real case I have a lot of column, I would like to avoid to rewrite them all.
And I would like to avoid making typing error. writing 2 time the same name is prone making typing error.
It will NOT work inside the PIVOT you MUST call the function outside of the PIVOT:
WITH
FUNCTION f(
arg IN INTEGER,
colum_name IN VARCHAR2
) RETURN VARCHAR2
IS
BEGIN
IF arg = 0 THEN
RETURN 'this column doesn''t exist';
ELSE
RETURN colum_name;
END IF;
END;
t (a) AS (
SELECT COLUMN_VALUE
FROM sys.odcivarchar2list ('a', 'b', 'd')
)
SELECT f(a, 'a') AS a,
f(b, 'b') AS b,
f(c, 'c') AS c,
f(d, 'd') AS d
FROM t
PIVOT (
COUNT (*)
FOR a IN (
'a' AS a,
'b' AS b,
'c' AS c,
'd' AS d
)
);
Which outputs:
A
B
C
D
a
b
this column doesn't exist
d
Addressing the comments:
Because I need the name of the column, I can't make a subquery that takes the result of the aggregation function and uses f in the principal query.
Yes, you can; exactly as the example above shows.
An SQL statement (in any dialect, not just Oracle) must have a known, fixed number of output columns before it can be compiled. Therefore, you can know all the columns that will be output from the PIVOT statement (one for each item in the FOR clause and one for each column from the original table that is not used in the PIVOT clause) and can call the function on those columns you want to.
And I would like to avoid making typing error. writing 2 time the same name is prone making typing error.
Perform a code review on your code after writing it and use testing to check that it works properly.
Your second query can be fixed using exactly the same principle (except you need quoted identifiers as, without explicitly providing identifiers, the identifier created by the pivot start with numbers and must be quoted):
SELECT "1" + 1 AS "1",
"2" + 1 AS "2"
FROM (SELECT 1 a FROM DUAL)
PIVOT (COUNT (*) FOR a IN (1, 2));
Which outputs:
1
2
2
1
db<>fiddle here
This solution make possible to get the names of elements inside the table, without having to write these elements 2 times.
The elements that are not in the table have the value null. If you want to change use something other than the null, you have to use this query as a sub-query and use the function nvl. But you have to rewrite the name of all the columns again....
This solution suits me, but it's not totally satisfactory...
with
t (a) AS (SELECT COLUMN_VALUE FROM sys.odcivarchar2list ('a', 'b', 'd'))
SELECT *
FROM t
ANY_VALUE(a)
FOR a
IN ('a', 'b', 'c', 'd'));
code
I have a string of comma separated values "'a', 'b', 'c', 'd'".
select 'a', 'b', 'c', 'd' from dual;
How do i select these into a temp table all in a single column rather than a column per value? So my select would return output like this:
value
a
b
c
d
Note: Doing the normal multiple selects and unions is not allowed due to the comma separated values as the input.
I guess you could do something like this, taking the assumption that you can qualify your columns:
SQL> select val from
( select 'a' as c1, 'b' as c2, 'c' as c3, 'd' as c4 from dual ) unpivot include
nulls ( val for col in ( c1,c2,c3,c4 ) );
V
-
a
b
c
d
In this option, it does not matter what values you have in 'a' , 'b' , 'c' 'd' , as basically unpivot is transposing columns into rows.
Hope it helps
You can use hierarchy query as follows:
SQL> with YOUR_DATA as (select q'#'a', 'b', 'c', 'd'#' as str from dual)
2 -- Your query starts from here
3 SELECT TRIM('''' FROM TRIM(REGEXP_SUBSTR(STR,'[^,]+',1,LEVEL))) AS RESULT
4 FROM YOUR_DATA CONNECT BY
5 LEVEL <= 1 + REGEXP_COUNT(STR,',');
RESULT
------------------
a
b
c
d
SQL>
If you have version 12c or later, and if your data has no weird characters, then:
with input(x) as (select q'#'a', 'b', 'c', 'd'#' from dual)
select x_out from input,
json_table(
'['||replace(x, '''', '"')||']', '$[*]'
columns x_out path '$'
);
X_OUT
a
b
c
d
What is the sql code to print 'Query' if the data in field = 'Q'?
Welcome to SO!
An example out of the box:
select decode(dummy, 'X', 'Y') from dual;
For your scenario, something like:
select decode(mycol, 'Q', 'Query') mycol from mytable;
Best of luck!
Preferred option is to use CASE because of readability; although, as #Bjarte suggested, DECODE can also be used (which I what I do, especially for simple cases). Also, tables have columns, not fields.
Anyway, CASE:
SQL> with test (field) as
2 -- sample data; you already have that and don't type it
3 (select 'A' from dual union all
4 select 'Q' from dual union all
5 select 'B' from dual
6 )
7 -- query you need
8 select field,
9 case when field = 'A' then 'Answer'
10 when field = 'Q' then 'Query'
11 else 'Unknown'
12 end as result
13 from test;
F RESULT
- -------
A Answer
Q Query
B Unknown
SQL>
I have a SQL query built that returns a singular column of figures. Sample is shown below. I need to enhance this in a singular query and retrieve a 2nd set of figures using similar filters/parameters (instead of status=E I'd look for category=E). This query has gotten fairly complex for my knowledge though and I'm a bit stumped on how to approach adding this additional SELECT statement to get a 2nd column. Appreciate if anyone can point me in the right direction.
SELECT Count(eqnbr) EQUIP,
imp_exp,
'TITLE' TITLE
FROM (SELECT cy.container EQNbr,
cy.location_position "POS",
Substr(cy.location_position, 1, 1) Row_Nbr,
CASE
WHEN Substr(cy.location_position, 1, 3) IN (
'M01', 'N03', 'N04', 'N05',
'N06', 'N07', 'N08', 'N09' ) THEN
'Rail'
WHEN Substr(cy.location_position, 1, 1) IN ( 'F', 'G', 'H', 'J',
'K', 'L', 'M', 'P',
'R', 'S' ) THEN
'Locals'
WHEN Substr(cy.location_position, 1, 1) IN ( 'B', 'D' ) THEN
'Export'
ELSE Concat ('Row ', Substr(cy.location_position, 1, 1))
END IMP_EXP
FROM current_yard cy
WHERE eq_class = 'CTR'
AND location_position NOT LIKE 'CT%'
AND using_line NOT IN ( 'TEST', 'UNK', 'TST', 'B125',
'BO77', 'BR75' )
AND using_line IS NOT NULL
AND ( container NOT LIKE 'TEST%'
OR container NOT LIKE 'KENT%' )
AND Length(container) >= 10
AND status = 'E'
AND Substr(cy.location_position, 1, 1) NOT IN
( '1', '2', '3', '4',
'5', '6', '7', '8',
'9', '0', 'A', 'C',
'E', 'U', 'V', 'W', 'X' )) a
GROUP BY imp_exp
ORDER BY 2 ASC
Here is the problem together with the kind of result I am expecting.
I already have a routine that does this, I would like to research other solutions though so need any popular names for either a solutions or the problem itself.
If name1 and name2 are aliases or equivalent and name3 and name2 are aliases then
name1, name2, and name3 are all aliases of each other.
>>> aliases = [('name1', 'name2'), ('name3', 'name2')]
>>> consolidate_aliases(aliases)
[('name2', 'name3', 'name1')]
>>> aliases = [('A', 'X'), ('B', 'Y'), ('C', 'Z'), ('S', 'L'), ('T', 'M'), ('U', 'N'), ('Y', 'T'), ('B', 'L')]
>>> consolidate_aliases(aliases)
[('S', 'B', 'T', 'Y', 'M', 'L'), ('U', 'N'), ('C', 'Z'), ('A', 'X')]
>>>
It's called transitive closure.
It looks like Union-Find algorithm
It is a variant of the "Connected components of a graph" problem. Google for it to see answers like this and this.