I have a table that contains common subjects text, that requires insertion of some string at run time. For example below:
CREATE TABLE certification.cert_email_dtls (
cert_eid_id numeric(10,0),
cert_eid_email_body character varying(8000),
);
insert into certification.cert_email_dtls(1,'hello world <blank-value1>);
insert into certification.cert_email_dtls(2,'hello guys <blank-other-value2>);
insert into certification.cert_email_dtls(3,'hello <blank-value3> india <some-other-value4>);
and so on. <some-value>,<some-other-value> etc come at run time.
SELECT cert_eid_id ,
cert_eid_email_body,
INTO v_id,
v_email_body_end
FROM certification.cert_email_dtls where cert_eid_id = in_id;
My requirement is to insert that some-values that are coming in between and generate the final email body.
format() might be the perfect tool for this. Read details in the manual.
If you can save cert_eid_email_body in a compatible format, like:
CREATE TABLE cert_email_dtls (
cert_eid_id serial PRIMARY KEY
, cert_eid_email_body text
);
INSERT INTO cert_email_dtls (cert_eid_email_body)
VALUES ('hello %1$s india %2$s');
The whole operation can be as simple as:
SELECT format(cert_eid_email_body, 'foo', 'bar', 'baz') AS cert_eid_email_body
FROM cert_email_dtls
WHERE cert_eid_id = 1;
Result:
cert_eid_email_body
-------------------
hello foo india bar
Note that the 3rd parameter 'baz' is silently ignored, since it is not referenced in the string.
SQL Fiddle.
Aside: Don't use numeric(10,0) for an ID or primary key. integer or bigint typically serve you better. Using serial PRIMARY KEY above as it probably should be.
Related
I'm trying to create 3 access tables in SQL view on Microsoft Access but whenever I try to execute it, I receive the following error. 'Syntax Error in CREATE TABLE statement'.
Please find my code below.
CREATE TABLE Book (
Book_ID int,
Book_Title varchar (30),
PRIMARY KEY (Book_ID)
);
CREATE TABLE Users (
User_ID int,
User_Name varchar (30),
PRIMARY KEY (User_ID)
);
CREATE TABLE Borrows (
User_ID int,
Book_ID int,
B_ID int,
PRIMARY KEY(B_ID),
FOREIGN KEY(User_ID) REFERENCES Users(User_ID),
FOREIGN KEY(Book_ID) REFERENCES Book(Book_ID)
);
INSERT INTO Book VALUES (101, 'The Hobbit'), (102, 'To Kill a Mockingbird');
INSERT INTO Users VALUES (1, 'Stephen'), (2, 'Tom'), (3,' Eric');
INSERT INTO Borrows VALUES (3, 102, 1), (1, 101, 2);
Appreciate any feedback I can get, have a good day.
Your first CREATE TABLE executed flawlessly from the query designer in Access 2010. However my preference is to include the PRIMARY KEY constraint as part of the field definition:
CREATE TABLE Book (
Book_ID int PRIMARY KEY,
Book_Title varchar (30)
);
That variation also executed successfully.
I suspect you have at least 2 issues to resolve:
Access does not allow you to execute more than one SQL statement at a time (as Heinzi and Albert mentioned). You must execute them one at a time.
In Access, INSERT ... VALUES can only be used to add one row at a time. Revise your inserts accordingly.
IOW, split the first one into 2 statements which you then execute individually:
-- INSERT INTO Book VALUES (101, 'The Hobbit'), (102, 'To Kill a Mockingbird');
INSERT INTO Book VALUES (101, 'The Hobbit');
INSERT INTO Book VALUES (102, 'To Kill a Mockingbird');
Then split and execute the remaining inserts similarly.
Your code example use SQL Server (T-SQL) syntax, not MS Access syntax.
The syntax for Access' CREATE TABLE statement is documented here:
https://learn.microsoft.com/en-us/office/client-developer/access/desktop-database-reference/create-table-statement-microsoft-access-sql
The most obvious differences seem to be there is no varchar type and that PRIMARY KEY needs a constraint name if specified in an extra line. There might be more, see the article and its examples for details. I also suggest that you submit your statements one-by-one, instead of submitting a complete ;-separated batch; I'm not sure Access queries even support the latter.
In the script file, I inserted the following code.
drop table Test;
create table Test(
name char(2) unique not null,
constraint name_c check(
regexp_like(name, '^[A-Z]{1,2}$', 'c')
)
);
insert into Test values ('B');
The developer never budge. It keeps on saying violating the name_c constraint and I don't understand why. The regular expression looks fine for me.
Some variants, however, succeeded, for example, dropping the dollar sign
drop table Test;
create table Test(
name char(2) unique not null,
constraint name_c check(
regexp_like(name, '^[A-Z]{1,2}', 'c')
)
);
insert into Test values ('B');
And I don't understand either.
Why?
Edit: This is the problem: logically the regex is right. But somehow Oracle SQL Developer doesn't budge.
if you went it to start with 1 or 2 capital letter and doesn't matter what coming next you should do it like this :
^[A-Z]{1,2}.*
.* mean 1 or more character exept \n (nex line)
I've recently been caught out by being unaware of the issue where SERIAL fields increment whether data is inserted or not.
Most of the answers I've read on this matter discuss preventing holes from appearing in the column, which I'm fairly certain in most cases isn't what the question posed is concerned with, and it certainly wasn't in my case.
My situation was that a specific user of my software was using a feature in a way that caused millions of upserts to be performed on a single record. That record was used as status information, and in my naivety I was blissfully unaware of the impending failure when the INTEGER id fields nextval() reached its limit, that being the following error:
ERROR: integer out of range
SQL state: 22003
So my question is and was, how can I prevent id fields from incrementing the next sequence value in the case of a conflict rollback.
I look foward to others adding their knowledge to my solution.
My immediate solution to this issue which alleviated the out of range situation was to alter the column to BIGINT, as follows:
ALTER TABLE MyTable ALTER COLUMN idMyTable TYPE BIGINT;
The number of records in my case was extremely small (<1000) so this was a trivial alteration to perform.
Once that was out of the way, it was time to look for a solution to the underlying issue. My solution is unlikely to be as performant as using a SERIAL field, so keep that in mind based on your use case if you are going to implement something similar to what I have done - there's always a trade off somewhere.
Consider the following table and resulting data insert/query:
CREATE TABLE TestTable ( id SERIAL PRIMARY KEY NOT NULL, Key TEXT UNIQUE NOT NULL, Val TEXT );
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'banana') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'apple') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'peach') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Animal', 'horse') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
SELECT * FROM TestTable;
id Key Val
1 Fruit peach
4 Animal horse
In this case, each conflict during the Fruit update has bumped the SERIAL value, even though it wasn't creating any new record in TestTable.
Now this is the workaround I'm currently working with. If anyone knows how to concatenate the table name onto 'NEW.id', I'd love to hear that, as I like to name my id columns idTablename for consistency.
CREATE OR REPLACE FUNCTION IncrementSerial()
RETURNS trigger AS $fn$
BEGIN
EXECUTE format('SELECT COALESCE( MAX( id ), 0 ) + 1 FROM %I.%I;',TG_TABLE_SCHEMA,TG_TABLE_NAME) INTO NEW.id;
RETURN NEW;
END
$fn$ LANGUAGE 'plpgsql'
CREATE TABLE TestTable ( id INTEGER PRIMARY KEY NOT NULL, Key TEXT UNIQUE NOT NULL, Val TEXT );
CREATE TRIGGER trgIncrementSerial
BEFORE INSERT ON TestTable
FOR EACH ROW
EXECUTE PROCEDURE IncrementSerial()
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'banana') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'apple') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Fruit', 'peach') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
INSERT INTO TestTable (Key,Val) VALUES ('Animal', 'horse') ON CONFLICT( Key ) DO UPDATE SET Val=EXCLUDED.Val;
SELECT * FROM TestTable;
id Key Val
1 Fruit peach
2 Animal horse
As you can see, the SERIAL id is now just using the next highest id number, which is ideal for most of my use cases.
Obviously this is going to be a problem if the id key must always be unique, as removal of the last record will free up that id. If that's not a problem (ie. where the id is just used for references and cascades) then this might be a good solution for you.
I have two tables that are initialized with something like this:
create table foo (
"id" varchar(254) not null primary key,
"first_name" varchar(254) not null);
create table my_user (
"id" serial not null primary key,
"role" varchar(254) not null,
"first_name" varchar(254) not null);
The reason why the id column of foo is a varchar(254) instead of a serial is because in normal operations I'm inserting in an id provided by Google OAuth2 instead of generating my own id values.
I now have a set of records in a third table I call temp with the first_name column. I'm trying to emulate this post, but I'm not sure how to do so for string primary keys.
select * from (insert into my_user(id, role)
('some id value I want to generate, like historical || incrementing number',
[a fixed number],
select first_name from temp) returning id);
As it says in the official Postgres documentation, I know I need to get the arguments following the insert statement into the format of a table that matches the declaration of my_user. I guess I'm just lost as to how to generate a column of the ids I want here, or even a column of one number repeating.
Thanks for reading
You could insert a UUID (it's like a GUID) in your ID... It's guaranteed to be unique.
Sadly it's a little complex to load the module: Generating a UUID in Postgres for Insert statement?
Ah... and what wildplasser said, +1! :-)
I am creating a small database for a telecom system.
One of the tables (calls) requires that a if a phone number's area code is not contained in a predefined list, then the number should not be added to the table.
The way I have thought about doing this is to put a check constraint within the calls table to not accept numbers that are not a part of this mentioned list. However, this list is quite long and I am not too sure if there would be a better implementation method.
Here is the list:
01 or 02: local/national number. Ex.: 01612 338866.
075, 077, 078, 079: mobile phone number. Ex.: 07747 556647.
0800: free number. Ex.: 08002 223344.
0845, 0870: special service. Ex.: 08451 423456.
08442 to 08449: 5p special service. Ex.: 08444 404404.
08712 to 08719: 10p special service. Ex.: 08713 457893.
090, 091, 098: premium rate special service. Ex.: 09119 229595.
The only way I could think of to do this is as follows:
ALTER TABLE calls ADD (CONSTRAINT area_ck
CHECK area_code ("01" or "02" or "075" or "077" or "078" or "079" or "0800" or
"0845" or "0870" or (BETWEEN ("08442" AND "08449")) or
(BETWEEN ("08712" AND "08719")) or
"090" or "091" or "098")
) ;
My two main issues with this are:
It gives an error as it is implemented incorrectly
If I were to modify it slightly until it did work, would it still be a long way about trying to solve my task?
The more common approach would be to define a table of valid area codes
CREATE TABLE area_code (
area_code VARCHAR2(5) PRIMARY KEY
);
Fill the Area_Code table with the set of valid values
INSERT INTO area_code( area_code ) VALUES( '01' );
INSERT INTO area_code( area_code ) VALUES( '02' );
INSERT INTO area_code( area_code ) VALUES( '075' );
...
or
BEGIN
FOR i IN 1000 .. 2999
LOOP
INSERT INTO area_code( area_code )
VALUES( to_char( i, '00000' ) );
END LOOP;
END;
And then define a foreign key constraint from your Call table to the Area_Code table
CREATE TABLE call (
call_id NUMBER PRIMARY KEY,
area_code VARCHAR2(5) REFERENCES area_code( area_code ),
<<other columns>>
);
That's going to be more efficient to enforce than a CHECK constraint and it will be easier to list the valid area codes.
The first problem is about using = and between together. Do it like:
area_code in ('01', '02', '03') or area_code between ('1000' and '1500') or ....
You could either write area_code='01' OR area_code='02' ... or you can use area_code in ('01','02', ...). You also need to add area_code before between keywords.
But I would suggest you to store the area codes in a table instead of the check constraint and use the area codes as foreign keys. This way the list of area codes can easily be modified.