I have added new uuid column for one of my tables and installed uuid-ossp extension.
Now I want to update all existing records and set value for this new uuid column.
I do not want to use DEFAULT fallback for ADD COLUMN, but rather I want to do it in UPDATE statement, so I have something like this:
UPDATE table_name SET uuid = (SELECT uuid_generate_v4());
but the issue I have is that same UUID is generated for all records.
Is there a way to pass seed value or something to generate function or another way to enforce generated UUIDs to be unique?
You could try modifying the UUID subquery such that it forces/tricks Postgres into generating a new UUID for each record:
UPDATE table_name
SET uuid = uuid_generate_v4()
WHERE uuid IS NOT NULL;
The WHERE clause is just a dummy, but perhaps will result in Postgres calling the UUID function once for each record.
If you call the uuid_generate_v4() function in a subquery, PostgreSQL will assume that the subquery needs to be called only once, since it does not contain any reference to the surrounding query. Consequently, all rows will be updated to the same uuid, which fails.
If you remove the subquery and leave only the function call, the function is called for each row, since it is VOLATILE.
Related
In my exercise sheet, my teacher use a python interface with SQLite. But I preferred to work with SQLite on Datagrip.
I'm asked to write a function which take as argument a table and returns the primary key column. I found that writing function in SQLite wasn't possible and that I needed some php interface or whatever.
Anyway as far as it's just training to get some SQL knowledge I tried to write a query for a table which returns a table containing the primary keys of a table.
Using the PRAGMA table_info (Evenements) query I get this table :
So I copied it in a Test table and wrote this query which works :
SELECT name from Test where pk;
But I can't replace Test by PRAGMA table_info (Evenements) or table_info (Evenements).
Do you know if I can directly use the table_info result without having to copy it in a new table?
The function that you should use is pragma_table_info():
SELECT *
FROM pragma_table_info('Evenements')
You will get info about all the columns of the table Evenements.
If you want info only for the primary key add a WHERE clause:
WHERE pk
I am adding a column as a foreign key which cannot be NULL and so need to have a DEFAULT value.
ALTER TABLE location
ADD [retailer_brand_id] INT NOT NULL DEFAULT (SELECT retailer_id from retailer),
FOREIGN KEY(retailer_brand_id) REFERENCES retailer_brand(retailer_brand_id);
What I want to achieve is, get the retailer_id from SELECT retailer_id from retailer and if it is equal to 12 then set it to 0, otherwise set to the retailer_id returned by the select query.
When I use the above query, I get an error message
Subqueries are not allowed in this context. Only scalar expressions are allowed.
I recommend a calculated column instead....so you don't also have to have this case statement in application logic as well as the table definition...don't want it in 2 spots...and don't have to worry about when retailerid changes...calc column would take care of that
I needed similar functionality. Calculated column is not an option for me, since my value should be changeble later by user, so I went with trigger on insert.
Described for example here: Trigger to update table column after insert?
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 ...
Can a Check Constraint (or some other technique) be used to prevent a value from being set that contradicts its prior value when its record is updated.
One example would be a NULL timestamp indicating something happened, like "file_exported". Once a file has been exported and has a non-NULL value, it should never be set to NULL again.
Another example would be a hit counter, where an integer is only permitted to increase, but can never decrease.
If it helps I'm using postgresql, but I'd like to see solutions that fit any SQL implementation
Use a trigger. This is a perfect job for a simple PL/PgSQL ON UPDATE ... FOR EACH ROW trigger, which can see both the NEW and OLD values.
See trigger procedures.
lfLoop has the best approach to the question. But to continue Craig Ringer's approach using triggers, here is an example. Essentially, you are setting the value of the column back to the original (old) value before you update.
CREATE OR REPLACE FUNCTION example_trigger()
RETURNS trigger AS
$BODY$
BEGIN
new.valuenottochange := old.valuenottochange;
new.valuenottochange2 := old.valuenottochange2;
RETURN new;
END
$BODY$
LANGUAGE plpgsql VOLATILE
COST 100;
DROP TRIGGER IF EXISTS trigger_name ON tablename;
CREATE TRIGGER trigger_name BEFORE UPDATE ON tablename
FOR EACH ROW EXECUTE PROCEDURE example_trigger();
One example would be a NULL timestamp indicating something happened,
like "file_exported". Once a file has been exported and has a non-NULL
value, it should never be set to NULL again.
Another example would be a hit counter, where an integer is only
permitted to increase, but can never decrease.
In both of these cases, I simply wouldn't record these changes as attributes on the annotated table; the 'exported' or 'hit count' is a distinct idea, representing related but orthogonal real world notions from the objects they relate to:
So they would simply be different relations. Since We only want "file_exported" to occur once:
CREATE TABLE thing_file_exported(
thing_id INTEGER PRIMARY KEY REFERENCES(thing.id),
file_name VARCHAR NOT NULL
)
The hit counter is similarly a different table:
CREATE TABLE thing_hits(
thing_id INTEGER NOT NULL REFERENCES(thing.id),
hit_date TIMESTAMP NOT NULL,
PRIMARY KEY (thing_id, hit_date)
)
And you might query with
SELECT thing.col1, thing.col2, tfe.file_name, count(th.thing_id)
FROM thing
LEFT OUTER JOIN thing_file_exported tfe
ON (thing.id = tfe.thing_id)
LEFT OUTER JOIN thing_hits th
ON (thing.id = th.thing_id)
GROUP BY thing.col1, thing.col2, tfe.file_name
Stored procedures and functions in PostgreSQL have access to both old and new values, and that code can access arbitrary tables and columns. It's not hard to build simple (crude?) finite state machines in stored procedures. You can even build table-driven state machines that way.
I have a postgres table like this:
CREATE SEQUENCE seq;
CREATE TABLE tbl (id INTEGER DEFAULT VALUE nextval('seq'), data VARCHAR);
When I insert into the table:
INSERT INTO tbl (data) VALUES ('something');
How can I get back the value of the id field for the record I just created?
(Note, I may have got some of the SQL syntax a bit off; the gist should be clear, though)
Suppose for the sake of argument that I'm not able to call currval on the same session because I don't control the transaction boundaries. I might be working in the same session with my next call, but I might not be, too.
You're looking for INSERT ... RETURNING. From The Manual's section on INSERT:
The optional RETURNING clause causes INSERT to compute and return value(s) based on each row actually inserted. This is primarily useful for obtaining values that were supplied by defaults, such as a serial sequence number. However, any expression using the table's columns is allowed. The syntax of the RETURNING list is identical to that of the output list of SELECT.
In the same session:
SELECT currval('seq');
EDIT: But if you can't use currval/nextval because you don't know if the inserting and selecting of the sequence value will occur in the same session, and if you're on postgresql 8.2 or later, you could probably write your insert statement like this.
INSERT INTO tbl (data) VALUES ('something') RETURNING id;
which should also return the last inserted id.
Use nextval and currval, take a look here: http://www.postgresql.org/docs/8.0/static/functions-sequence.html