Can I not use a function in an insert statement? - sql

It seems as though I cannot use a SAS function in an insert statement:
proc sql;
create table tq84_tab (col char(100));
insert into tq84_tab values (repeat('foo ', 10));
quit;
When I run the code, I am getting:
insert into tq84_tab values (repeat('foo ', 10));
---- -----
22 26
202 200
ERROR 22-322: Syntax error, expecting one of the following: a quoted string, a numeric constant, a datetime constant,
a missing value, +, -, MISSING, NULL, USER.
ERROR 200-322: The symbol is not recognized and will be ignored.
ERROR 202-322: The option or parameter is not recognized and will be ignored.
Am I doing something wrong or is my suspiscion indeed the case?

For an item to appear N times you repeat it N-1 times. You'll also need to macro quote the item if you want it to repeat a trailing space:
insert into tq84_tab values ("%sysfunc(repeat(%str(foo ), 9))");
You can also create a dummy table with only one row. Then use run-time functions to construct the value to insert.
create table onerow (ignore_me char(0));
insert into onerow values ('');
insert into tq84_tab select (repeat("foo ",9)) as col from onerow;
onerow is acting as Oracle's DUAL or SQL Server's bare select (no from).

No/Maybe.
With insert .. values you have to you fixed values, no functions.
In this simple case you'd be able to help yourself with SAS macro - call the SAS function using %sysfunc macro function, that "pre-processes" your code.
proc sql;
create table tq84_tab (col char(100));
insert into tq84_tab values ("%sysfunc(repeat(foo, 10))");
select * from tq84_tab;
quit;

Related

Posgresql is trying to insert into GENERATED ALWAYS column

My table schema:
CREATE TABLE project_sectors(
sector_id int GENERATED ALWAYS AS IDENTITY,
sector_name varchar(256),
project_count int,
PRIMARY KEY (sector_id)
);
And I am trying to execute a query for many tables with some particular column name:
DO $$
DECLARE
t text;
BEGIN
FOR t IN
SELECT table_name FROM information_schema.columns WHERE column_name = 'project_name'
LOOP
RAISE NOTICE 'INSERT METADATA FOR: %', t;
EXECUTE 'INSERT INTO project_sectors VALUES ($1, 0)'
USING t;
end loop;
end
$$ language 'plpgsql';
Once I try to run the query I get:
[42804] ERROR: column "sector_id" is of type integer but expression is of type text Hint: You will need to rewrite or cast the expression. Where: PL/pgSQL function inline_code_block line 9 at EXECUTE
When previously the EXECUTE statement was
EXECUTE format('INSERT INTO megaproject_sectors VALUES (''%I'', 0)', t)
I would get the error
ERROR: invalid input syntax for type integer: "railway"
railway is the value of t.
Why is it trying to insert data into GENERATED ALWAYS column?
Why is it trying to insert data into GENERATED ALWAYS column?
Because you are not specifying the target columns in your INSERT statement, so Postgres uses them from left to right.
It is good coding practice to always specify the target columns. As your table name is hardcoded, the dynamic SQL is unnecessary as well:
INSERT INTO project_sectors (sector_name, sector_count) VALUES (t.table_name, 0)
Note that in other database products, specifying less values than the table has columns would result in an error. So in e.g. Oracle your statement would result in "ORA-00947: not enough values"

Can't save comma separated number string in varchar2()

I've got a list of items I want to add in a single click, for this purpose I created a table with a column with a type varchar2(4000), in this column I want to list id's that refer to the other table so I can paste the value of this column as a parameter. ex. select t.* from table_name t where t.point_id in (varchar2 string of comma seprated point_ids).
The problem I've got is that when I put more than 1 id in the varchar2 field I get ORA-06502: PL/SQL: numeric or value error: character to number conversion error
How can I avoid this error? My field is varchar2, not number and I don't want it to be converted. I need the value I'm parsing to be saved. ex. (11, 12)
Picture of my Table:
EDIT: Note - My select is working okay, the problem I'm having is with saving the information.
My Insert :
procedure lab_water_pointsgroup (v_group_id lab_water_pointsgroups.group_name%type,
v_group_name lab_water_pointsgroups.group_code%type,
v_group_code lab_water_pointsgroups.lab_points_ids%type,
v_lab_points_ids lab_water_pointsgroups.group_id%type) as
begin
update lab_water_pointsgroups
set group_name = v_group_name,
group_code = v_group_code,
lab_points_ids = v_lab_points_ids
where group_id = v_group_id;
if ( SQL%RowCount = 0 ) then
insert into lab_water_pointsgroups
(group_id, group_name, group_code, lab_points_ids)
values
(v_group_id, v_group_name, v_group_code, v_lab_points_ids);
end if;
end;
Not sure how exactly I can help you here as you gave no example. Have a look at the below demo, maybe the contruct with xmltable solves your problem. HTH KR
create table testtab (id number);
insert into testtab values (1);
select * from testtab where id in ('1'); -- works
select * from testtab where id in (1); -- works
select * from testtab where id in (1,2); -- works
select * from testtab where id in ('1,2'); -- ORA-01722: invalid number
select * from testtab where id in (select to_number(xt.column_value) from xmltable('1,2') xt); -- works
Here is how you defined parameters for your procedure:
v_group_id lab_water_pointsgroups.group_name%type,
v_group_name lab_water_pointsgroups.group_code%type,
v_group_code lab_water_pointsgroups.lab_points_ids%type,
v_lab_points_ids lab_water_pointsgroups.group_id%type
I suspect that you made mistake with types, because id has name type, name has code type etc. So it should be:
v_group_id lab_water_pointsgroups.group_id%type,
v_group_name lab_water_pointsgroups.group_name%type,
v_group_code lab_water_pointsgroups.group_code%type,
v_lab_points_ids lab_water_pointsgroups.lab_points_ids%type
And I suggest to use merge instead of this update / insert, but it's not what you asked for :)
Your error is in that you don't make difference between variable containing comma separated numbers and actual enumeration in the 'in' operator. After your code analize and preparation to execution your statement will be like .. id in ('1,2,3') instead of ..id in (1,2,3), did you notice differnce ? So you need to transform comma separated values into array or in this case into collection. Your code should be like this:
select t.*
from table_name t
where t.point_id in
(select regexp_substr(YOUR_VARCHAR2_COLUMN_VALUE, '[^,]+', 1, level)
from dual
connect by regexp_substr(YOUR_VARCHAR2_COLUMN_VALUE, '[^,]+', 1, level) is not null)

Inserting Data Into Table Using Execute Immediate in Oracle

For example, I have some table "Test" which has one column "my_date" . What I am trying to do is adding record to a table using some variable:
query_date := "SELECT sysdate FROM dual";
EXECUTE IMMEDIATE ('insert into test values query_date');
I need to insert the record to the table in this exact way by constructing string and executing query, however I get error. Is it possible to do so?
You can either get the result of the first query into a (date) variable and then use that:
SELECT sysdate into query_date FROM dual;
insert into test (my_date) values (query_date)
-- or if you really want dynamic SQL, with a bind variable
EXECUTE IMMEDIATE 'insert into test (my_date) values (:query_date)' using query_date;
Or reading your question literally, use the first string as part of the second string by concatenating it:
query_date := "SELECT sysdate FROM dual";
EXECUTE IMMEDIATE 'insert into test (my_date) ' || query_date;
If you printed out the second statement instead of executing it you'd see:
insert into test (my_date) SELECT sysdate FROM dual
... which is valid SQL. This will work if the query_string is more complicated or is itself being constructed dynamically. But if the number of column expressions in the query_string select list also varies, you will have to construct the column list dynamically too, otherwise you'll have too many or too few columns for the insert.
Exactly how you do that depends on how you're constructing the query string - essentially as you add an expression to the query string, you'd also add a column name to a separate list, and end up with:
EXECUTE IMMEDIATE 'insert into test (' || column_list ' ||) ' || query_string);
where column_list is built up as say col1, col2 and query_string as select x.col1, y.col2 from ....
There isn't an obvious reason to use dynamic SQL in what you've shown. Or, if you are really using sysdate, any need for separate query to get that, as you can just do:
insert into test (my_date) values (sysdate)
... so I assume your real scenario is really more complicated. But note that you don't use the values keyword with an insert ... select ... pattern. You can with a single column and a subquery but it's not a good idea even then, and doesn't work if you have multiple columns in the subquery.
Why you need an EXECUTE IMMEDIATE for Insert statement ?
As long as the base table where you are inserting the values remain same we dont need to do EXIMM. Now the query_date ? Just do a traditional looping or variable thing.

Automatic sequence in oracle

I am trying to cretae a automatic sequence number genaration using trigger but it is giving me following wrror while inserting the values.
ALTER TABLE sppinv_tblinventory_ex ADD (
CONSTRAINT sppinv_tblinventory_PK PRIMARY KEY (uniqueid));
create sequence row_seq ;
create or replace trigger row_count before insert on sppinv_tblinventory_ex
for each row
begin
select row_seq.nextval into : new.uniqueid from dual;
end;
if I am excuting below then I am able to insert values
insert into sppinv_tblinventory_ex
select row_seq.nextval,
b.member_id,b.src_claim_nbr,b.client_nbr,b.src_platform_cd,
b.suspense_date,b.batch_gen_key,b.bucket_name,b.grouper_rule,
b.event_number,b.case_stat,b.case_stat_dt,b.assigned_to,
b.assigned_on,b.followup_dt,b.release_ind,b.release_dt,
b.viewtype
from sppinv_tblinventory b
When I am inserting the values with out uniqueID I am getting error like below
insert into sppinv_tblinventory_ex
select b.member_id,b.src_claim_nbr,b.client_nbr,b.src_platform_cd,
b.suspense_date,b.batch_gen_key,b.bucket_name,b.grouper_rule,
b.event_number,b.case_stat,b.case_stat_dt,b.assigned_to,
b.assigned_on,b.followup_dt,b.release_ind,b.release_dt,
b.viewtype
from sppinv_tblinventory b
ORA-00947: not enough values
Note : I dont want to disable the trigger
ORA-00947: not enough values - means you have n number of columns in the table but you are only supplying values for (n-m) number of fields.
In your case, if you dont want to insert unique id, then you may have to do
Insert into sppinv_tblinventory_ex (col1, col2, col3.. coln) select (val1, val2, val3 .. valn)
There are so many answers for this on internet...
Try removing the space before new.uniqueid ; and add a IF test like this :
create or replace trigger row_count
before insert on sppinv_tblinventory_ex
for each row
begin
IF :new.uniqueid IS NULL THEN
select row_seq.nextval into :new.uniqueid from dual;
END IF;
end;
Now if you put null in the corresponding value field in your insert, it should work

INSERT ALL INTO and Sequence.nextval for a Surrogate Key

I'm trying to insert 40 rows using an INSERT ALL INTO and I'm not certain on how to insert the surrogate key. Here's what I have
BEGIN
INSERT ALL
INTO question(question_id)
VALUES (question_seq.nextval)
END
Now if I add another INTO VALUES then I get a unique constraint violation.
BEGIN
INSERT ALL
INTO question(question_id)
VALUES (question_seq.nextval)
INTO question(question_id)
VALUES (question_seq.nextval)
END
How can I update the sequences nextval value for each INTO VALUES so that I can avoid the unique constraint violation? I assumed that nextval would automatically update itself.
UPDATE: I don't know if this is the best way to handle this but here's the solution I came up with:
first I created a function that returns a value
then I called that function in the id field of the VALUES clause
create or replace
FUNCTION GET_QUESTION_ID RETURN NUMBER AS
num NUMBER;
BEGIN
SELECT UHCL_QUESTIONS_SEQ.nextval
INTO num
FROM dual;
return num;
END GET_QUESTION_ID;
INSERT ALL
INTO question(question_id)
VALUES (GET_QUESTION_ID())
INTO question(question_id)
VALUES (GET_QUESTION_ID())
Being from a SQL Server background, I've always created a trigger on the table to basically emulate IDENTITY functionality. Once the trigger is on, the SK is automatically generated from the sequence just like identity and you don't have to worry about it.
You can use something like this:
insert into question(question_id)
select question_seq.nextval from
(
select level from dual connect by level <= 40
);
Although it's not a very convenient format, especially if there are other columns you want to add. You'd probably need to create another UNION ALL query, and join it by the LEVEL or ROWNUM.
My first thought was to do something like this:
insert into question(question_id)
select question_seq.nextval value from dual
union all
select question_seq.nextval from dual;
But it generates ORA-02287: sequence number not allowed here, due to the restrictions on sequence values.
By the way, are you sure your INSERT ALL works without a subquery? I get the error ORA-00928: missing SELECT keyword, and the diagram from the 11.2 manual implies there must be a subquery:
I don't know if this is the best way to handle this but here's the solution I came up with:
first I created a function that returns a value
then I called that function in the id field of the VALUES clause
create or replace
FUNCTION GET_QUESTION_ID RETURN NUMBER AS
num NUMBER;
BEGIN
SELECT UHCL_QUESTIONS_SEQ.nextval
INTO num
FROM dual;
return num;
END GET_QUESTION_ID;
INSERT ALL
INTO question(question_id)
VALUES (GET_QUESTION_ID())
INTO question(question_id)
VALUES (GET_QUESTION_ID())