How can we enter many values in a SQL parameter - SQL Developer? - sql

In SQL Developer, we can use parameters in order to test our query with different values - for example:
I have a table called Fruits (code, name). I want to retrieve code's apples.
SELECT *
FROM fruits
WHERE name IN (:PM_NAME)
It works correctly when I fill out one value (in this case :PM_NAME equal apple)
But when I want to fill out many values it doesn't work! I've tried these forms and these separators but still..
apple;orange
'apple';'orange'
('apple','orange')
['apple','orange']
"apple","orange"
In a nutshell what's the correct format to fill out multiple values in a SQL parameter in SQL Developer ?

I can't take credit for this 'trick' but the solution lies in a regular expression.
I want to search on multiple types of objects, fed to a :bind, and used in a WHERE clause.
SELECT owner,
object_name,
object_type
FROM all_objects
WHERE object_name LIKE :SEARCH
AND owner NOT IN (
'SYS',
'MDSYS',
'DBSNMP',
'SYSTEM',
'DVSYS',
'APEX_050100',
'PUBLIC',
'ORDS_METADATA',
'APEX_LISTENER'
)
AND object_type IN (
SELECT regexp_substr(:bind_ename_comma_sep_list,'[^,]+',1,level)
FROM dual CONNECT BY
regexp_substr(:bind_ename_comma_sep_list,'[^,]+',1,level) IS NOT NULL
)
ORDER BY owner,
object_name,
object_type;
I first learned of this 'trick' or technique from here.
So your query would look like this
SELECT *
FROM fruits
WHERE name IN (
SELECT regexp_substr(:PM_NAME,'[^,]+',1,level)
FROM dual CONNECT BY
regexp_substr(:PM_NAME,'[^,]+',1,level) IS NOT NULL
)
When you're prompted for values by SQL Developer, don't quote the strings, just comma separate them. Also, no spaces.
So in the input box, enter
apple,orange
And I suppose if you want ; vs , then update the regex call as needed.

Related

How to run a single query over all the tables in a user schema in ORACLE

Let say I have tables A,B,C,......,Z and I want to count all the entries in the tables and combine the results like so:
SELECT 'A' AS A, count(*) from USER.A UNION
.
.
.
SELECT 'J' AS J, count(*) from USER.J UNION
.
.
SELECT 'Z' AS 'Z' COUNT(*) from USER.Z
I want to avoid all above hassle. How can I do this in a smart way?.
I tend to do this by writing a query using the user_tables Meta information View and first time selecting text that is actually an sql query:
SELECT 'select '''||TABLE_NAME||''', count(*) from '||TABLE_NAME||'union all' FROM USER_TABLES
Running that in your query tool will produce a grid of rows that are actually sql queries in their own right. Copying them out of the results grid, pasting them into the query window (remove the trailing UNION ALL) and running them again produces data for each table
select 'a', count(*) from a union all
select 'b', count(*) from b union all ....
To get more involved and include column names, there's a USER_TAB_COLUMNS view that cites info about columns (e.g. You could write a query that generates queries that search any varchar columns for a particular value)
If you get really involved it can be cleaner to use placeholders in the string and REPLACE them:
SELECT REPLACE(REPLACE(REPLACE(
'select ''{o}.{t}'' as tname, ''{c}'' as cname
from {o}.{t} where
{c} like ''hello%'' or
{c} like ''goodbye%'' union all'
--to add more placeholders copy paste these lines
--and ensure there is the same number of REPLACE
--as there are numbers of lines
, '{t}', TABLE_NAME)
, '{c}', COLUMN_NAME)
, '{o}', OWNER)
FROM ALL_TAB_COLUMNS
WHERE DATA_TYPE = 'VARCHAR'
This is massively cleaner than starting and stopping your string all the time with || concatenation. I've deliberately uppercase the outer query and lowercase the inner query so you can tell which part is which
To add more placeholders, put additional REPLACE( at the top and copy paste in place the lines underneath that begin with a comma. Example copy , '{t}', table_name) paste and change to , '{d}', data_type) add another REPLACE( at the top and now you have a {d} placeholder you can use anywhere in the first string (the sql query pattern) to signify the data type

Oracle - CSV splitting only one column in a query

Oracle 12cR1 - I have a complex business process I am putting into a query.
In general, the process will be
with t1 as (select CATEGORY, PRODUCT from ... )
select <some manipulation> from t1;
t1 -aka the output of the first line- will look like this:
CATEGORY PRODUCT
Database Oracle, MS SQL Server, DB2
Language C, Java, Python
I need the 2nd line of the SQL query (aka the manipulation) to keep the CATEGORY column, and to split the PRODUCT column on the comma. The output needs to look like this
CATEGORY PRODUCT
Database Oracle
Database MS SQL Server
Database DB2
Language C
Language Java
Language Python
I have looked at a couple of different CSV splitting options. I cannot use the DBMS_Utility.comma_to_Table function as this has restrictions with special characters or starting with numbers. I found a nice TABLE function which will convert a string to separate rows, called f_convert. This function is on StackOverflow about 1/3 the way down the page here.
Since this is a table function, it is called like so...And will give me 3 rows, as expected.
SELECT * FROM TABLE(f_convert('Oracle, MS SQL Server, DB2'));
How do I treat this TABLE function as it is was a "column function"? Although this is totally improper SQL, I am looking for something like
with t1 as (select CATEGORY, PRODUCT from ... )
select CATEGORY from T1, TABLE(f_convert(PRODUCT) as PRODUCT from t1;
Any help appreciated...
Use connect by to "loop" through the elements of the list where a comma-space is the delimiter. regexp_substr gets the list elements (the regex allows for NULL list elements) and the prior clauses keep the categories straight.
with t1(category, product) as (
select 'Database', 'Oracle, MS SQL Server, DB2' from dual union all
select 'Language', 'C, Java, Python' from dual
)
select category,
regexp_substr(product, '(.*?)(, |$)', 1, level, NULL, 1) product
from t1
connect by level <= regexp_count(product, ', ')+1
and prior category = category
and prior sys_guid() is not null;
CATEGORY PRODUCT
-------- --------------------------
Database Oracle
Database MS SQL Server
Database DB2
Language C
Language Java
Language Python
6 rows selected.
SQL>

Finding lines where a table is inserted and updated with dba_source

I want to find all insert, update , and merge lines in whole dba
what i tried:
SELECT owner, name, type,line,text
FROM dba_source where regexp_like(text,'into my_table')
union all
SELECT owner, name, type,line,text
FROM dba_source where regexp_like(text,'update my_table')
but nothing returns from my select function.
EDIT: The return must contain specific table. Return should not contain tables like "my_table_2" or "my_table_temp"
You are not using a very good method to search. You will not get any results, if the source is saved in the below way -
insert
into
any_table
Because there are multiple new lines, so your query from dba_source/all_source will fail.
A better way to find that is -
select * from dba_dependencies
where referenced_name = 'TABLE_NAME|SYNONYM_NAME'
and referenced_type = 'TABLE|SYNONYM'
Example,
suppose you have a PROCEDURE XYZ, which has the below construct
create or replace procedure xyz
begin
insert
into
any_table ....
end;
Then, if you run the below query
select * from dba_dependencies
where referenced_name = 'any_table'
and referenced_type = 'TABLE';
The result of the above query will give you 'XYZ' as a record.
As I mentioned in comments, one issue could be the case of the text. Another could be the number of white-space characters between the characters.
SELECT owner,
name,
type,
line,
text
FROM dba_source
WHERE REGEXP_LIKE(
text,
'(into|update)\s+table_name(\s|$)',
-- Match both conditions and any amount of white-space
'i' -- Case insensitive
)
You then need to deal with other case where:
The table identifier is on the next line from the INTO or UPDATE keyword;
The table identifier has a schema prefix SCHEMA_NAME.TABLE_NAME; or
The an identifier is quoted "TABLE_NAME" or "SCHEMA_NAME"."TABLE_NAME";
A combination of the above.
Which would give you an expression like:
SELECT owner,
name,
type,
line,
text
FROM dba_source
WHERE REGEXP_LIKE(
text,
'(into|update)\s+(("?)schema_name\3\.)?("?)table_name\4(\s|$)',
-- Match both conditions, schema, quotes and any amount of white-space
'i' -- Case insensitive
)
You could also use LAG or LEAD to look for statements spread across lines.
There are several things to consider:
there may be space between the keyword INSERT and the table name
there may be a schema name preceding the table name (my_schema.my_table)
the table name may be in upper or lower case or a mix of them
excluding my_table_xxx for a search on my_table, as you already mentioned
Here is a query for this:
SELECT owner, name, type, line, text
FROM dba_source
WHERE regexp_like(text, '(update|into)[[:space:]]+my_table($|\s|\W)', 'i')
OR regexp_like(text, '(update|into)[[:space:]]+[[:alpha:]]+\.my_table($|\s|\W)', 'i');
However, this still doesn't cover the case where you have INSERT in one line and the table name in the next.
Try this:-
SELECT owner, name, type,line,text
FROM dba_source where regexp_like(UPPER(text),'INTO YOUR_TABLE_NAME')
union all
SELECT owner, name, type,line,text
FROM dba_source where regexp_like(UPPER(text),'UPDATE YOUR_TABLE_NAME');

Use sql to select data, then insert row in first position of result set only (not update database)

If I select a column in a table, say 10 rows, can I use SQL to insert a 'please select' row into the first position using SQL only? I want to insert it in the result set only - not the database.
First you should know this is a bad idea.
You are confusing your presentation layer and your database layer. Forcing SQL to do things like output status messages or feedback to users is an antipattern to be avoided.
That being said, if the column is of a string type (char, varchar, etc), you can do something like:
SELECT 'Please Select'
UNION ALL
SELECT TOP 10 Varcharfield
FROM Mytable
If it's numeric then no unless you cast it to a string type.
Here's something you can try:
SELECT u.YourVarcharColumnName
FROM (
SELECT 1 AS rID, 'Please select' AS YourVarcharColumnName
UNION
SELECT 2 AS rID, YourVarcharColumnName
FROM YourTableName
) u
ORDER BY u.rID;
I placed rID in place and sorted by it as an extreme-case-measure when your intended first rowset does not come out on top...
However, you should keep in mind that YourVarcharColumnName should be (as it says) a string. You'll have to convert it to a string if it's a non-string column.
As #JNK mentioned it.. I thought I should edit my post as well:
Please first try:
SELECT 'Please select' AS YourVarcharColumnName
UNION
SELECT YourVarcharColumnName
FROM YourTableName
Which is similar to what the others have posted. If you ever experience what I've been unfortunate to encounter and 'Please select' doesn't come out on top, please refer to the query I posted at the top.. Thanks!
SELECT '(Please select)'
UNION
SELECT ColumnName FROM TableName

Inline table in Oracle SQL

I'm trying to integrate with some software (that I can't modify) that queries a database that I can modify.
I can give this software SQL queries, like so "select username, firstname, lastname from users where username in ?"
The software than fills in the ? with something like ('alice', 'bob'), and gets user information for them.
Thing is, there's another piece of software, which I again can't modify, which occasionally generates users like 'user2343290' and feeds them through to the first piece of software. Of course, it throws errors because it can't find that user.
So the query I want to run is something like this:
select username, firstname, lastname from users where username in ?
UNION ALL
select t.column1, 'Unknown', 'Unknown' from create_table(?) t
where create_table generates a table with the rows mentioned in ?, with the first column named column1.
Or alternatively:
select username, firstname, lastname from users where username in ?
UNION ALL
select t.column1, 'Unknown', 'Unknown' from _universe_ t where t.column1 in ?
where _universe_ is some fake table that contains possible every value in column1 (i.e. infinitely large).
I've tried select ? from dual, but unfortunately this only worked when ? was something like ('x'), not ('x', 'y').
Keep in mind I can't change the format of how the ? comes out, so I can't do select 'alice' from dual union all select 'bob' from dual.
Anyone know how I could do what I've mentioned, or something else to have a similar effect?
You can turn the delimited string of names into a table type like so:
CREATE TYPE name_tab AS TABLE OF VARCHAR2(30);
/
SELECT * FROM table(name_tab('alice','bob'));
So you would just need to create the type then your example would become:
select username, firstname, lastname from users where username in ?
UNION ALL
select t.column1, 'Unknown', 'Unknown' from table(name_tab ?) t
(I'm assuming that the ? is replaced by simple text substitution -- because the IN wouldn't work if it was done as a bind variable -- and that the substituted text includes the parentheses.)
However, I am not sure the result of this will be helpful, since when a list of good usernames is given, you'll now have two result rows for each username, one with the actual information and another with the 'Unknown' values.
A better way to phrase the query might be:
select t.column_value username,
NVL(users.firstname,'Unknown'),
NVL(users.lastname,'Unknown')
from table(name_tab ?) t left join users on users.username = t.column_value
That should give you one row per username, with the actual data if it exists, or the 'Unknown' values if it does not.
You could use a pipelined function:
create type empname_t is table of varchar2(100);
create or replace function to_list(p_Names in string) return empname_t pipelined is
begin
pipe row(p_Names);
return;
end;
select * from table(to_list('bob'))
If you need to split the names (e.g. 'bob,alice'), you could use a function accepting a string and returning a empname_t, e.g. Tom Kyte's in_list, see
http://asktom.oracle.com/pls/apex/f?p=100:11:0::::P11_QUESTION_ID:210612357425
and modify the to_list function to iterate over the collection and pipe each item from the collection.