This may seem like a pointless question, but I'll ask it.
In creating a DDL, I want to have all my relationships and "extra" information NAMED, so when the db throws an error its using MY name and not some auto-generated ID
this is great for my relations and indexes, however I've noticed that DB2 stores "sequences", and it just names them... I can't seem to figure out how to name them in the create script - and going in AFTER creation to figure out what is what is not an option.
EXAMPLE
SEQUENCE MY_SCHEMA.SQL131021121240860 is what DB2 named it when I created
What I'd like to do
CREATE SEQUENCE MY_SCHEMA.MY_TABLE_SEQ START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 32767 0 0 CACHE 20;
I've tried this BEFORE the CREATE TABLE, and AFTER... but the sequences ALWAYS go in with this 'SQL131021121240..' naming
Here are the two ways I've tried it....
-- BEFORE, as my ERD software does...
CREATE SEQUENCE MY_SCHEMA.TEST_SEQ START WITH 1 INCREMENT BY 1 MINVALUE 1 MAXVALUE 32767 0 0 CACHE 20;
CREATE TABLE MY_SCHEMA.TEST (
ID SMALLINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1, INCREMENT BY 1 ) NOT NULL,
NAME VARCHAR( 255 ) NOT NULL,
CONSTRAINT MY_SCHEMA_TEST_PK PRIMARY KEY ( ID )
) IN STORE16K;
-- AFTER, as it makes sense to me, that the table has to exist first
CREATE TABLE MY_SCHEMA.TEST2 (
ID SMALLINT GENERATED BY DEFAULT AS IDENTITY ( START WITH 1, INCREMENT BY 1 ) NOT NULL,
NAME VARCHAR( 255 ) NOT NULL,
CONSTRAINT MY_SCHEMA_TEST2_PK PRIMARY KEY ( ID )
) IN STORE16K;
CREATE SEQUENCE MY_SCHEMA.TEST2_SEQ START WITH 1 INCREMENT BY 1 MINVALUE 1000 MAXVALUE 32767 0 0 CACHE 20;
In both cases a sequence that is named by the DB shows up in the SEQUENCES folder....
can it be done? is there a reason NOT to do it?, I just hate seeing labels like "SQL131021121240860" showing up in diagrams and DDLs....
In doing some more reading, it appears as though I don't understand SEQUENCES, however I think my question is still relevant. I'd still like to NAME them.
I'm confused by the fact that sequences get automatically created on identity fields - the comments in this post ( DB2 Auto generated Column / GENERATED ALWAYS pros and cons over sequence ) make it seem as thou it's an either/or proposition to use 'generated identity' or 'sequences' - but the behavior of CREATE TABLE with a single column as identity DOES create a sequence...NOT ONLY that but it appears as though it's a 1:1 - which may be needed for identity, and its most likely that a defined sequence can be used by other things.. I get that they are "not bound" to tables specifically....
either way - my question of HOW to NAME them, instead of letting the DB name this is still my point.
Thx
As you have already realized, DB2 implicitly creates a sequence for each IDENTITY column, and there is no way to supply the name for such a sequence. Your requirement to have custom names seems arbitrary.
Related
I was using the serial datatype to create a PRIMARY KEY column but was recently informed that this is outdated and I should be using GENERATED ALWAYS AS IDENTITY instead. I used pgAdmin 4 to fix it by changing the datatype to integer and then adding the generated as identity. This worked but I am a bit baffled by the result as the SQL code shows 2 id columns instead of 1 now:
CREATE TABLE public.names
(
id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
id integer NOT NULL GENERATED ALWAYS AS IDENTITY ( INCREMENT 1 START 1 MINVALUE 1 MAXVALUE 2147483647 CACHE 1 ),
name character varying(128) COLLATE pg_catalog."default",
CONSTRAINT names_pkey PRIMARY KEY (id)
)
However when I query the table with SELECT * FROM names it still shows me only 1 id column as there was before.
I would appreciate help on understanding what went wrong and how to fix it.
After lengthy search here is what I found:
There is indeed only one column which can be confirmed using psql. However using the command pg_dump -st names <databasename> from bash I noticed that there were 2 sequences being defined associated with the id column: public.names_id_seq and public.names_id_seq1.
This can also be confirmed by running SELECT c.relname FROM pg_class c WHERE c.relkind = 'S'; from psql where both sequences show up.
If one tries to delete public.names_id_seq1 one gets the error ERROR: cannot drop sequence apis_id_seq1 because column id of table apis requires it.
The solution is to delete the first sequence with DROP SEQUENCE names_id_seq; After refreshing pgAdmin4 the second column was gone.
TL;DR
Run SELECT c.relname FROM pg_class c WHERE c.relkind = 'S'; and identify duplicate sequences (ie. the ones where there is a version with and one without a 1 at the end)
Delete the superfluous sequence with DROP SEQUENCE <sequenceName_seq>; (only the one without a 1 at the end can be deleted)
Refresh pgAdmin4
is it Possible to recover from a temporal table?
I defined 2 tables like this:
create table lib.x(
"ID" INTEGER GENERATED ALWAYS AS IDENTITY (
START WITH 1 INCREMENT BY 1
NO MINVALUE NO MAXVALUE
NO CYCLE NO ORDER
CACHE 20
),
char char(1),
row_start TIMESTAMP(12) NOT NULL GENERATED ALWAYS AS ROW BEGIN IMPLICITLY hidden,
row_end TIMESTAMP(12) NOT NULL GENERATED ALWAYS AS ROW END IMPLICITLY hidden,
row_id TIMESTAMP(12) GENERATED ALWAYS AS TRANSACTION START ID IMPLICITLY hidden,
PERIOD SYSTEM_TIME(row_start, row_end)
);
create table lib.x_history like lib.x;
alter TABLE lib.x
ADD VERSIONING USE HISTORY TABLE lib.x_history;
then I did this:
insert into lib.x(char) values('a'), ('b'), ('c');
delete from lib.x where id = 2;
Is it possible to restore the char 'b' with the ID 2?
Yes and no. It is not a system restore, but of course you can query the old state and use it to insert into the regular table. See the section "Querying system-period temporal data" in the Db2 docs.
You would first construct a query to search AS OF. Later, you could use it as input to an insert statement. Thus, you restore the value, but it is treated as deleting the value and inserting it as new.
In pgsql, is there a way to have a table of several values, and choose one of them (say, other_id), find out what its highest value is and make every new entry that is put in the table increment from that value.
I suppose this was just too easy to have had a chance of working..
ALTER TABLE address ALTER COLUMN new_id TYPE SERIAL
____________________________________
ERROR: type "serial" does not exist
Thanks much for any insight!
Look into postgresql documentation of datatype serial. Serial is only short hand.
CREATE TABLE tablename (
colname SERIAL
);
is equivalent to specifying:
CREATE SEQUENCE tablename_colname_seq;
CREATE TABLE tablename (
colname integer NOT NULL DEFAULT nextval('tablename_colname_seq')
);
ALTER SEQUENCE tablename_colname_seq OWNED BY tablename.colname;
This happened because you may use the serial data type only when you are creating a new table or adding a new column to a table. If you'll try to ALTER an existing table using this data type you'll get an error. Because serial is not a true data type, but merely an abbreviation or alias for a longer query.
In case you would like to achieve the same effect, as you are expecting from using serial data type when you are altering existing table you may do this:
CREATE SEQUENCE my_serial AS integer START 1 OWNED BY address.new_id;
ALTER TABLE address ALTER COLUMN new_id SET DEFAULT nextval('my_serial');
The first line of the query creates your own sequence called my_serial. The
OWNED BY statement connects the newly created sequence with the exact column of your table. In your case the table is address and the column is new_id.
The START statement defines what value this sequence should start from.
The second line alters your table with the new default value, which will be determined by the previously created sequence.
It will give you the same result as you were expecting from using serial.
A quick glance at the docs tells you that
The data types smallserial, serial and bigserial are not true types
but merely a notational convenience for creating unique identifier columns
If you want to make an existing (integer) column to work as a "serial", just create the sequence by hand (the name is arbitrary), set its current value to the maximum (or bigger) of your current address.new_id value, at set it as default value for your address.new_id column.
To set the value of your sequence see here.
SELECT setval('address_new_id_seq', 10000);
This is just an example, use your own sequence name (arbitrary, you create it), and a number greater than the maximum current value of your column.
Update: as pointed out by Lucas' answer (which should be the acccepted one) you should also specify to which column the sequence "belongs to" by using CREATE/ALTER SEQUENCE ... OWNED BY ...
There is a need to build constraint on the column that guarantees that only one value in all rows is 1 and all the others are 0.
Solution with triggers exists but I would like to have something built in.
Is such thing possible at all?
Edit
Actually I just noticed you are on SQL Server 2008 you could use a filtered index for this
CREATE UNIQUE NONCLUSTERED INDEX UIX ON YourTable (col) where col = 1
Original Answer
The easiest way would probably be to store this one special pk in a separate one row table. The no more than one row aspect can be enforced with check constraints.
CREATE TABLE OneRowTable
(
lock CHAR(1) DEFAULT 'X' NOT NULL PRIMARY KEY CHECK (lock = 'X'),
OtherTablePK int
);
Otherwise assuming you might have an id field comprised of positive integers you could add a computed column with the following definition
case when col=1 then -1 else id end
and add a unique constraint to that.
I have a table where the results are sorted using an "ORDER" column, eg:
Doc_Id Doc_Value Doc_Order
1 aaa 1
12 xxx 5
2 bbb 12
3 ccc 24
My issue is to initially set up this order column as efficiently and reusably as possible.
My initial take was to set up a scalar function that could be used as a default value when a new entry is added to the table:
ALTER FUNCTION [dbo].[Documents_Initial_Order]
( )
RETURNS int
AS
BEGIN
RETURN (SELECT ISNULL(MAX(DOC_ORDER),0) + 1 FROM dbo.Documents)
When a user wants to permute 2 documents, I can then easily switch the 2 orders.
It works nicely, but I now have a second table I need to set up the same way, and I am quite sure there is a nicer way to do it. Any idea?
Based on your comment, I think you have a very workable solution. You could make it a little more userfriendly by specifying it as a default:
alter table documents
add constraint constraint_name
default (dbo.documents_initial_order()) for doc_order
As an alternative, you could create an update trigger that copies the identity field to the doc_order field after an insert:
create trigger Doc_Trigger
on Documents
for insert
as
update d
set d.doc_order = d.doc_id
from Documents d
inner join inserted i on i.doc_id = d.doc_id
Example defining doc_id as an identity column:
create table Documents (
doc_id int identity primary key,
doc_order int,
doc_value ntext
)
It sounds like you want an identity column that you can then override once it gets it initial value. One solution would be to have two columns, once call "InitialOrder", that is an auto-increment identity column, and then a second column called doc_order that initially is set to the same value as the InitialOrder field (perhaps even as part of the insert trigger or a stored procedure if you are doing inserts that way), but give the user the ability to edit that column.
It does require an extra few bytes per record, but solves your problem, and if its of any value at all, you would have both the inital document order and the user-reset order available.
Also, I am not sure if your doc_order needs to be unique or not, but if not, you can then sort return values by doc_order and InitialOrder to ensure a consistent return sequence.
If there is no need to have any control over what that DOC_ORDER value might be, try using an identity column.