Why can't you use " ' " on a numeric value in a table? - sql

For an example, I wanted to search all employees in department 20. Why do I have to list is as the first one versus the second one. Does ' only apply to data that is in words?
Select * from emp
Where dept = 20
versus
Select * from emp
Where dept = '20'

It is about datatype.
if dept column's datatype is NUMBER, you'd use where dept = 20
if it is a string (one of CHAR datatypes; VARCHAR2 being the most common), then you'd enclose value into single quotes: where dept = '20'
Oracle will try (and sometimes succeed) to implicitly convert one datatype to another. So, if dept column was a varchar2 and you used where dept = 20, it might work. But, if there were dept values that aren't numerics but contain letters (e.g. departments whose values were 2A or CT), then you might expect problems.
For example:
SQL> create table temp (dept varchar2(2));
Table created.
Insert works because of implicit conversion of 20 into a string:
SQL> insert into temp values (20);
1 row created.
Select also works:
SQL> select * from temp where dept = 20;
DE
--
20
This is, though, how you should be using it:
SQL> select * from temp where dept = '20';
DE
--
20
But, if not all values in a table are numerics (although stored into varchar2 datatype column) ...
SQL> insert into temp values ('2A');
1 row created.
... then implicit conversion won't work any more:
SQL> select * from temp where dept = 20;
ERROR:
ORA-01722: invalid number
no rows selected
Enclosing the value into single quotes is the way to go:
SQL> select * from temp where dept = '20';
DE
--
20
SQL>

Related

Oracle SQL/PLSQL: change type of specific columns in one time

Assume following table named t1:
create table t1(
clid number,
A number,
B number,
C number
)
insert into t1 values(1, 1, 1, 1);
insert into t1 values(2, 0, 1, 0);
insert into t1 values(3, 1, 0, 1);
clid A B C
1 1 1 1
2 0 1 0
3 1 0 1
Type of columns A, B, and C is number. What I need to do is to change types of those columns to VARCHAR but in a quite tricky way.
In my real table I need to change datatype for hundred of columns so it is not so convenient to write a statement like following hundred of time:
ALTER TABLE table_name
MODIFY column_name datatype;
What i need to do is rather to convert all columns to VARCHAR except CLID column like we can do that in Python or R
Is there any way to do so in Oracle SQL or PLSQL?
Appreciate your help.
Here is a example of procedure that can help...
It accepts two parameters that should be a name of your table and list of columns you do not want to change...
At the begining there is a cursor that gets all the column names for your table except the one that you do not want to change...
Then it loop's though the columns and changes them...
CREATE OR REPLACE procedure test_proc(p_tab_name in varchar2
, p_col_names in varchar2)
IS
v_string varchar2(4000);
cursor c_tab_cols
is
SELECT column_name
FROM ALL_TAB_COLS
WHERE table_name = upper(p_tab_name)
and column_name not in (select regexp_substr(p_col_names,'[^,]+', 1, level) from dual
connect by regexp_substr(p_col_names, '[^,]+', 1, level) is not null);
begin
FOR i_record IN c_tab_cols
loop
v_string := 'alter table ' || p_tab_name || ' modify '
|| i_record.column_name || ' varchar(30)';
EXECUTE IMMEDIATE v_string;
end loop;
end;
/
Here is a demo:
DEMO
You can also extend this procedure with a type of data you want to change into... and with some more options I am sure....
Unfortunately, that isn't as simple as you'd want it to be. It is not a problem to write query which will write query for you (by querying USER_TAB_COLUMNS), but - column must be empty in order to change its datatype:
SQL> create table t1 (a number);
Table created.
SQL> insert into t1 values (1);
1 row created.
SQL> alter table t1 modify a varchar2(1);
alter table t1 modify a varchar2(1)
*
ERROR at line 1:
ORA-01439: column to be modified must be empty to change datatype
SQL>
If there are hundreds of columns involved, maybe you can't even
create additional columns in the same table (of VARCHAR2 datatype)
move values in there
drop "original" columns
rename "new" columns to "old names"
because there'a limit of 1000 columns per table.
Therefore,
creating a new table (with appropriate columns' datatypes),
moving data over there,
dropping the "original" table
renaming the "new" table to "old name"
is probably what you'll finally do. Note that it won't be necessarily easy either, especially if there are foreign keys involved.
A "query that writes query for you" might look like this (Scott's sample tables):
SQL> SELECT 'insert into dept (deptno, dname, loc) '
2 || 'select '
3 || LISTAGG ('to_char(' || column_name || ')', ', ')
4 WITHIN GROUP (ORDER BY column_name)
5 || ' from emp'
6 FROM user_tab_columns
7 WHERE table_name = 'EMP'
8 AND COLUMN_ID <= 3
9 /
insert into dept (deptno, dname, loc) select to_char(EMPNO), to_char(ENAME), to_char(JOB) from emp
SQL>
It'll save you from typing names of hundreds of columns.
I think its not possible to change data type of a column if values are there...
Empty the column by copying values to a dummy column and change data types.

How to pass string data type to number datatype in plsql

I have procedure like this...
declare
v_psg varchar2(10);
id_no number;
begin
select value into v_psg from settings_am where key = 'PSG';
select id into id_no from product where to_char(psg) in (v_psg);
end;`
The value returned from select value into v_psg from settings_am where key = 'PSG'; would be
'1','2','3'
when i run this procedure i am returned with ora error - ORA-01403.
please advise how i should pass the v_psg value to psg column of product table?
EDIT - Tried with test case suggested
If you got ORA-01403, you were kind of lucky. It is the NO_DATA_FOUND error, which means that one (probably the first) query didn't return anything.
Those two statements could be combined into
select id
from product
where to_char(psg) in (select value
from settings_am
where key = 'PSG'
);
Why would you select value first, and then use it in another query? Besides, it just wouldn't work. v_psg is declared as VARCHAR2 variable. The way you described it, it contains the following string: '1','2','3', as if this is what you have:
SQL> create table settings_am (key varchar2(10),
2 value varchar2(20)); --> note size here
Table created.
SQL> insert into settings_am (key, value)
2 values ('PSG', q'['1','2','3']');
1 row created.
SQL> select * From settings_am;
KEY VALUE
---------- --------------------
PSG '1','2','3'
SQL>
As you can see, I enlarged the value column size, although variable you declared says 10. Why? Because of
SQL> select length(value) from settings_am where key = 'PSG';
LENGTH(VALUE)
-------------
11
i.e. you can't put something that is long 11 into something that accepts length 10.
Or, if your data actually contains 3 rows for the PSG key, are those values already enclosed into single quotes? If so, that's strange; people usually don't do that. Anyway, suppose that you managed to get string '1,2,3' (which is what I presume you actually have) into a VARCHAR2 variable, then you have to split it into rows in order to be able to use it in the IN clause:
SQL> create table product (id number, psg varchar2(10));
Table created.
SQL> insert into product (id, psg) values (100, '1');
1 row created.
SQL> insert into product (id, psg) values (200, '2');
1 row created.
SQL>
Query is then (where lines #3 - 5 represent splitting a string into rows):
SQL> select p.id
2 from product p
3 where p.psg in (select regexp_substr('&&v_psg', '[^,]+', 1, level)
4 from dual
5 connect by level <= regexp_count('&&v_psg', ',') + 1
6 );
Enter value for v_psg: 1,2,3
ID
----------
100
200
So, wouldn't it be simpler to use
SQL> select id
2 from product
3 where to_char(psg) in (select value
4 from settings_am
5 where key = 'PSG'
6 );
ID
----------
100
200
SQL>
Note that both options also show why your query is wrong: you can't put two values (rows) into a variable declared as id_no number; as you'd get TOO_MANY_ROWS error.
Finally, what is it that you'd want to do? What problem are you trying to solve? Apparently, except for special cases (only one row for each value) your query can't work. If you could provide test case (create table & insert into sample data), as well as expected output, it would be easier to help you.

Oracle Using Variable in DML data fetch?

I'm trying the following:
SET SERVEROUTPUT ON;
DECLARE
V_COUNT NUMBER := 1;
V_LIITM NUMBER := 1;
V_LIMCU CHAR(12) := NULL;
V_LILOCN CHAR(20) := NULL;
V_LILOTN CHAR(30) := NULL;
BEGIN
-- INITIAL COUNT --
SELECT COUNT(*) INTO V_COUNT FROM QADTA.F41021
where exists (select IBGLPT FROM QADTA.F4102
where ibitm = liitm and ibmcu = LIMCU
and ibglpt <> liglpt AND (LIPBIN = 'P' OR LIPQOH != 0));
DBMS_OUTPUT.PUT_LINE('There are initially '||V_COUNT||' missing registries.');
-- GENERATING SEARCH VARIABLES --
SELECT LIITM, LIMCU, LILOCN, LILOTN, LIGLPT
INTO V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN, V_LIGLPT
FROM QADTA.F41021 where exists (select IBGLPT FROM QADTA.F4102
where ibitm = liitm and ibmcu = LIMCU and ibglpt <> liglpt
AND (LIPBIN = 'P' OR LIPQOH != 0)) AND ROWNUM <= 1;
-- FIRST EXERCISE -- INSERT --
INSERT INTO QADTA.F41021_BACKOUT
SELECT LIITM, LIMCU, LILOCN, LILOTN, LIGLPT FROM QADTA.F41021
WHERE (LIITM, LIMCU, LILOCN, LILOTN)
IN (V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN);
-- SECOND EXERCISE -- UPDATE --
UPDATE QADTA.F41021
SET F41021.LIGLPT = (SELECT IBGLPT FROM QADTA.F4102
WHERE IBITM = LIITM and IBMCU = LIMCU)
WHERE (LIITM, LIMCU, LILOCN, LILOTN)
IN (V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN);
END;
However, when executing I get error "00920 - Invalid Relational Operators". I nullified the DML sentences & it worked, but I need to do data modification.
With IN operator you can check if a list of expressions exists in another list of expressions. So if you use multiple expressions to the left of the operator, you also have to use multiple expressions on the right side.
In your example you will try to compare 4 values LIITM, LIMCU, LILOCN, LILOTN with the values V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN.
For it to work, you have to make a group of elements by putting them in parentheses.(V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN).
WHERE (LIITM, LIMCU, LILOCN, LILOTN)
IN ((V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN)
, (V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN));
Without the parentheses, Oracle interprets the four values V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN as a list of elements, and they do not match to the expression.
This is wrong:
WHERE (LIITM, LIMCU, LILOCN, LILOTN)
IN (V_LIITM, V_LIMCU, V_LILOCN, V_LILOTN);
Have a look at an example:
SQL> create table x as select * From dept where 1 = 2;
Table created.
SQL> insert into x
2 select * from dept
3 where (deptno, dname) in (10, 'ACCOUNTING');
where (deptno, dname) in (10, 'ACCOUNTING')
*
ERROR at line 3:
ORA-00920: invalid relational operator
SQL> insert into x
2 select * from dept
3 where deptno = 10
4 and dname = 'ACCOUNTING';
1 row created.
SQL>
I guess you see what you have to do in order to fix it.
Also, while generating search variables, you're selecting into V_LIGLPT which is not declared.
Furthermore, I'd suggest you to use table aliases and qualify all columns in all queries with those aliases, so that it is clear which column belongs to which table.
Finally, when INSERTING INTO, name all target columns - don't rely on the fact that you "know" description of the target table, as it may (and some day it will) change.

Convert LONG into VARCHAR2 or some text datatype

As we all know LONG is deprecated in Oracle a long back but Oracle itself is still using this datatype in their views.
So if I have to change LONG into some kind of text datatype how can I achieve that.
I am trying to query this and getting error.
ORA-00932: inconsistent datatypes: expected - got LONG
Query -
SELECT NVL(ie.column_expression, ic.column_name)
from user_ind_columns ic left join user_ind_expressions ie on ic.index_name = ie.index_name and ic.table_name = ie.table_name
where ic.table_name = 'Some Table'
There are several methods, one such is create table using TO_LOB. It is designed to convert a LONG or LONG RAW column to a CLOB or BLOB, respectively. Other methods are using PL/SQL, DBMS_XMLGEN. You can also use TO_LOB in insert statements.
Let's see how to convert LONG into CLOB-
SQL> CREATE TABLE t (x INT, y LONG);
Table created.
SQL>
SQL> INSERT INTO t VALUES (1, RPAD('*',9,'!'));
1 row created.
SQL> INSERT INTO t VALUES (2, RPAD('*',9,'#'));
1 row created.
SQL> INSERT INTO t VALUES (3, RPAD('*',9,'#'));
1 row created.
SQL> COMMIT;
Commit complete.
SQL>
So, we have table t with column y s LONG data type.
SQL> CREATE TABLE t1
2 AS
3 SELECT * FROM t
4 /
SELECT * FROM t
*
ERROR at line 3:
ORA-00997: illegal use of LONG datatype
SQL>
We can see the LONG restriction.
Let's use TO_LOB to convert it into CLOB.
SQL> CREATE TABLE t1
2 AS
3 SELECT x,
4 to_lob(y) as y
5 FROM t
6 /
Table created.
SQL> desc t1;
Name Null? Type
----------------------------------------------------- -------- ------------------------------------
X NUMBER(38)
Y CLOB
SQL>
Now you have the same table with the LONG column converted to CLOB.
this is stupid (as in probably not efficient) but it works for samll lengths of y (ie < 2000 characters)..
CREATE TABLE t (x INT, y LONG);
INSERT INTO t VALUES (1, RPAD('*',9,'!'));
CREATE TABLE t1
AS
SELECT x,
regexp_substr(SYS.DBMS_XMLGEN.GETXML('select y from t where rowid = '''||rowid||''''),'<Y>(.*)</Y>',1,1,'in',1) y
FROM t
/
it works by using dbms_xmlgen to generate a clob based on the LONG column.. then substr-ing the value back out.
this only works for small contents of the LONG column. but that is all i had and this worked for me.
I had a similar need, to list the objects and their sizes (including info on columns used in indexes), and came with this solution:
select idx1.table_owner owner, idx1.table_name, idx1.index_name, listagg(nvl(idx1.column_expression,idx1.column_name),',') within group (order by idx1.column_position) column_name
from xmltable(
'/ROWSET/ROW'
passing (select dbms_xmlgen.getxmltype('select ic.table_owner, ic.table_name, ic.index_name, ic.column_position, ic.column_name, ie.column_expression
from all_ind_columns ic
left outer join dba_ind_expressions ie on ie.table_owner=ic.table_owner and ie.table_name=ic.table_name and ie.index_name=ic.index_name and ie.column_position=ic.column_position') from dual)
columns index_name varchar2(30) path 'INDEX_NAME'
, table_owner varchar2(30) path 'TABLE_OWNER'
, table_name varchar2(30) path 'TABLE_NAME'
, column_position number path 'COLUMN_POSITION'
, column_name varchar2(30) path 'COLUMN_NAME'
, column_expression varchar2(4000) path 'COLUMN_EXPRESSION') idx1
group by idx1.table_owner, idx1.table_name, idx1.index_name

Using SEQUENCE(Oracle) in WHERE Clause [duplicate]

The following Oracle SQL code generates the error "ORA-02287: sequence number not allowed here":
INSERT INTO Customer (CustomerID,Name) VALUES (Customer_Seq.nextval,'AAA');
SELECT * FROM Customer where CustomerID=Customer_Seq.currval;
The error occurs on the second line (SELECT statement). I don't really understand the problem, because this does work:
INSERT INTO Customer (CustomerID,Name) VALUES (Customer_Seq.nextval,'AAA');
SELECT Customer_Seq.currval from dual;
You have posted some sample code, so it is not clear what you are trying to achieve. If you want to know the assigned value, say for passing to some other procedure you could do something like this:
SQL> var dno number
SQL> insert into dept (deptno, dname, loc)
2 values (deptno_seq.nextval, 'IT', 'LONDON')
3 returning deptno into :dno
4 /
1 row created.
SQL> select * from dept
2 where deptno = :dno
3 /
DEPTNO DNAME LOC
---------- -------------- -------------
55 IT LONDON
SQL>
Edit
We can use the RETURNING clause to get the values of any column, including those which have been set with default values or by trigger code.
You don't say what version of Oracle you are using. There have in the past been limitations on where sequences can be used in PL/SQL - mostly if not all gone in 11G. Also, there are restrictions in SQL - see this list.
In this case you may need to write:
SELECT Customer_Seq.currval INTO v_id FROM DUAL;
SELECT * FROM Customer where CustomerID=v_id;
(Edited after comments).
This doesn't really directly answer your question, but maybe what you want to do can be resolved using a the INSERT's RETURNING clause?
DECLARE
-- ...
last_rowid rowid;
-- ...
BEGIN
-- ...
INSERT INTO Customer (CustomerID,Name) VALUES (Customer_Seq.nextval,'AAA') RETURNING rowid INTO last_rowid;
SELECT * FROM Customer where rowid = last_rowid;
-- ...
END;
/
You may not use a sequence in a WHERE clause - it does look natural in your context, but Oracle does not allow the reference in a comparison expression.
[Edit]
This would be a PL/SQL implementation:
declare
v_custID number;
cursor custCur is
select customerid, name from customer
where customerid = v_custID;
begin
select customer_seq.nextval into v_custID from dual;
insert into customer (customerid, name) values (v_custID, 'AAA');
commit;
for custRow in custCur loop
dbms_output.put_line(custRow.customerID||' '|| custRow.name);
end loop;
end;
You have not created any
sequence
First create any sequence its cycle and cache. This is some basic example
Create Sequence seqtest1
Start With 0 -- This Is Hirarchy Starts With 0
Increment by 1 --Increments by 1
Minvalue 0 --With Minimum value 0
Maxvalue 5 --Maximum Value 5. So The Cycle Of Creation Is Between 0-5
Nocycle -- No Cycle Means After 0-5 the Insertion Stopes
Nocache --The cache Option Specifies How Many Sequence Values Will Be Stored In Memory For Faster Access
You cannot do Where Clause on Sequence in SQL beacuse you cannot filter a sequence . Use procedures like #APC said