Postgresql - Using subqueries with alter sequence expressions - sql

Is it possible to use subqueries within alter expressions in PostgreSQL?
I want to alter a sequence value based on a primary key column value.
I tried using the following expression, but it wouldn't execute.
alter sequence public.sequenceX restart with (select max(table_id)+1 from table)

I don't believe you can do it like that but you should be able to use the setval function direction which is what the alter does.
select setval('sequenceX', (select max(table_id)+1 from table), false)
The false will make it return the next sequence number as exactly what is given.

In addition if you have mixed case object names and you're getting an error like this:
ERROR: relation "public.mytable_id_seq" does not exist
... the following version using regclass should be useful:
select setval('"public"."MyTable_Id_seq"'::regclass, (select MAX("Id") FROM "public"."MyTable"))

Related

How to use a temp sequence within a Postgresql function

I have some lines of SQL which will take a set of IDs from the same GROUP_ID that are not contiguous (ex. if some rows got deleted) and will make them contiguous again. I wanted to turn this into a function for reusability purposes. The lines work if executed individually but when I try to create the function I get the error
ERROR: relation "id_seq_temp" does not exist
LINE 10: UPDATE THINGS SET ID=nextval('id_se...
If I create a sequence outside of the function and use that sequence in the function instead then the function is created successfully (schema qualified or unqualified). However I felt like creating the temp sequence inside of the function rather than leaving it in the schema was a cleaner solution.
I have seen this question: Function shows error "relation my_table does not exist"
However, I'm using the public schema and schema qualifying the sequence with public. does not seem to help.
I've also seen this question: How to create a sql function using temp sequences and a SELECT on PostgreSQL8. I probably could use generate_series but this adds a lot of complexity that SERIES solves such as needing to know how big of a series to generate.
Here is my function, I anonymized some of the names - just in case there's a typo.
CREATE OR REPLACE FUNCTION reindex_ids(IN BIGINT) RETURNS VOID
LANGUAGE SQL
AS $$
CREATE TEMPORARY SEQUENCE id_seq_temp
MINVALUE 1
START WITH 1
INCREMENT BY 1;
ALTER SEQUENCE id_seq_temp RESTART;
UPDATE THINGS SET ID=ID+2000 WHERE GROUP_ID=$1;
UPDATE THINGS SET ID=nextval('id_seq_temp') WHERE GROUP_ID=$1;
$$;
Is it possible to use a sequence you create within a function later in the function?
Answer to question
The reason is that SQL functions (LANGUAGE sql) are parsed and planned as one. All objects used must exist before the function runs.
You can switch to PL/pgSQL, (LANGUAGE plpgsql) which plans each statement on demand. There you can create objects and use them in the next command.
See:
Why can PL/pgSQL functions have side effect, while SQL functions can't?
Since you are not returning anything, consider a PROCEDURE. (FUNCTION works, too.)
CREATE OR REPLACE PROCEDURE reindex_ids(IN bigint)
LANGUAGE plpgsql AS
$proc$
BEGIN
IF EXISTS ( SELECT FROM pg_catalog.pg_class
WHERE relname = 'id_seq_temp'
AND relnamespace = pg_my_temp_schema()
AND relkind = 'S') THEN
ALTER SEQUENCE id_seq_temp RESTART;
ELSE
CREATE TEMP SEQUENCE id_seq_temp;
END IF;
UPDATE things SET id = id + 2000 WHERE group_id = $1;
UPDATE things SET id = nextval('id_seq_temp') WHERE group_id = $1;
END
$proc$;
Call:
CALL reindex_ids(123);
This creates your temp sequence if it does not exist already.
If the sequence exists, it is reset. (Remember that temporary objects live for the duration of a session.)
In the unlikely event that some other object occupies the name, an exception is raised.
Alternative solutions
Solution 1
This usually works:
UPDATE things t
SET id = t1.new_id
FROM (
SELECT pk_id, row_number() OVER (ORDER BY id) AS new_id
FROM things
WHERE group_id = $1 -- your input here
) t1
WHERE t.pk_id = t1.pk_id;
And only updates each row once, so half the cost.
Replace pk_id with your PRIMARY KEY column, or any UNIQUE NOT NULL (combination of) column(s).
The trick is that the UPDATE typically processes rows according to the sort order of the subquery in the FROM clause. Updating in ascending order should never hit a duplicate key violation.
And the ORDER BY clause of the window function row_number() imposes that sort order on the resulting set. That's an undocumented implementation detail, so you might want to add an explicit ORDER BY to the subquery. But since the behavior of UPDATE is undocumented anyway, it still depends on an implementation detail.
You can wrap that into a plain SQL function.
Solution 2
Consider not doing what you are doing at all. Gaps in sequential numbers are typically expected and not a problem. Just live with it. See:
Serial numbers per group of rows for compound key

SQL - conditionally select a column if exists

I need to select a column only if it exists in table, else it can be set to null.
Sample table below, lets say the marks col is not necessary be there, so need to be checked if it exists
Table1:
name marks
joe 10
john 11
mary 13
Query:
select
name,
marks if it exists else null as marks1 -- pseudo code
from
table1
What should go in line to select marks ?
SQL Doesn't permit that. Your result set has two options:
Static inclusion
All from table or subquery through column-expansion with * and tbl.*
Perhaps this will suit your needs, SELECT * FROM table1; You'll always get that column, if it exists.
try this
IF COL_LENGTH('your_table_name','column_name_you_want_to_select') IS NULL BEGIN
--This means columns does not exist or permission is denied
END
else
--Do whatever you want
It is possible to achieve this in PostgreSQL using JSON. Consider the following SQL query:
SELECT c.relname, c.relkind, c.relispartition
FROM pg_class c
WHERE c.relkind IN ('r','p') AND
c.relnamespace=(SELECT oid FROM pg_namespace WHERE nspname='public')
In PostgreSQL 10+, that will show you the names of all the tables in public schema, including whether they are partitioned and if so whether the table is the partitioned table or one of the partitions of it. However, if you try to run the same query on PostgreSQL 9.6 or earlier, it will fail since the relispartition column does not exist on the pg_class table prior to PostgreSQL 10.
An obvious solution would be to dynamically generate the SQL based on a condition, or have two different versions of the SQL. However, suppose you don't want to do that, you want to have a single query which works on both versions – in other words, you want to conditionally select the relispartition column if it exists.
The core SQL language does not have any facility to conditionally select a column, but it is achievable in PostgreSQL using the row_to_json function, as follows:
SELECT c.relname, c.relkind,
(row_to_json(c)->>'relispartition')::boolean AS relispartition
FROM pg_class c
WHERE c.relkind IN ('r','p') AND
c.relnamespace=(SELECT oid FROM pg_namespace WHERE nspname='public')
If you try running that, you will find on PostgreSQL 10+ the relispartition column is returned as true/false, whereas in pre-10 versions it is NULL. You could make it return false instead of NULL in pre-10 versions by doing COALESCE((row_to_json(c)->>'relispartition')::boolean,false).
What this is doing, is row_to_json(c) turns all the data of the row into JSON. Next, ->>'relispartition' selects the value of the relispartition JSON object key as text, which will be the same as the value of the relispartition column; if there is no such key in the JSON, the result of that will be NULL. Then, ::boolean converts the string value true/false back into a PostgreSQL boolean value. (If your column is of some other type, use the appropriate cast for the type of your column.)
(Obviously this approach will not work in Postgres versions which are too old to have the necessary JSON support – I have tested it works in Postgres 9.4; while I haven't tested it in Postgres 9.3, it probably works there. However, I would not expect it to work in 9.2 or earlier – the ->> operator was added in 9.3, and the JSON type and row_to_json function was added in 9.2. However, I expect few people will need to support those old unsupported versions–9.3 was released in 2013, and 9.2 supported ended in 2017.)
Try this:
IF EXISTS( SELECT 1
FROM information_schema.columns
WHERE table_name='your_table' and column_name='your_column') THEN
SELECT your_column as 'some_column'
ELSE
SELECT NULL as 'some_column'
END IF
Replying to an old question yet again but here's my hacky solution to this problem since I don't know how to write SQL functions... yet! %I formats the string as an identifier, and if there is no such table the return value is NULL and the alias is used!
SELECT (SELECT format('%I', 'my_column')
AS my_column_alias
FROM information_schema.columns
WHERE table_name='my_table'
AND column_name='my_column')
FROM source_table
Hope this helps everybody out there =)

Use result of query in a function (postgres 8.3)

I am trying to do something like this:
select char_length(select text_field from table where key = 1)
This won't work, and I presume, because the return type of a query is a table, not a string or text variable.
Is there a way to specify row,col of the result from a select statement?
edit: I overlooked to mention, that char_length is a function.
When passing the result of a query to a function, simply wrap the query in brackets:
select char_length((select text_field from table where key = 1));
The outer set of brackets is for the function, the inner set converts a query to a result.
This syntax is not specific to postgres - it applies to all SQL servers.
This link shows the above code executing correctly (thanks to #Fahim Parkar for this)
Although, you could re-factor your query to not require this syntax, nevertheless this is how you "pass the result of a query to a function".
select char_length(text_field) from "table" where key = 1
Assuming key is a primary key or unique key the first example below will work. It works only if the sub-query returns only 1 row. The second example will work for 1 or more rows.
select char_length((select text_field from table where key = 1));
select char_length(text_field) from table where key = 1;
It should be
select char_length(text_field) from "table" where key = 1
Also I believe, your table name is not table.

In sqlite How to add column in table if same column is not exists in table

How can I add a column in an SQLite table if and only if the same column does not exist in the table?
Using ALTER TABLE I am able to create a new column but want to know how to check whether that column already exists in the table or not?
SQLite returns an error like "no such column: foo" if the table doesn't contain the column:
select foo from yourTable limit 1
Also you can get the create-table statement:
select sql from sqlite_master where tbl_name = 'YourTableName'
and then parse the result, looking for the column-name. I don't know of an elegant way to query the list of columns for a specified table, though one may exist.
Also if you attempt to do this:
alter table YourTable add column foo {column-def whatever it is}
you get an error from SQLite if the column already exists. You could trap that error too.
Finally you could do this:
select sql from sqlite_master
where tbl_name = 'YOURTABLE' and sql like '%"foo" CHAR%'; -- or whatever type
and if the specified table contains the column which is surrounded by double-quotes in the query, and with the type you have specified, you will get a result, otherwise an empty set. Specifying the datatype ensures that your LIKE substring match occurs on a column-name.
There's no way (that I know of) to do it all in a single SQLite query. You must use application code to manage the If/Elseness.
Check if table exists or not:
select count(*) from sqlite_master where type = 'table' and name = MyTable';
Check if column exists in table or now
pragma table_info(thumbnail);
However, a better approach may be explicit database schema updates based on schema versions your application maintains (e.g. specific alter table statement to go from schema version 1 to 2):
pragma user_version;
It seems like that it is impossible to do checking if the column not exists and addindg the new column in one command, because Sqlite don't support "IF NOT EXISTS" for column. "IF NOT EXISTS" works only on table.
Here is what I will do:
rev = ExecuteStatement("SELECT columnNamexx FROM tableNamexx limit 1;");
if(rev != SQLITE_OK){ // add col to table
ExecuteStatement("ALTER TABLE tableNamexx ADD COLUMN columnNamexx INTEGER DEFAULT 0;");
}
You can view the table columns by using '.schema tableName'

Duplicate value in a postgresql table

I'm trying to modify a table inside my PostgreSQL database, but it says there is duplicate! what is the best way to find a duplicate value inside a table? kinda a select query?
Try Like This
SELECT count(column_name), column_name
from table_name
group by column_name having count(column_name) > 1;
If you try to change a value in a column that is part of the PRIMARY KEY or has a UNIQUE constraint and get this error there, then you should be able to find the conflicting row by
SELECT *
FROM your_table
WHERE conflicting_column = conflicting_value;
If conflicting_value is a character type, put it in single quotes (').
EDIT: To find out which columns are affected by the constraint, check this post.
First of all, determine which fields in your table have to be unique. This may be something marked as a Primary Key, a unique index based on one or more fields or a check constraint, again based on one or more fields.
Once you've done that, look at what you're trying to insert and work out whether it busts any of the unique rules.
And yes, SELECT statements will help you determine what's wrong here. Use those to determine whether you are able to commit the row.