List of values as table - sql

I'm looking for a smarter way to have a list of values as a table in Oracle.
What I do nowadays is
select 'value1' as val from dual
union
select 'value2' from dual
What I'm hoping for is some function/way/magic, that I'll do for example
select 'value1', 'value2' from dual -- + some additional magic
I'm looking for non-PL/SQL way which I think is overkill, but I'm not saying definite no to PL/SQL if that's the only option, but I can look here Create an Oracle function that returns a table for inspiration for PL/SQL. But extra table to have a list seems still easier to maintain than PL/SQL.
The motivation for not using select distict from transactional table is that I want to have a defined list of values, and with that approach, I can miss those I expect there but have no records in the table.
The expected number of elements in the list is several tens of records (like 30).

Here is one option:
select column_value
from table(sys.dbms_debug_vc2coll('value1', 'value2', 'value3', 'value4'));
Starting Oracle 12.2, you don't need the table function:
select column_value
from sys.dbms_debug_vc2coll('value1', 'value2', 'value3', 'value4');

Or yet another, similar:
SQL> select column_value
2 from table(sys.odcivarchar2list('Little', 'Foot', 'Scott', 'Tiger'))
3 order by column_value;
COLUMN_VALUE
----------------------------------------------------------------------------
Foot
Little
Scott
Tiger
SQL>

Starting with Oracle 12c you could use JSON_TABLE for that:
select *
from json_table('["value1", "value2"]', '$[*]'
columns val varchar(20) path '$');

If you aren't on 12c and can't use json_table (or even if you are/can but don't want to) you could use an XML sequence via xmltable instead:
select *
from xmltable('"value1", "value2", "value3"');
Result Sequence
--------------------------------------------------------------------------------
value1
value2
value3
I'd probably use an ODCI collection by default, but might be interesting to compare how all of these options compare with large data volumes - with 30 values you might not be able to see much of a difference.

Also, You can use the connect by query:
SQL> select regexp_substr('VALUE1,VALUE2','[^,]+', 1, level) from dual
2 connect by level <= regexp_count('VALUE1,VALUE2', '[^,]+');
REGEXP_SUBSTR('VALUE1,VALUE2','[^,]+',1,LEVEL)
----------------------------------------------------
VALUE1
VALUE2
SQL>

Related

How to insert multipleRows in oracle using sequence

I need to insert multiple Rows in oracle database, I use this command line:
INSERT ALL
INTO PHMR_VIP (PHMR_VIP_ID,PHMR_VIP_NOM,PHMR_VIP_PRENOM) VALUES (SQ_PHMR_VIP.nextval, 'dfdf', 'dfdfd')
INTO PHMR_VIP (PHMR_VIP_ID,PHMR_VIP_NOM,PHMR_VIP_PRENOM) VALUES (SQ_PHMR_VIP.nextval, 'ffdf', 'dfdf')
INTO PHMR_VIP (PHMR_VIP_ID,PHMR_VIP_NOM,PHMR_VIP_PRENOM) VALUES (SQ_PHMR_VIP.nextval, 'mohfdfd','fdfdf')
SELECT * FROM dual;
But, I have oracle error:
ORA-00001: violation de contrainte unique (PHMR.PHMR_VIP_PK)
How can I resolve this please? I need to do a bulkInsert with column PHMR_VIP_ID as a sequence
You can use:
INSERT INTO PHMR_VIP (PHMR_VIP_ID,PHMR_VIP_NOM,PHMR_VIP_PRENOM)
SELECT SQ_PHMR_VIP.nextval, col1, col2
FROM (
SELECT 'dfdf' AS col1, 'dfdfd' AS col2 FROM dual
UNION ALL
SELECT 'ffdf', 'dfdf' FROM dual
UNION ALL
SELECT 'mohfdfd','fdfdf' FROM dual
);
For some, you have to use SELECT 1 FROM dual; instead of SELECT * FROM dual;. See this reference: https://community.oracle.com/thread/2457262. This is because there is only 1 row in dual.
That doesn't help in this instance though, because you can't use sequences with SELECT ALL, per this article: https://community.oracle.com/thread/1038439, at least not the way you have. You can if you have a trigger that catches each nextval call and updates the value manually, but as mentioned further in the postings, it can decrease performance.
The recommendation is to use multiple inserts rather than an insert all.
Hope this helps.
-C§

Oracle SQL - Selecting column without knowing column name

Is it possible to select the value of the second column using something like an index or column position if i dont know the name of the column?
Select col(2) FROM (
Select 'a', 'b',' c', 'd' from dual
)
Is it possible? Sure. You could write a PL/SQL block that used dbms_sql to open a cursor using the actual query against dual, describe the results, bind a variable to whatever you find the second column to be, fetch from the cursor, and then loop. That would be a terribly involved and generally rather painful process but it could be done.
The SQL language does not define a way to do this in a static SQL statement and Oracle does not provide an extension that would allow this. I'd be rather concerned about the underlying problem that you're trying to solve, though, if you somehow know that you want the second column but don't know what that column represents. That's not something that makes a lot of sense in a relational database.
SELECT ORDINAL_POSITION, COLUMN_NAME
FROM INFORMATION_SCHEMA.COLUMNS
WHERE TABLE_NAME = 'dual'
not sure if this is what you need.
If you don't know the name, just give it a nice name ;).
Select b FROM (
Select 'a', 'b' as b, 'c', 'd' from dual
)
It is possible in case when you know number of columns in the subquery:
SELECT COL2 FROM (
SELECT NULL COL1, NULL COL2, NULL COL3, NULL COL4
UNION ALL
select 'a', 'b', 'c', 'd' from dual
)
WHERE COL2 IS NOT NULL;
There are limitations:
query is hard to read and understand
you should know number of columns
value should not be NULL
not recommended for production use

Using LIKE in an Oracle IN clause

I know I can write a query that will return all rows that contain any number of values in a given column, like so:
Select * from tbl where my_col in (val1, val2, val3,... valn)
but if val1, for example, can appear anywhere in my_col, which has datatype varchar(300), I might instead write:
select * from tbl where my_col LIKE '%val1%'
Is there a way of combing these two techniques. I need to search for some 30 possible values that may appear anywhere in the free-form text of the column.
Combining these two statements in the following ways does not seem to work:
select * from tbl where my_col LIKE ('%val1%', '%val2%', 'val3%',....)
select * from tbl where my_col in ('%val1%', '%val2%', 'val3%',....)
What would be useful here would be a LIKE ANY predicate as is available in PostgreSQL
SELECT *
FROM tbl
WHERE my_col LIKE ANY (ARRAY['%val1%', '%val2%', '%val3%', ...])
Unfortunately, that syntax is not available in Oracle. You can expand the quantified comparison predicate using OR, however:
SELECT *
FROM tbl
WHERE my_col LIKE '%val1%' OR my_col LIKE '%val2%' OR my_col LIKE '%val3%', ...
Or alternatively, create a semi join using an EXISTS predicate and an auxiliary array data structure (see this question for details):
SELECT *
FROM tbl t
WHERE EXISTS (
SELECT 1
-- Alternatively, store those values in a temp table:
FROM TABLE (sys.ora_mining_varchar2_nt('%val1%', '%val2%', '%val3%'/*, ...*/))
WHERE t.my_col LIKE column_value
)
For true full-text search, you might want to look at Oracle Text: http://www.oracle.com/technetwork/database/enterprise-edition/index-098492.html
A REGEXP_LIKE will do a case-insensitive regexp search.
select * from Users where Regexp_Like (User_Name, 'karl|anders|leif','i')
This will be executed as a full table scan - just as the LIKE or solution, so the performance will be really bad if the table is not small. If it's not used often at all, it might be ok.
If you need some kind of performance, you will need Oracle Text (or some external indexer).
To get substring indexing with Oracle Text you will need a CONTEXT index. It's a bit involved as it's made for indexing large documents and text using a lot of smarts. If you have particular needs, such as substring searches in numbers and all words (including "the" "an" "a", spaces, etc) , you need to create custom lexers to remove some of the smart stuff...
If you insert a lot of data, Oracle Text will not make things faster, especially if you need the index to be updated within the transactions and not periodically.
No, you cannot do this. The values in the IN clause must be exact matches. You could modify the select thusly:
SELECT *
FROM tbl
WHERE my_col LIKE %val1%
OR my_col LIKE %val2%
OR my_col LIKE %val3%
...
If the val1, val2, val3... are similar enough, you might be able to use regular expressions in the REGEXP_LIKE operator.
Yes, you can use this query (Instead of 'Specialist' and 'Developer', type any strings you want separated by comma and change employees table with your table)
SELECT * FROM employees em
WHERE EXISTS (select 1 from table(sys.dbms_debug_vc2coll('Specialist', 'Developer')) mt
where em.job like ('%' || mt.column_value || '%'));
Why my query is better than the accepted answer: You don't need a CREATE TABLE permission to run it. This can be executed with just SELECT permissions.
In Oracle you can use regexp_like as follows:
select *
from table_name
where regexp_like (name, '^(value-1|value-2|value-3....)');
The caret (^) operator to indicate a beginning-of-line character &
The pipe (|) operator to indicate OR operation.
This one is pretty fast :
select * from listofvalue l
inner join tbl on tbl.mycol like '%' || l.value || '%'
Just to add on #Lukas Eder answer.
An improvement to avoid creating tables and inserting values
(we could use select from dual and unpivot to achieve the same result "on the fly"):
with all_likes as
(select * from
(select '%val1%' like_1, '%val2%' like_2, '%val3%' like_3, '%val4%' as like_4, '%val5%' as like_5 from dual)
unpivot (
united_columns for subquery_column in ("LIKE_1", "LIKE_2", "LIKE_3", "LIKE_4", "LIKE_5"))
)
select * from tbl
where exists (select 1 from all_likes where tbl.my_col like all_likes.united_columns)
I prefer this
WHERE CASE WHEN my_col LIKE '%val1%' THEN 1
WHEN my_col LIKE '%val2%' THEN 1
WHEN my_col LIKE '%val3%' THEN 1
ELSE 0
END = 1
I'm not saying it's optimal but it works and it's easily understood. Most of my queries are adhoc used once so performance is generally not an issue for me.
select * from tbl
where exists (select 1 from all_likes where all_likes.value = substr(tbl.my_col,0, length(tbl.my_col)))
You can put your values in ODCIVARCHAR2LIST and then join it as a regular table.
select tabl1.* FROM tabl1 LEFT JOIN
(select column_value txt from table(sys.ODCIVARCHAR2LIST
('%val1%','%val2%','%val3%')
)) Vals ON tabl1.column LIKE Vals.txt WHERE Vals.txt IS NOT NULL
You don't need a collection type as mentioned in https://stackoverflow.com/a/6074261/802058. Just use an subquery:
SELECT *
FROM tbl t
WHERE EXISTS (
SELECT 1
FROM (
SELECT 'val1%' AS val FROM dual
UNION ALL
SELECT 'val2%' AS val FROM dual
-- ...
-- or simply use an subquery here
)
WHERE t.my_col LIKE val
)

select a column where the column name is retrieved from a query

I'm looking for a elegant way to select a column from table A where as the column name is retrieved from a query on table B.
A query on table B results in 'col01'
The Table A has several columns named 'col01','col02','col03',...
Final query should be for result
result from B | effective SQL query
'col01' | SELECT col01 FROM A
'col02' | SELECT col02 FROM A
I'm looking for pure SQL solution with no scripting. It should run with Oracle and/or MySQL.
I'm looking for pure SQL solution with no scripting.
This is not possible. You have to create the statements dynamically.
In a very simple case you can just about do it with a CASE statement, but this is unlikely to be of much use in the real world:
with a as (
select 'col_01_val_01' as col_01, 'col_02_val_01' as col_02,
'col_03_val_01' as col_03 from dual
union select 'col_01_val_02' as col_01, 'col_02_val_02' as col_02,
'col_03_val_02' as col_03 from dual
union select 'col_01_val_03' as col_01, 'col_02_val_03' as col_02,
'col_03_val_03' as col_03 from dual),
b as (
select 1 parm, 'col_01' value from dual
union select 2 parm, 'col_02' value from dual
union select 3 parm, 'col_03' value from dual)
select b.value as result_from_b,
case b.value
when 'col_01' then col_01
when 'col_02' then col_02
when 'col_03' then col_03 end as result_from_a
from a, b
where b.parm = 1;
RESULT_FROM_B RESULT_FROM_A
------------- -------------
col_01 col_01_val_01
col_01 col_01_val_02
col_01 col_01_val_03
The columns have to be the same type (or at least expressible as such in the case), and b can only return one row... And it isn't very pretty of course. For anything more complicated you need dynamic SQL as #a_horse_with_no_name has already suggested.
This could be done within a stored routine in MySQL. I don't know about Oracle as I haven't used it. You can find out more about MySQL stored procedures here and here (the 2nd link is a pretty good PDF which walks you through some simple examples).

Oracle Select numbers from an IN clause

I'm looking for the best way to select numbers directly from an in clause.
Basically like:
SELECT * FROM (2,6,1,8);
That doesn't work. I can do it this way:
SELECT Lv FROM ( SELECT Level LV
FROM DUAL
CONNECT BY Level < 20)
WHERE Lv IN (2,6,1,8);
But that seems to be a bit clunky. Is there a more elegant way?
You can do
select column_value from table(sys.dbms_debug_vc2coll(1,2,3,4,5));
but that actually returns a varchar2. You can create your own TYPE and use that
create type tab_num is table of number;
/
select column_value from table(tab_num(1,2,3,4,5));
It's also worth looking at the MODEL clause. It looks complicated, but it is very good at generating data
SELECT x from dual
MODEL DIMENSION BY (1 AS z) MEASURES (1 x)
RULES ITERATE (7) (x[ITERATION_NUMBER]=ITERATION_NUMBER+1)
It's more elegant if you materialize an auxiliary numbers table:
SELECT num FROM numbers WHERE num IN (2,6,1,8);
And this is also useful when combined with another table.
For instance, I've had a case where I needed to populate large configuration tables with changes from piecewise results:
Big SP or Excel sheet or report identifies missing cost centers in config gives a large set of results which need to be inserted with varying data in some groups.
Paste partial results into a individual comma separated lists:
INSERT INTO {stuff}
SELECT {stuff}, 130 as line_item
FROM numbers
WHERE numbers.num IN ({pasted a section of results})
INSERT INTO {stuff}
SELECT {stuff}, 135 as line_item
FROM numbers
WHERE numbers.num IN ({pasted another section of results})
If you don't explicitly need the IN clause, you could use UNION:
select 2 from dual
union
select 6 from dual
union
select 1 from dual
union
select 8 from dual
There is a more elegant variant to INSERT multiple rows into a table:
INSERT ALL
INTO table (col) VALUES ('a')
INTO table (col) VALUES ('b')
INTO table (col) VALUES ('c')
SELECT * FROM dual;
But I don't know a way to do that for a SELECT.