SQLSTATE[22P02]: Invalid text representation - sql

I'm using Postgresql and PHP 5.3.x with PDO to access the DB.
I have this the SQL query (stripped down version), with a placeholder for PDO to fill in:
INSERT INTO t_articles (a_article_id) VALUES (?) RETURNING a_id
I want a_article_id to be either a number, like 5, or else it should be the result of the subquery:
((SELECT max(a_article_id) FROM t_articles) + 1)
However, PDO says:
SQLSTATE[22P02]: Invalid text representation: 7 ERROR: invalid input syntax for integer: "(SELECT max(a_article_id) FROM t_articles) + 1"
And I've tried to set the subquery as the default value, but it is not allowed apparently:
ERROR: cannot use sub query in default expression
How can I insert the result of this sub query (or what can be done to achieve the same result)?

You'd have to use INSERT...SELECT for that:
insert into t_articles (a_article_id)
select max(a_article_id) + 1
from t_articles
returning id
Or if you don't need contiguous values for a_article_id, use a sequence for it:
Create a sequence, we'll call it article_id_sequence.
-- Get the current max(a_article_id)+1 to use instead of FIRST_VAL below
create sequence article_id_sequence
start FIRST_VAL
owned by t_articles.a_article_id;
Set the default value for t_articles.a_article_id to nextval('article_id_sequence').
alter table t_articles
alter column a_article_id
set default nextval('article_id_sequence');
Use the default value when inserting:
insert into t_articles (a_article_id)
values (default)
returning id;

Related

SELECT INTO INTEGER ARRAY

I want to assign values in a table column (column type is integer) to integer array. Unfortunately I could not.
--TYPE--
CREATE OR REPLACE TYPE ABILITY_ID_ARRAY IS VARRAY(100) OF INTEGER
--DECLARE IN PROCEDURE
ABILITY_IDS ABILITY_ID_ARRAY;
--STATEMENT--
SELECT ABILITY_FK INTO ABILITY_IDS
FROM T_EDUCATION_ABILITY_REL
WHERE EDUCATION_FK = edu_id;
I received this error:
[Error] ORA-00932 (16: 12): PL/SQL: ORA-00932: inconsistent datatypes: expected UDT got NUMBER
You need to use BULK COLLECT clause to store a resultset into a collection variable.
Please study the documentation of SELECT INTO statement:
https://docs.oracle.com/database/121/LNPLS/selectinto_statement.htm#LNPLS01345
into_clause
With this clause, the SELECT INTO statement retrieves one or more columns from a single row and stores them in either one or
more scalar variables or one record variable.
bulk_collect_into_clause With this clause, the SELECT INTO statement retrieves an entire result set and stores it in one or more
collection variables.
The PL/SQL statament should look like this in your case:
SELECT ABILITY_FK BULK COLLECT INTO ABILITY_IDS
FROM T_EDUCATION_ABILITY_REL
WHERE EDUCATION_FK = edu_id;

How to insert french number format in oracle SQL table

I am facing the issue to insert the french number in a number field of oracle.I am using SQL Developer IDE. When i insert the number(say 3,4) its says invalid number. Specifically,
I don't want to replace the value 3,4 to 3.4.
I tried with changing the NLS Setting also (using command
Alter session set NLS_NUMERIC_CHARACTERS=',';
If I use this command I am able to insert directly in editor but insert command is not working due to comma, Oracle assume that its another value.
Any help would be appreciated.
You can't and you don't need to.
The number format for SQL literals requires the . for the decimal separator.
In the column itself the decimal separator isn't stored at all. You just need to change the display format of the number. This is ideally done in your application, not on SQL level. But if you require this in the SQL output, use to_char() to format your numbers:
select to_char(your_number_column, '9999D99')
from your_table;
The D in the format mask will be replaced with the decimal separator defined by the current session's NLS settings.
A dot . and , are returned literally:
select to_char(your_number_column, '9999,99')
from your_table;
More details on the to_char() format mask can be found in the manual: https://docs.oracle.com/cd/E11882_01/server.112/e41084/sql_elements004.htm#BABIGFBA
You application/ide may be performing an implicit conversion of character data into number, which uses the session default nls_numeric_characters.
You can force the insert command to perform an explicit conversion as follows using to_number:
SQL> alter session set nls_language = ENGLISH;
Session altered
SQL> alter session set nls_numeric_characters = ',.';
Session altered
SQL> create table t1 (num_col number);
Table created
SQL> insert into t1 (num_col) values ('3.4');
insert into t1 (num_col) values ('3.4')
ORA-01722: invalid number
SQL> -- this is equivalent to:
SQL> insert into t1 (num_col) values (to_number('12345,678', '999999D999', 'NLS_NUMERIC_CHARACTERS=.,'));
insert into t1 (num_col) values (to_number('12345,678', '999999D999', 'NLS_NUMERIC_CHARACTERS=.,'))
ORA-01722: invalid number
SQL> -- now converting explictly
SQL> insert into t1 (num_col) values (to_number('12.345,678', '999999D999', 'NLS_NUMERIC_CHARACTERS=,.'));
1 row inserted
SQL> insert into t1 (num_col) values (to_number('12345,678', '999999D999', 'NLS_NUMERIC_CHARACTERS=,.'));
1 row inserted
in NLS_NUMERIC_CHARACTERS the first character is the decimal separator and the second one is the thousands grouping marker.
The docs have more info:
https://docs.oracle.com/cd/B28359_01/olap.111/b28126/dml_functions_2117.htm

How to use a default in SELECT query?

I have created a default value like this:
create default [dbo].[MAX_DATE] as '31/12/9999 23:59:59'
Now I would like to return it from a sql query. I tried this:
SELECT
CASE date_field WHEN dbo.MAX_DATE THEN '' ELSE date_field END
FROM
myTable
However, I get the following error:
Server: Msg 107, Level 16, State 2, Line 2
The column prefix 'dbo' does not match with a table name or alias name used in the query.
Defaults are used by binding them to columns. The default value is applied by the server when a row is created and a column value isn't specified. (See: http://msdn.microsoft.com/en-us/library/ms173565.aspx)
Here are 3 options for you:
Option (1)
It looks like you're using a "named constant" for use in doing compares. In this case, you might want to go with a function, such as:
CREATE Function [dbo].[MAX_DATE] ()
Returns Datetime
as
BEGIN
Return '99991231 23:59:59'
END;
GO
select dbo.MAX_DATE()
Option (2)
Another option you might consider is having a table of named constants. It might have the columns: ID (autonumber), ValueName, numValue, strValue, dtValue, binValue. You would populate the ValueName and the appropriate column depending on what type of value you're storing in it.
Option (3)
To use a constant in just your current script, you can declare a value and set it's value and use it in the rest of your script. These variables are out of scope outside of their batch, so either when the script has finished running, or SQL encounters a GO statement. E.g.
DECLARE #MAX_VALUE as datetime
set #MAX_VALUE = '99991231 23:59:59'
select #MAX_VALUE

INSERT SELECT in Firebird

I'm new to firebird and I have verious issues. I want to insert various lines into a table selected from another table.
Here's the code:
/*CREATE GENERATOR POS; */
SET GENERATOR POS TO 1;
SET TERM ^;
create trigger BAS_pkassign
for MATERIAL
active before insert position 66
EXECUTE BLOCK
AS
declare posid bigint;
select gen_id(POS, 1)
from RDB$DATABASE
into :posid;
BEGIN
END
SET TERM ; ^
INSERT INTO MATERIAL ( /*ID */ LOCATION, POSID, ARTID, ARTIDCONT, QUANTITY )
SELECT 1000, ':posid', 309, BAS_ART.ID, 1
FROM BAS_ART
WHERE BAS_ART.ARTCATEGORY LIKE '%MyWord%'
The ID should autoincrement from 66 on. The posid should autoincrement from 1 on.
Actually it is not inserting anything.
I'm using Firebird Maestro and have just opened the SQL Script Editor (which doesnt throw any error message on executing the script).
Can anybody help me?
Thanks!
Additional information:
The trigger should autoincrement the column "ID" - but I dont know how exactly I can change it so it works.. The ':posid' throws an error using it :posid but like this theres no error (I guess its interpretated as a string). But how do I use it right?
I dont get errors when I execute it. The table structure is easy. I have 2 tables:
1.
Material (
ID (INTEGER),
Location (INTEGER),
POSID (INTEGER),
ARTID (INTEGER),
ARTIDCONT (INTEGER),
QUANTITY (INTEGER),
OTHERCOLUMN (INTEGER))
and the 2. other table
BAS_ART (ID (INTEGER), ARTCATEGORY (VARCHAR255))
-> I want to insert all entries from the table BAS_ART which contain "MyWord" in the column ARTCATEGORY into the MATERIAL table.
I don't understand why you need the trigger at all.
This problem:
I want to insert all entries from the table BAS_ART which contain "MyWord" into the MATERIAL table
Can be solved with a single insert ... select statement.
insert into material (id, location, posid, artid, quantity)
select next value for seq_mat_id, 1000, next value for seq_pos, id, 1
from bas_art
where artcategory = 'My Word';
This assumes that there is a second sequence (aka "generator") that is named seq_mat_id that provides the new id for the column material.id
For most of my answer I will assume a very simple table:
CREATE TABLE MyTable (
ID BIGINT PRIMARY KEY,
SomeValue VARCHAR(255),
posid INTEGER
)
Auto-increment identifier
Firebird (up to version 2.5) does not have an identity column type (this will be added in Firebird 3), instead you need to use a sequence (aka generator) and a trigger to get this.
Sequence
First you need to create a sequence using CREATE SEQUENCE:
CREATE SEQUENCE seqMyTable
A sequence is atomic which means interleaving transactions/connections will not get duplicate values, it is also outside transaction control, which means that a ROLLBACK will not revert to the previous value. In most uses a sequences should always increase, so the value reset you do at the start of your question is wrong for almost all purposes; for example another connection could reset the sequence as well midway in your execution leaving you with unintended duplicates of POSID.
Trigger
To generate a value for an auto-increment identifier, you need to use a BEFORE INSERT TRIGGER that assigns a generated value to the - in this example - ID column.
CREATE TRIGGER trgMyTableAutoIncrement FOR MyTable
ACTIVE BEFORE INSERT POSITION 0
AS
BEGIN
NEW.ID = NEXT VALUE FOR seqMyTable;
END
In this example I always assign a generated value, other examples assign a generated value only when the ID is NULL.
Getting the value
To get the generated value you can use the RETURNING-clause of the INSERT-statement:
INSERT INTO MyTable (SomeValue) VALUES ('abc') RETURNING ID
INSERT INTO ... SELECT
Using INSERT INTO ... SELECT you can select rows from one table and insert them into others. The reason it doesn't work for you is because you are trying to assign the string value ':pos' to a column of type INTEGER, and that is not allowed.
Assuming I have another table MyOtherTable with a similar structure as MyTable I can transfer values using:
INSERT INTO MyTable (SomeValue)
SELECT SomeOtherValue
FROM MyOtherTable
Using INSERT INTO ... SELECT it is not possible to obtain the generated values unless only a single row was inserted.
Guesswork with regard to POSID
It is not clear to me what POSID is supposed to be, and what values it should have. It looks like you want to have an increasing value starting at 1 for a single INSERT INTO ... SELECT. In versions of Firebird up to 2.5 that is not possible in this way (in Firebird 3 you would be able to use ROW_NUMBER() for this).
If my guess is right, then you will need to use an EXECUTE BLOCK (or a stored procedure) to assign and increase the value for every row to be inserted.
The execute block would be something like:
EXECUTE BLOCK
AS
DECLARE posid INTEGER = 1;
DECLARE someothervalue VARCHAR(255);
BEGIN
FOR SELECT SomeOtherValue FROM MyOtherTable INTO :someothervalue DO
BEGIN
INSERT INTO MyTable (SomeValue, posid) VALUES (:someothervalue, :posid);
posid = posid + 1;
END
END
Without an ORDER BY with the SELECT the value of posid is essentially meaningless, because there is no guaranteed order.

how to use the value of an Identity column for a diffent column in the same insert

I have created a table like this:
CREATE TABLE A
( ID BIGINT NOT NULL GENERATED BY DEFAULT AS IDENTITY (
START WITH +1
INCREMENT BY +1
NO MINVALUE
NO MAXVALUE
NO CYCLE
CACHE 20
NO ORDER )
, ID_MIRROR CHAR(20))
I would like to do an insert such that ID would be automatically set, and ID_MIRROR would be what is in ID, but prefixed with 'PRE'.
I have unsuccessfully tried the following:
INSERT INTO A (ID_MIRROR)
VALUES ( 'PRE' || CHAR(A.ID))
Error 12/4/2009 6:43:08
AM 0:00:00.296 DB2 Database Error:
ERROR [42703] [IBM][DB2/AIX64]
SQL0206N "A.ID" is not valid in the
context where it is used.
SQLSTATE=42703 1 0
insert into A (id_mirror)
VALUES (CONCAT('PRE', CHAR(identity_val_local())))
ID_MIRROR is NULL, subsequent inserts are previous value of ID.
insert into A (id_mirror)
VALUES (CONCAT('PRE', CHAR(scope_identity())))
Error 12/4/2009 6:11:11
AM 0:00:00.234 DB2 Database Error:
ERROR [42884] [IBM][DB2/AIX64]
SQL0440N No authorized routine named
"SCOPE_IDENTITY" of type "FUNCTION"
having compatible arguments was found.
SQLSTATE=42884 1 0
Another forum answered the question like this:
INSERT INTO A (ID_MIRROR) VALUES ( 'PRE' || IDENTITY_VAL_LOCAL());
Why would you ever need to do this when you could simply create this column on an ad hoc basis any time you wanted to, in a SELECT statement?