In my project, I have a database in SQL which was working fine. But now I have to make the application support oracle db too.
Some limitations I found out was that in Oracle, there is no bit field and the table name cannot be greater than 30 char. Is there any other limitation that I need to keep in mind.
Any suggestion from past experience will be helpful.
If I recall correctly from my earlier Oracle days:
there's no IDENTITY column specification in Oracle (you need to use sequences instead)
you cannot simply return a SELECT (columns) from a stored procedure (you need to use REF CURSOR)
of course, all stored procs/funcs are different (Oracle's PL/SQL is not the same as T-SQL)
The SQL ISNULL counterpart in Oracle is NVL
select ISNULL(col, 0)...
select NVL(col, 0)...
You will also struggle if you attempt to select without a from in Oracle. Use dual:
select 'Hello' from DUAL
Bear in mind also, that in Oracle there is the distinction between PL/SQL (Procedural SQL) and pure SQL. They are two distinct and separate languages, that are commonly combined.
Varchar in Oracle Databases called
varchar2 is limited to 4000
characters
Oracles concept of temporary tables is different, they have a global redefined structure
by default sort order and string compare is case-sensitive
When you add a column to a select *
Select * from table_1 order by id;
you must prefix the * by the table_name or an alias
Select
(row_number() over (order by id)) rn,
t.*
from table_1 t
order by id;
Oracle doesn't distinguish between null and '' (empty string). For insert and update you ca use '', but to query you must use null
create table t1 (
id NUMBER(10),
val varchar2(20)
);
Insert into t1 values (1, '');
Insert into t1 values (2, null);
Select * from t1 where stringval = 0; -- correct but empty
Select * from t1 where stringval is null; -- returns both rows
ORACLE do not support TOP clause. Instead of TOP you can use ROWNUM.
SQL Server: TOP (Transact-SQL)
SELECT TOP 3 * FROM CUSTOMERS
ORACLE: ROWNUM Pseudocolumn
SELECT * FROM CUSTOMERS WHERE ROWNUM <= 3
Related
Consider a SQLite database for things with parts, containing the following tables
CREATE TABLE thing (id integer PRIMARY KEY, name text, total_cost real);
CREATE TABLE part (id integer PRIMARY KEY, cost real);
CREATE TABLE thing_part (thing_id REFERENCES thing(id), part_id REFERENCES part(id));
I have an index to find the parts of a thing
CREATE INDEX thing_part_idx ON thing_part (thing_id);
To illustrate the problem, I'm using the following queries to fill the tables with random data
INSERT INTO thing(name)
WITH RECURSIVE
cte(x) AS (
SELECT 1
UNION ALL
SELECT 1 FROM cte LIMIT 10000
)
SELECT hex(randomblob(4)) FROM cte;
INSERT INTO part(cost)
WITH RECURSIVE
cte(x) AS (
SELECT 1
UNION ALL
SELECT 1 FROM cte LIMIT 10000
)
SELECT abs(random()) % 100 FROM cte;
INSERT INTO thing_part (thing_id, part_id)
SELECT thing.id, abs(random()) % 10000 FROM thing, (SELECT 1 UNION ALL SELECT 1), (SELECT 1 UNION ALL SELECT 1);
So each thing is associated with a small number of parts (4 in this example).
At this point, I have not yet set the total cost of the things. I thought I could use the following query
UPDATE thing SET total_cost = (
SELECT sum(part.cost)
FROM thing_part, part
WHERE thing_part.thing_id = thing.id
AND thing_part.part_id = part.id);
but it is extremely slow (I did not have the patience to wait for it to complete).
EXPLAIN QUERY PLAN shows that both thing and thing_part are being scanned over, only the lookup in part is done using the rowid:
SCAN TABLE thing
EXECUTE CORRELATED SCALAR SUBQUERY 0
SCAN TABLE thing_part
SEARCH TABLE part USING INTEGER PRIMARY KEY (rowid=?)
If I look at the query plan for the inner query with a fixed thing_id, i.e.
SELECT sum(part.cost)
FROM thing_part, part
WHERE thing_part.thing_id = 1000
AND thing_part.part_id = part.id;
it does use the thing_part_idx:
SEARCH TABLE thing_part USING INDEX thing_part_idx (thing_id=?)
SEARCH TABLE part USING INTEGER PRIMARY KEY (rowid=?)
I would expect the first query to be equivalent to iterating over all rows of thing and executing the inner query each time, but obviously that's not the case. Why? Should I use a different index or rewrite my query or maybe do the iteration in the client to generate multiple queries instead?
In case it matters, I'm using SQLite version 3.22.0
SQLite might use dynamic typing, but column types still matter for affinity, and indexes can be used only when the database can prove that index lookups behave the same as comparisons with the actual table values, which often requires the affinities to be compatible.
So when you tell the database that the thing_part values are integers:
CREATE TABLE thing_part (
thing_id integer REFERENCES thing(id),
part_id integer REFERENCES part(id)
);
then the index on that will have the correct affinity, and will be used:
QUERY PLAN
|--SCAN TABLE thing
`--CORRELATED SCALAR SUBQUERY
|--SEARCH TABLE thing_part USING INDEX thing_part_idx (thing_id=?)
`--SEARCH TABLE part USING INTEGER PRIMARY KEY (rowid=?)
I would rewrite your query as:
-- calculating sum for each thing_id at once
WITH cte AS (
SELECT thing_part.thing_id, sum(part.cost) AS s
FROM thing_part
JOIN part
ON thing_part.part_id = part.id
GROUP BY thing_part.thing_id
)
UPDATE thing
SET total_cost = (SELECT s FROM cte WHERE thing.id = cte.thing_id);
Trying to generalize the SQL what splits a string/varchar into records. Here is the working SQL:
SELECT test.* FROM test JOIN (
SELECT level nbr, REGEXP_SUBSTR('1,3', '(.*?)(,|$)', 1, level, NULL, 1) value
FROM dual CONNECT BY level <= REGEXP_COUNT('1,3', ',')+1 ORDER BY level
) requested ON test.id=requested.value
What I mean by generalizing is; moving the recurring SQL (in this case the bit between the parenthesis's from the working SQL above) to a procedure/function so it can be reused. In this case I'm trying to find a way to insert a generated inner select statement. This is how the generalized SQL may look like:
SELECT t.* FROM table t JOIN (<GENERATED_INNER_SELECT>) my ON t.x=my.x;
However I didn't succeed yet, I tried tho but calling my function to generate the inner select statement directly resulted in:
ORA-00900: invalid SQL statement
And using the function in the generalized SQL resulted in:
ORA-00907: missing right parenthesis
None of these errors make any sense to me in this context.
Perhaps you can help? check out the full case on dbfiddle.
If you generate a SQL fragment to use as a subquery then the overall statement that embeds that as a subquery would have to be executed dynamically too.
It would be simpler to have the function actually doing the split itself, and returning a collection - as a schema-level collection type:
CREATE TYPE T_NUMBERS AS TABLE OF NUMBER
/
CREATE OR REPLACE FUNCTION split(p_string VARCHAR2, p_seperator VARCHAR2 DEFAULT ',')
RETURN T_NUMBERS AS
L_NUMBERS T_NUMBERS;
BEGIN
SELECT REGEXP_SUBSTR(p_string, '(.*?)(,|$)', 1, level, NULL, 1)
BULK COLLECT INTO L_NUMBERS
FROM dual
CONNECT BY level <= REGEXP_COUNT(p_string, ',')+1;
RETURN L_NUMBERS;
END split;
/
SELECT * FROM TEST
WHERE id MEMBER OF (split('1,3'))
/
ID NAM
---------- ---
1 foo
3 foe
or if you prefer the table collection expression approach:
SELECT t.*
FROM TABLE(split('1,3')) tmp
JOIN test t ON t.id = tmp.column_value;
It would be even simpler if the query could be called with a collection of numbers in the first place, but without seeing how the call is being made - and the string generated - it's hard to say exactly how you'd need to change that. You could even use a built-in collection type then, instead of having to define your own:
SELECT t.*
FROM TABLE(SYS.ODCINUMBERLIST(1,3)) tmp
JOIN test t ON t.id = tmp.column_value;
but it relies on the caller being able to pass the numbers in rather than a string (note the lack of single quotes...)
I know this isn't a specific bit of code or problem, but I am having trouble with a very similar issue to the person asking this (except theirs is for SQL Server): Combining INSERT INTO and WITH/CTE ...and I can't seem to find it out there on any SAP HANA help forums etc. so thought there may be an expert on here who can just give me a simple yes or no answer.
The SQL statement I am using contains multiple CTEs, but when I try to insert it tells me there is a Syntax error around the word INSERT. It is definitely laid out exactly the same as in the question I've linked above (spent hours checking), and I can post code samples if necessary but I simply want to know whether it is supported first! Thanks
Short answer:
No, CTEs are not supported for INSERT/UPDATE statements.
Longer answer:
SQLScript's INSERT/UPDATE commands are actually "borrowed" SQL commands as the documentation explains.
Checking the documentation for SQL INSERT we find that it supports a subquery as a source of values.
The subquery term is defined as part of the SQL SELECT statement. Checking the documentation for SELECT shows that <subquery> and <with_clause> are different, non-overlapping terms.
This means, that CTEs cannot be used in subqueries and therefore not be part of the subqueries used in INSERT/UPDATE commands.
You can, however, use SQLScript table variables in INSERT statements in your SQLScript blocks, which is very similar to CTEs:
DO BEGIN
te_a := SELECT 10, 'xyz' as VAL from dummy;
te_b := SELECT 20, 'abc' as VAL from dummy;
te_all := SELECT * from :te_a
UNION ALL SELECT * from :te_b;
INSERT INTO VALS
(SELECT * from :te_all);
END;
You can convert the CTE into a Sub-Select statement in many cases
You can use following
insert into city (city, countryid, citycode)
select
city, countryid, citycode
from (
-- CTE Expression as subselect
select * from city
-- end (CTE)
) cte
Instead of using following valid CTE command combined with INSERT (on SQL Server)
with cte as (
select * from city
)
insert into city (city, countryid, citycode)
select
city, countryid, citycode
from cte
SAP HANA includes this posibility, the order of the code is different than SQL Server:
INSERT INTO EXAMPLE (ID)
WITH cte1 AS (SELECT 1 AS ID FROM DUMMY)
SELECT ID FROM cte1;
Kindly help me writing below query in openquery.Thanks in advance
INSERT INTO Tablename
SELECT * FROM tablename1 WHERE insertionorderid IN (
SELECT orderid FROM temp_table2)
If you are trying to insert into SQL Server the syntax would be
INSERT INTO dbo.[YOURTABLE] SELECT * FROM [MATCHING_TABLE] WHERE CLAUSE
The caveat here is that the two tables must have identical schemas or you will have to explicitly define the columns. Also this will not work properly for tables with identity columns
I have the following statements in Oracle 11g:
CREATE TYPE person AS OBJECT (
name VARCHAR2(10),
age NUMBER
);
CREATE TYPE person_varray AS VARRAY(5) OF person;
CREATE TABLE people (
somePeople person_varray
)
How can i select the name value for a person i.e.
SELECT somePeople(person(name)) FROM people
Thanks
I'm pretty sure that:
What you're doing isn't what I'd be doing. It sort of completely violates relational principles, and you're going to end up with an object/type system in Oracle that you might not be able to change once it's been laid down. The best use I've seen for SQL TYPEs (not PL/SQL types) is basically being able to cast a ref cursor back for pipelined functions.
You have to unnest the collection before you can query it relationally, like so:
SELECT NAME FROM
(SELECT SP.* FROM PEOPLE P, TABLE(P.SOME_PEOPLE) SP)
That'll give you all rows, because there's nothing in your specifications (like a PERSON_ID attribute) to restrict the rows.
The Oracle Application Developer's Guide - Object Relational Features discusses all of this in much greater depth, with examples.
To insert query:-
insert into people values (
person_varray(person('Ram','24'))
);
To select :-
select * from people;
SELECT NAME FROM (SELECT SP.* FROM PEOPLE P, TABLE(P.somePeople) SP)
While inserting a row into people table use constructor of
person_varray and then the constructor
of person type for each project.
The above INSERT command
creates a single row in people table.
select somePeople from people ;
person(NAME, age)
---------------------------------------------------
person_varray(person('Ram', 1),
To update the query will be:-
update people
set somePeople =
person_varray
(
person('SaAM','23')
)