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.
Related
This question already has answers here:
PL/SQL - Use "List" Variable in Where In Clause
(3 answers)
How to load a large number of strings to match with oracle database?
(3 answers)
Closed 10 months ago.
There's a way to store a set of codes into a variable in Oracle SQL?
I have these codes and I'll need to use them in different parts of my query.
But I wouldn't repeat this list in many places in my SQL code.
'G31', 'G310', 'G311', 'G312', 'G318', 'G319', 'G239', 'G122', 'G710',
'B20', 'B22', 'B23', 'B24', 'G35', 'C811', 'G37', 'G375', 'K702', 'K741'
I would like to do something like this idea:
LIST <- ['G31', 'G310', 'G311', 'G312', 'G318', 'G319', 'G239', 'G122', 'G710',
'B20', 'B22', 'B23', 'B24', 'G35', 'C811', 'G37', 'G375', 'K702', 'K741']
SELECT * FROM TABLE_A where COLUMN IN [LIST];
SELECT * FROM TABLE_B where COLUMN IN [LIST];
A fancy approach is this
WITH CODE_VALUES AS
( SELECT DISTINCT COLUMN_VALUE AS CODE_VALUE
FROM TABLE (sys.dbms_debug_vc2coll ('G31',
'G310',
'G311',
'G312',
'G318',
'G319',
'G239',
'G122',
'G710',
'B20',
'B22',
'B23',
'B24',
'G35',
'C811',
'G37',
'G375',
'K702',
'K741'))
)
SELECT *
FROM CODE_VALUES -- + the rest of your query
You could do the same thing with successive union's against "dual" too
WITH CODE_VALUES AS
( SELECT 'ABC' AS code_value FROM dual UNION
SELECT 'CDE' AS code_value FROM dual
)
If this is going to get used across multiple operational queries it's probably best just to store them in a table.
Create a global temporary table once and add the desired values in the gtt and then use it in query using join.
Benifit of gtt is that you don't have to worry about data maintance. (Delete - insert). Data added in one session/transaction will be visible in that session/transaction only (based on type of gtt that you have created.
Create global temporary table gtt
(Col1 varchar2(10))
On commit preserve row; -- session specific
Insert into gtt
Select 'G31' from dual union all
Select 'G310' from dual union all
...
...
Select 'K741' from dual;
Now, you can use it anywhere in the same session as follows:
SELECT *
FROM TABLE_A a
Join gtt g on a.COLUMN = g.col1;
SELECT *
FROM TABLE_B b
Join gtt g on b.COLUMN = g.col1;
I have a Redshift table with the following column
How can I extract the value starting by cat_ from this column please (there is only one for each row and at different position in the array)?
I want to get those results:
cat_incident
cat_feature_missing
cat_duplicated_request
Thanks!
There is no easy way to extract multiple values from within one column in SQL (or at least not in the SQL used by Redshift).
You could write a User-Defined Function (UDF) that returns a string containing those values, separated by newlines. Whether this is acceptable depends on what you wish to do with the output (eg JOIN against it).
Another option is to pre-process the data before it is loaded into Redshift, to put this information in a separate one-to-many table, with each value in its own row. It would then be trivial to return this information.
You can do this using tally table (table with numbers). Check this link on information how to create this table: http://www.sqlservercentral.com/articles/T-SQL/62867/
Here is example how you would use it. In real life you should replace temporary #tally table with a permanent one.
--create sample table with data
create table #a (tags varchar(500));
insert into #a
select 'blah,cat_incident,mcr_close_ticket'
union
select 'blah-blah,cat_feature_missing,cat_duplicated_request';
--create tally table
create table #tally(n int);
insert into #tally
select 1
union select 2
union select 3
union select 4
union select 5
;
--get tags
select * from
(
select TRIM(SPLIT_PART(a.tags, ',', t.n)) AS single_tag
from #tally t
inner join #a a ON t.n <= REGEXP_COUNT(a.tags, ',') + 1 and n<1000
)
where single_tag like 'cat%'
;
Thanks!
In the end I managed to do it with the following query:
SELECT SUBSTRING(SUBSTRING(tags, charindex('cat_', tags), len(tags)), 0, charindex(',', SUBSTRING(tags, charindex('cat_', tags), len(tags)))) tags
FROM table
I have a data as following:
1203
1222
3201
4300
Which are numbers of products, so i need to select them as a data in table ..
I think the solution will be with using dual table ..
I tried the following code:
Select * from { values '1203','1222','3201','4300'} as Table1
But it not working !
Oracle has the TABLE function, which takes as input a varray and returns an anonymous table (anonymous means "the table doesn't have a name"), with a single column named column_value.
Oracle also has the function sys.odcinumberlist(), which takes as input a comma-separated list of numbers and returns a varray of numbers. (Similarly, to generate a varray of varchar2 values, there is sys.odcivarchar2list().)
To input your for values and return a result with a single column named num (or any other name you want), you can use these two functions together - and alias the column in the select clause. Like this:
select column_value as num
from table( sys.odcinumberlist( 1203, 1222, 3201, 4300 ) )
;
NUM
----
1203
1222
3201
4300
In standard SQL you would need to use:
Select *
from (values ('1203','1222','3201','4300')) as Table1;
That would create a single row with four (character) columns. If you intended to return four rows it would be:
Select *
from (values ('1203'),('1222'),('3201'),('4300')) as Table1
However, Oracle does NOT support that syntax. Neither does it support a SELECT without a FROM clause.
If you want a single row with 4 columns in Oracle you need
select '1203','1222','3201','4300'
from dual;
If you want four rows you need
select '1203'
from dual
union all
select '1222'
from dual
union all
select '3201'
from dual
union all
select '4300'
from dual;
Additionally: '1203' is not a "number". It's a character string. Number literals are written without quotes in SQL: 1203 is a number
You can also use XMLTABLE.
select TO_NUMBER(TRIM(COLUMN_VALUE)) as numbers from XMLTABLE( '1203,1222,3201,4300');
Numbers
-------
1203
1222
3201
4300
This works only for numbers.
EDIT: Better to wrap it with to_number as suggested by experts in the comments.
While inserting multiple rows into a table using the following style :
insert all
into ghazal_current (GhazalName,Rating) values('Ajab Apna Haal Hota Jo Visaal-e-Yaar Hota',5)
into ghazal_current (GhazalName,Rating) values('Apne Hothon Par Sajana Chahta Hun',4)
into ghazal_current (GhazalName,Rating) values('Shaam Se Aankh Mein Nami Si Hai',4)
into ghazal_current (GhazalName,Rating) values('Tumhe Yaad Ho Ke Na Yaad Ho',3)
select 1 from dual;
What does the statement select 1 from dual mean ? What is it here for ?
DUAL is a built-in table, useful because it is guaranteed to return only one row. This means DUAL may be used to get pseudo-columns such as user or sysdate, the results of calculations and the like. The owner of DUAL is SYS but it can be accessed by every user. DUAL is well-covered in the documentation. Find out more.
In your case, SELECT 1 FROM DUAL; will simply returns 1. You need it because the INSERT ALL syntax demands a SELECT clause but you are not querying the input values from a table.
Brief re-introduction to one-row tables
Some SQL databases require all values to come FROM a table or table-like object, whereas others permit queries to construct values ex nihilo:
-- MySQL, sqlite, PostgreSQL, HSQLdb, and many others permit
-- a "naked" select:
SELECT 1;
-- Others *require* a FROM target, like Oracle.
SELECT 1 FROM DUAL;
-- ...and Firebird/Interbase:
SELECT 1 FROM RDB$DATABASE;
-- ...and DB2:
SELECT 1 FROM SYSIBM.SYSDUMMY1;
Here the cardinality of DUAL is important. If it had more than one row, your result set would have more than one row. What happens, for example, when you SELECT 1 FROM A_Table_With_Ten_Rows?
Why DUAL is used here
The SQL construct VALUES (<row-value-expression>) is a row value constructor. VALUES (1, 2, 3) "creates" a row of values just as SELECT 1, 2, 3 does.
Oracle, of course, requires that these values come FROM somewhere.
As a demonstration, instead of SELECTing from DUAL at the end of the INSERT ALL, try a table with N rows, and you'll see that each VALUES() row is inserted N times.
There are some samples about using dual in Queries:
select sysdate from dual /--it returns date of system
SELECT chr(223) FROM dual /--it returns character of AsciÙ‡i code
select my_sequence.nextval from dual; /-- It returns the next value of a sequence
select to_char(sysdate,'yyyy/mm/dd','nls_calendar=persian')from dual
/--returns persian date of system
Is there any SQL subquery syntax that lets you define, literally, a temporary table?
For example, something like
SELECT
MAX(count) AS max,
COUNT(*) AS count
FROM
(
(1 AS id, 7 AS count),
(2, 6),
(3, 13),
(4, 12),
(5, 9)
) AS mytable
INNER JOIN someothertable ON someothertable.id=mytable.id
This would save having to do two or three queries: creating temporary table, putting data in it, then using it in a join.
I am using MySQL but would be interested in other databases that could do something like that.
I suppose you could do a subquery with several SELECTs combined with UNIONs.
SELECT a, b, c, d
FROM (
SELECT 1 AS a, 2 AS b, 3 AS c, 4 AS d
UNION ALL
SELECT 5 , 6, 7, 8
) AS temp;
You can do it in PostgreSQL:
=> select * from (values (1,7), (2,6), (3,13), (4,12), (5,9) ) x(id, count);
id | count
----+-------
1 | 7
2 | 6
3 | 13
4 | 12
5 | 9
http://www.postgresql.org/docs/8.2/static/sql-values.html
In Microsoft T-SQL 2008 the format is:
SELECT a, b FROM (VALUES (1, 2), (3, 4), (5, 6), (7, 8), (9, 10) ) AS MyTable(a, b)
I.e. as Jonathan mentioned above, but without the 'table' keyword.
See:
FROM (T-SQL MSDN docs) (under derived-table), and
Table Value Constructor (T-SQL MSDN docs)
In standard SQL (SQL 2003 - see http://savage.net.au/SQL/) you can use:
INSERT INTO SomeTable(Id, Count) VALUES (1, 7), (2, 6), (3, 13), ...
With a bit more chasing, you can also use:
SELECT * FROM TABLE(VALUES (1,7), (2, 6), (3, 13), ...) AS SomeTable(Id, Count)
Whether these work in MySQL is a separate issue - but you can always ask to get it added, or add it yourself (that's the beauty of Open Source).
I found this link Temporary Tables With MySQL
CREATE TEMPORARY TABLE TempTable ( ID int, Name char(100) ) TYPE=HEAP;
INSERT INTO TempTable VALUES( 1, "Foo bar" );
SELECT * FROM TempTable;
DROP TABLE TempTable;
Since MariaDB v10.3.3 and MySQL v8.0.19 you can now do exactly that!
See docs: MariaDB, MySQL
MariaDB:
WITH literaltable (id,count) AS (VALUES (1,7),(2,6),(3,13),(4,12),(5,9))
SELECT MAX(count) AS max,COUNT(*) AS count FROM literaltable
I used a WITH here because MariaDB doesn't supply nice column names for VALUES .... You can use it in a union without column names:
SELECT 1 AS id,7 AS count UNION ALL VALUES (2,6),(3,13),(4,12),(5,9) ORDER BY count DESC
And although the docs don't appear to mention it, you can even use it as a top-level query:
VALUES (1,7),(2,6),(3,13),(4,12),(5,9) ORDER BY 2 DESC
The actual column names are in fact the just first row of values, so you can even do this (though it's inelegant, and you can run into duplicate column name errors):
SELECT MAX(`7`) AS max,COUNT(*) AS count FROM (VALUES (1,7),(2,6),(3,13),(4,12),(5,9)) literaltable
MySQL:
I don't have an instance of MySQL v8.0.19 to test against right now, but according to the docs either of these should work:
SELECT MAX(column_1) AS max,COUNT(*) AS count FROM (VALUES ROW(1,7), ROW(2,6), ROW(3,13), ROW(4,12), ROW(5,9)) literaltable
SELECT MAX(data) AS max,COUNT(*) AS count FROM (VALUES ROW(1,7), ROW(2,6), ROW(3,13), ROW(4,12), ROW(5,9)) literaltable(id,data)
Unlike MariaDB, MySQL supplies automatic column names column_0, column_1, column_2, etc., and also supports renaming all of a subquery's columns when referencing it.
I'm not sure, but this dev worklog page seems to suggest that MySQL has also implemented the shorter sytax (omitting "ROW", like MariaDB), or that they will in the near future.
In a word, yes. Even better IMO if your SQL product supports common table expressions (CTEs) i.e. easier on the eye than using a subquery plus the same CTE can be used multiple times e.g. this to 'create' a sequence table of unique integers between 0 and 999 in SQL Server 2005 and above:
WITH Digits (nbr) AS
(
SELECT 0 AS nbr UNION ALL SELECT 1 UNION ALL SELECT 2
UNION ALL SELECT 3 UNION ALL SELECT 4 UNION ALL SELECT 5
UNION ALL SELECT 6 UNION ALL SELECT 7 UNION ALL SELECT 8
UNION ALL SELECT 9
),
Sequence (seq) AS
(
SELECT Units.nbr + Tens.nbr + Hundreds.nbr
FROM Digits AS Units
CROSS JOIN Digits AS Tens
CROSS JOIN Digits AS Hundreds
)
SELECT S1.seq
FROM Sequence AS S1;
except you'd actually do something useful with the Sequence table e.g. parse the characters from a VARCHAR column in a base table.
HOWEVER, if you are using this table, which consists only of literal values, multiple time or in multiple queries then why not make it a base table in the first place? Every database I use has a Sequence table of integers (usually 100K rows) because it is so useful generally.
CREATE TEMPORARY TABLE ( ID int, Name char(100) ) SELECT ....
Read more at : http://dev.mysql.com/doc/refman/5.0/en/create-table.html
( near the bottom )
This has the advantage that if there is any problem populating the table ( data type mismatch ) the table is automatically dropped.
An early answer used a FROM SELECT clause. If possible use that because it saves the headache of cleaning up the table.
Disadvantage ( which may not matter ) with the FROM SELECT is how large is the data set created. A temporary table allows for indexing which may be critical. For the subsequent query. Seems counter-intuitive but even with a medium size data set ( ~1000 rows), it can be faster to have a index created for the query to operate on.