Is there an easier version of this SQL script? - sql

I'm trying to run a script on an Oracle Database via SQL Developer. There are five columns: ID, LAST_NAME, FIRST_NAME, USERID, and SALARY. This is the code I've written to accept values and edit the USERID field with the lowercase of the 1st letter of the FIRST_NAME and the whole LAST_NAME.
INSERT INTO ACT_MY_EMPLOYEE
VALUES (&P_ID, '&P_LAST_NAME', '&P_FIRST_NAME',
LOWER(SUBSTR('&P_FIRST_NAME', 1, 1) ||
SUBSTR('&P_LAST_NAME', 1, 7)), &P_SALARY);
Is there an easier version to this? Thank you!

You should definitely use && instead of & as single & will ask you for the value each time it is uses in your sql while double & will store substitution value (asks for value only once) and use it if same variable is used second time in the same session.
INSERT INTO ACT_MY_EMPLOYEE
VALUES (&P_ID, '&&P_LAST_NAME', '&&P_FIRST_NAME',
LOWER(SUBSTR('&&P_FIRST_NAME', 1, 1) ||
SUBSTR('&&P_LAST_NAME', 1, 7)), &P_SALARY);
Cheers!!

Maybe a stored procedure would be a good idea, especially if you frequently add new rows. Instead of keeping the INSERT INTO statement in some script (and have to remember where you put it), a procedure is permanently stored in the database.
It might look like this:
create or replace procedure p_ins_ame (
par_id in act_my_employee.id%type,
par_first_name in act_my_employee.first_name%type,
par_last_name in act_my_employee.last_name%type,
par_salary in act_my_employee.salary%type)
is
begin
insert into act_my_employee (id,
last_name,
first_name,
some_column,
salary)
values (
par_id,
par_last_name,
par_first_name,
lower (
substr (par_first_name, 1, 1)
|| substr (par_last_name, 1, 7)),
par_salary);
END;
You'd call it as
begin
p_ins_ame (par_id => 100,
par_first_name => 'Little',
par_last_name => 'Foot',
par_salary => 1000);
end;
Of course, it can be further developed so that it inserts a sequence number instead of a "fixed" ID value (or, if your database version supports it, use an identity column). Any changes you make will be used in all subsequent procedure calls.
As of a single INSERT INTO statement: as you've already been told, certain tools (such as SQL*Plus) allow you to use two ampersands (&&) along with the variable name. It means that you won't be prompted to enter the same variable's value twice (or as many times as it appears in that script), but only once.
That's OK, just beware! If you enter a slash (/) at the prompt and hit the ENTER key, you'll insert the same row once again (unless it fails on uniqueness). Don't forget to UNDEFINE those variables!

Related

Incorrect syntax error when trying to insert into SQL Server using multiple values

I am trying to insert into a table I created using multiple values. It throws error on incorrect syntax near coma in the first values line end.
I couldn't identify problem. I did try to insert separately, it inserted like a charm. Now I suspect the multi row inserting has some problem due to declared variable or GETDATE or both? It's not able to hold in session? I tried to put in transaction like begin tran and commit tran, still no luck. I have huge rows, any suggestions please?
DECLARE #USER VARCHAR(40)
SET #USER = SYSTEM_USER
INSERT INTO MyTable (Id, Name, User, Date)
VALUES (1, 'Cris', #USER, GETDATE()),
(2, 'Joel', #USER, GETDATE()),
(3, 'Kris', #USER, GETDATE());
I get this error:
Msg 102, Level 15, State 1, Line 18
Incorrect syntax near ','.
However, no error when inserting separately using the declare statement in individual insert.
The Issue is not with the Data But with your Column Names and the SQL Statement.
In SQL Server, there are certain System reserved Works which we call as Keywords. Usually, We don't entertain the use of these Keywords as Column Names since this may affect the Query Performance. The Column Names
Name, User and Date
Are Keywords or Data Types in SQL Server, so while compiling the statements it will make confusions to the Compiler. So You Can solve this issue with either of the below 2 approaches :
Approach# 1
Change the Column Names which have the Same name as of the Keywords. You Can Identify this column easily by typing the COlumn Name in the SQL Server Management Studio and if the Text Color Changes to Blud, Pink or Gray ( Default Color Settings in SSMS) these are some system-defined Keywords
Blue : Data Types
Pink : System Defined Functions
Gray : Keywords
Please Note :
Since changing the Column name will also cause issues with other Applications and you will have to change it in all those areas This approach is Less preferred if you already have a Stable system. But if you are just in the starting phase of your development then this will be a better way to solve future issues.
Approach# 2
This is the most easiest Idea, in all the places where you are using any of the systems reserved keywords listed above, put them inside Square brackets and this issue will be solved. This is the Quickest way to fix your issue
Your Updated Code should be like this :
DECLARE #USER VARCHAR(40)
SET #USER = SYSTEM_USER
INSERT INTO MyTable (Id, [Name], [User], [Date])
VALUES (1, 'Cris', #USER, GETDATE()),
(2, 'Joel', #USER, GETDATE()),
(3, 'Kris', #USER, GETDATE());

SQL Plus : insert into numeric values with single quotes around them gives error

A colleague of mine has created an export with INSERT INTO queries using Oracle SQL Developer. This export has put single quotes around every value, not just VARCHARs.
So let's say we have a simple table containing these columns (among others):
SOME_TEXT VARCHAR2(256 BYTE)
SOME_NUMBER NUMBER(15,2)
The export has an INSERT INTO like this:
Insert into MY_TABLE (SOME_TEXT,SOME_NUMBER) values ('test text','123,45');
You may note two things about the decimal value ('123,45'):
It has single quotes around it
It has a comma instead of a dot (the database of my colleague is Dutch, and mine is English)
The second is easy to fix, I can insert it in Oracle SQL Developer like this:
-- dot as thousand separator, comma as decimal point
alter SESSION set NLS_NUMERIC_CHARACTERS = '.,';
Insert into MY_TABLE (SOME_TEXT,SOME_NUMBER) values ('test text','123,45');
-- All other insert queries
alter SESSION set NLS_NUMERIC_CHARACTERS = ',.';
Which works fine.
However, some of the .sql files are very big, and can't be opened in SQL Developer. So instead I want to use SQL Plus to execute the .sql files containing the insert-statements.
However, even when NLS_NUMERIC_CHARACTERS is changed, I'm currently getting the following error in SQL Plus due to the single quotes around the numeric value:
SQL> #"C:\my\path\test_insert_statement.sql"
values ('test text','123,45')
*
ERROR at line 2:
ORA-01722: invalid number
I see two possible solutions, but I don't know if either is possible:
Is there a way to allow single quotes around numeric values in SQL Plus for Oracle databases?
Or is there a way to export a database without single quotes for numeric values in Oracle SQL Developer (then I can let my colleague generate another .sql file)?
You can use single quotes around a numeric value. The error has occurred due to the decimal point character. All you need to change the decimal point character which can be done, as you have shown, as given below.
SQL> create table tbl1(
SOME_TEXT VARCHAR2(256 BYTE),
SOME_NUMBER NUMBER(15,2)
); 2 3 4
Table created.
SQL> alter SESSION set NLS_NUMERIC_CHARACTERS = ',.';
Session altered.
SQL> Insert into tbl1 (SOME_TEXT,SOME_NUMBER) values ('test text','123,45');
1 row created.
SQL> select * from tbl1;
SOME_TEXT SOME_NUMBER
------------- -----------
test text 123,45

Apostrophe(') in SQL data,oracle 11g

I need to insert data into a sql table using a csv file with apostrophe(') and ('') in few rows.
I was able to handle it using the below method.
Get open_quote and close_quote and put the username and email_id between these two variable.
SELECT CHR(113)||CHR(39)||CHR(91) INTO OPEN_QUOTE FROM dual;
SELECT CHR(93)||CHR(39) INTO CLOSE_QUOTE FROM dual;
enter image description here
It looks ugly.I could have used replace but i opted for this method.
Could you please let me know of any other method so that my code looks good?
Attached is the screenshot of the dynamic sql.
You can have a single quote in a string by doubling it. For instance:
select 'It''s Bob''s book'
from dual;
As of Oracle 10g in PL/SQL you can have:
V_SQL_STATEMENT := q'[It's Bob's book]';
See Oracle SQL Reference for details on text literals.
Use the alternative-quoting mechanism and several REPLACEs instead of concatenation. It's a little extra work but it makes it clearer what the final SQL statement will look like.
v_sql_statement := replace(replace(replace(replace(
q'[
insert into login
(USER_ID,CLIENT_ID,EMAIL,PSWD_HASH,NETWORK_ID,FAILED_LOGIN_ATTEMPTS,NAME)
VALUES
(
LOGIN_SEQ.nextval,
#P_CLIENT_ID#,
'#PARSE_EMAIL#',
#V_PSWD_HASH#,
NULL,
0,
'#USER_NAME#'
)
]'
, '#P_CLIENT_ID#', p_client_id)
, '#PARSE_EMAIL#', parse_email(lower(c1.email)))
, '#V_PSWD_HASH#', v_pswd_hash)
, '#USER_NAME#', nvl(c1.name, generate_user_name(parse_email(c1.email))))
;

How to do an insert with multiple rows in Informix SQL?

I want to insert multiple rows with a single insert statement.
The following code inserts one row, and works fine:
create temp table mytmptable
(external_id char(10),
int_id integer,
cost_amount decimal(10,2)
) with no log;
insert into mytmptable values
('7662', 232, 297.26);
select * from mytmptable;
I've tried changing the insert to this, but it gives a syntax error:
insert into mytmptable values
('7662', 232, 297.26),
('7662', 232, 297.26);
Is there a way to get it working, or do I need to run many inserts instead?
You could always do something like this:
insert into mytmptable
select *
from (
select '7662', 232, 297.26 from table(set{1})
union all
select '7662', 232, 297.26 from table(set{1})
)
Pretty sure that's standard SQL and would work on Informix (the derived table is necessary for Informix to accept UNION ALL in INSERT .. SELECT statements).
As you found, you can't use multiple lists of values in a single INSERT statement with Informix.
The simplest solution is to use multiple INSERT statements each with a single list of values.
If you're using an API such as ESQL/C and you are concerned about performance, then you can create an INSERT cursor and use that repeatedly. This saves up the inserts until a buffer is full, or you flush or close the cursor:
$ PREPARE p FROM "INSERT INTO mytmptable VALUES(?, ?, ?)";
$ DECLARE c CURSOR FOR p;
$ OPEN c;
while (...there's more data to process...)
{
$PUT c USING :v1, :v2, :v3;
}
$ CLOSE c;
The variables v1, v2, v3 are host variables to hold the string and numbers to be inserted.
(You can optionally use $ FLUSH c; in the loop if you wish.) Because this buffers the values, it is pretty efficient. Of course, you could also simply use $ EXECUTE p USING :v1, :v2, :v3; in the loop; that foregoes the per-row preparation of the statement, too.
If you don't mind writing verbose SQL, you can use the UNION technique suggested by Matt Hamilton, but you will need a FROM clause in each SELECT with Informix. You might specify:
FROM "informix".systables WHERE tabid = 1, or
FROM sysmaster:"informix".sysdual, or
use some other technique to ensure that the SELECT has a FROM clause but only generates one row of data.
In my databases, I have either a table dual with a single row in it, or a synonym dual that is a synonym for sysmaster:"informix".sysdual. You can get away without the "informix". part of those statements if the database is 'normal'; the owner name is crucial if your database is an Informix MODE ANSI database.
In some versions of Infomix you can build a virtual table using the TABLE keyword followed by a value of one of the COLLECTION data types, such as a LIST collection. In your case, use a LIST of values of Unnamed Row type using the ROW(...) constructor syntax.
Creating a TABLE from COLLECTION value
http://www.ibm.com/support/knowledgecenter/SSGU8G_11.50.0/com.ibm.sqls.doc/ids_sqs_1375.htm
ROW(...) construction syntax, for literals of Unnamed Row data type
http://www.ibm.com/support/knowledgecenter/SSGU8G_11.50.0/com.ibm.sqlr.doc/ids_sqr_136.htm
Example:
select *
from TABLE(LIST{
ROW('7662', 232, 297.26),
ROW('7662', 232, 297.26)
}) T(external_id, int_id, cost_amount)
into temp mytmptable with no log
In the above, the data types are implied by the value, but when needed you can explicitly cast each value to the desired data type in the row constructor, like so:
ROW('7662'::char(10), 232::integer, 297.26::decimal(10,2))
You can also insert multiple rows by storing the values in an external file and executing the following statement in dbaccess:
LOAD FROM "externalfile" INSERT INTO mytmptable;
However, the values would have to be DELIMITED by a pipe "|" symbol, or whatever you set the DBDELIMITER environment variable to be.
If you're using the pipe delimiter, the data in your external file would look like:
7662|232|297.26|
7663|233|297.27|
...
NOTE that the data in the external file must be properly formatted or able to be converted to successfully be inserted into each mytmptable.column datatype.
Here is a simple solution fro bulk insert with SELECT part solving the rest
INSERT INTO cccmte_pp
( cmte, pref, nro, eje, id_tri, id_cuo, fecha, vto1, vto2, id_tit, id_suj, id_bie, id_gru )
SELECT * FROM TABLE (MULTISET {
row('RC', 4, 10, 2020, 1, 5, MDY(05,20,2020), MDY(05,20,2020),MDY(05,27,2020),101, 1, 96, 1 ),
row('RC', 4, 11, 2020, 1, 5, MDY(05,20,2020), MDY(05,20,2020),MDY(05,27,2020),101, 1, 96, 1 ) })
AS t( cmte, pref, nro, eje, id_tri, id_cuo, fecha, vto1, vto2, id_tit, id_suj, id_bie, id_gru )
;

PL/SQL varchar(10) to varchar(9)

How would I write a loop that has a select in the “in” clause of the loop that selects a col of type varchar(10), but then inserts those values in a col that wants them to be varchar(9)? Basically I’m trying to “typecast” from one precision to another, if that makes any sense. Example:
FOR V_TEN IN (SELECT THIS_IS_VARCHAR_TEN FROM TABLE WHERE SOMETHING=’VALUE’)
LOOP
INSERT INTO OTHER_TABLE
(THIS_IS_VARCHAR_NINE)
VALUES
(V_TEN);
END LOOP;
The error is that the column types aren’t the same. I’ve tried looking at to_char() and cast() but neither seem to be what I want. I realize there is a loss of precision here and am okay with that, since I actually know that the values in the varchar(10) column are always going to be 9 chars.
You are looking for the SUBSTR function.
Also, do not use PL/SQL for this, plain SQL will do and be faster.
INSERT INTO OTHER_TABLE
SELECT OTHER_COLUMN, SUBSTR(THIS_IS_VARCHAR_TEN,1,9)
FROM TABLE WHERE SOMETHING=’VALUE’;
And if there are really no values longer than nine character, you do not even need to call the substr function (it will be converted automatically, and raise an error if too long).
since I actually know that the values
in the varchar(10) column are always
going to be 9 chars.
If that's true, then you don't even need to use SUBSTR as others have been suggesting.
I believe the reason that you're getting an error is that you are trying to insert the value of V_TEN. When you use a construct like FOR x IN (SELECT ...) LOOP, x is implicitly declared as a record type. In your case, it's a record with only one field, but you still can't use it directly as a scalar type.
You just need to reference the field of the record by name in your insert.
FOR V_TEN IN (SELECT THIS_IS_VARCHAR_TEN FROM TABLE WHERE SOMETHING=’VALUE’)
LOOP
INSERT INTO OTHER_TABLE
(THIS_IS_VARCHAR_NINE)
VALUES
(V_TEN.THIS_IS_VARCHAR_TEN);
END LOOP;
In any case, as Thilo pointed out, there's no reason to do this in an explicit loop at all. Just write it as a single INSERT ... SELECT.
Use:
FOR V_TEN IN (SELECT SUBSTR(t.this_is_varchar_ten, 1, 9)
FROM TABLE t
WHERE t.something = 'VALUE')
LOOP
INSERT INTO OTHER_TABLE
(THIS_IS_VARCHAR_NINE)
VALUES
(V_TEN);
END LOOP;
Use the SUBSTR function to substring the VARCHAR(10) data so it is returned as VARCHAR(9)