constraint on all numerical tables - sql

I need to put a constraint on all numerical columns, this is what I tried:
Everything has to be possitive
ALTER TABLE * ADD CONSTRAINT Checknumbers CHECK ( > 0 )
This isn't working but I can't find a solution for it.
Is their any other syntax that I can use or do I need to do it manualy for each table?

You would need to create a separate constraint for each column in each table. You could potentially write a bit of dynamic SQL for this
DECLARE
l_sql_stmt VARCHAR2(1000);
BEGIN
FOR x IN (SELECT *
FROM user_tab_columns
WHERE data_type = 'NUMBER'
AND table_name in (SELECT table_name
FROM user_tables
WHERE dropped = 'NO' )
LOOP
l_sql_stmt := 'ALTER TABLE ' || x.table_name ||
' ADD CONSTRAINT chk_' || x.table_name || '_' || x.column_name ||
' CHECK( ' || x.column_name || ' > 0)';
EXECUTE IMMEDIATE l_sql_stmt;
END LOOP;
END;
For every numeric column in every table in the current schema, this will attempt to create a check constraint. The constraint name is limited to 30 characters so if the sum of the length of the table name and the column name is more than 25, this will attempt to generate an invalid identifier. You'd need to figure out an alternate way of generating the constraint name (or you could let the system generate a name). This also won't handle case-sensitive identifiers if you happen to have any of those. You'd need to double-quote the identifiers if that is an issue for you.

Related

COMMENT ON generates ORA-00905: missing keyword through EXECUTE IMMEDIATE

I need help on figuring out the problem with the ORA-00905: missing keyword ORA-06512: at line 73
When it says line 73 it actually refers to the sql statement itself at line 56. However, I am using this same script with a different table which working perfectly.
By changing the schema, table and column name I keep getting this error. I have be experimenting with several versions and also using fetch into cursor.
It keeps saying the sql statement has missing keyword but it is working on another script with the same line. I am hoping somebody could help me here. This is my first time posting on this forum and I am hoping someday I could contribute to this great community. Thank you in advance!
DECLARE
--CREATE OR REPLACE PROCEDURE setcomment
--IS
CURSOR cur IS
SELECT COLUMN_NAME, TABLE_NAME, OWNER
FROM DBA_TAB_COLUMNS
WHERE COLUMN_NAME = 'SSAN'
ORDER BY OWNER ASC, TABLE_NAME ASC, COLUMN_NAME ASC;
c_schema_name DBA_TAB_COLUMNS.OWNER%type;
c_table_name DBA_TAB_COLUMNS.TABLE_NAME%type;
c_column_name DBA_TAB_COLUMNS.COLUMN_NAME%type;
--This is a variable name to concatenate column names from <c_schema_name>.<c_table_name>.<c_column_name>
col_name VARCHAR(250) ;
--This is a variable to hold SQL statement and the message to be commented
sql_stmt1 VARCHAR(2000) ;
msg VARCHAR(250) := ' '' Comment going here '' ';
BEGIN
--Looping r cursor through cur cursor. Retrieving a row of record at a time
FOR r in cur LOOP
c_schema_name := r.owner;
c_table_name := r.table_name;
c_column_name := r.column_name;
--Concatenate all the column names into a single column name.
col_name := c_schema_name||'.'||c_table_name||'.'||c_column_name;
sql_stmt1 := 'COMMENT ON COLUMN '|| col_name ||' IS ''Comment going here '' ' ;
-- sql_stmt1 := 'COMMENT ON COLUMN '|| col_name ||' IS '||msg;
EXECUTE IMMEDIATE sql_stmt1;
--EXECUTE IMMEDIATE 'COMMENT ON COLUMN '|| c_schema_name||'.'||c_table_name||'.'||c_column_name || ' IS '' Comment going here '' ' ;
DBMS_OUTPUT.PUT_LINE ('COMMENT ON ' || col_name || ' procedure completed....');
END LOOP;
END;
/
If you still cannot find a source of the error, then create a log table, run the below code, and display (select) all error entries from the table
Then try to manually run the command.
Does you user have an appriopriate privileges to comment on tables in other schemas ? It can have a privilege to SELECT from DBA_TAB_COLS, but that doesn't mean that it can modify other schemas/tables.
CREATE TABLE log_errors( error_msg varchar2(4000));
DECLARE
CURSOR cur IS
SELECT COLUMN_NAME, TABLE_NAME, OWNER
FROM DBA_TAB_COLUMNS
WHERE COLUMN_NAME = 'SSAN'
ORDER BY OWNER ASC, TABLE_NAME ASC, COLUMN_NAME ASC;
col_name VARCHAR(250) ;
sql_stmt1 VARCHAR(2000) ;
msg VARCHAR(250) := 'Comment going here';
BEGIN
FOR r in cur LOOP
col_name := '"'|| r.OWNER ||'"."'||r.TABLE_NAME||'"."'||r.COLUMN_NAME||'"';
sql_stmt1 := 'COMMENT ON COLUMN ' || col_name || ' IS ''' || msg || '''';
BEGIN
EXECUTE IMMEDIATE sql_stmt1;
EXCEPTION
WHEN OTHERS THEN
INSERT INTO log_errors( error_msg ) VALUES ( sql_stmt1 );
END;
END LOOP;
END;
/
SELECT * FROM log_errors;
In addition to Mathguy's answer - your script will fail if any of the tables has been created using quoted identifiers
Database Object Naming Rules
Every database object has a name. In a SQL statement, you represent
the name of an object with a quoted identifier or a nonquoted
identifier.
A quoted identifier begins and ends with double quotation marks (").
If you name a schema object using a quoted identifier, then you must
use the double quotation marks whenever you refer to that object.
A nonquoted identifier is not surrounded by any punctuation.
You can use either quoted or nonquoted identifiers to name any
database object. However, database names, global database names, and
database link names are always case insensitive and are stored as
uppercase. If you specify such names as quoted identifiers, then the
quotation marks are silently ignored.
Simple practical example - a name of the first table is nonquoted identifier, a name of the second table is quoted identifier :
CREATE TABLE table_one (
SSAN int
);
CREATE TABLE "TaBle ##% TWO" (
SSAN int
);
SELECT 'COMMENT ON COLUMN ' || OWNER || '.' || TABLE_NAME || '.' || COLUMN_NAME || ' IS ''My superb comment'''
As my_comment_command
FROM ALL_TAB_COLUMNS
WHERE COLUMN_NAME = 'SSAN' ;
MY_COMMENT_COMMAND
----------------------------------------------------------------
COMMENT ON COLUMN SCOTT.TABLE_ONE.SSAN IS 'My superb comment'
COMMENT ON COLUMN SCOTT.TaBle ##% TWO.SSAN IS 'My superb comment'
It's obvious, that the second command will fail.
But if you use quotes in your script, then everything will work fine:
SELECT 'COMMENT ON COLUMN "' || OWNER || '"."' || TABLE_NAME || '"."' || COLUMN_NAME || '" IS ''My superb comment'''
As my_comment_command
FROM ALL_TAB_COLUMNS
WHERE COLUMN_NAME = 'SSAN' ;
MY_COMMENT_COMMAND
----------------------------------------------------------------------
COMMENT ON COLUMN "SCOTT"."TABLE_ONE"."SSAN" IS 'My superb comment'
COMMENT ON COLUMN "SCOTT"."TaBle ##% TWO"."SSAN" IS 'My superb comment'

drop all tables sharing the same prefix in postgres

I would like to delete all tables sharing the same prefix ('supenh_agk') from the same database, using one sql command/query.
To do this in one command you need dynamic SQL with EXECUTE in a DO statement (or function):
DO
$do$
DECLARE
_tbl text;
BEGIN
FOR _tbl IN
SELECT quote_ident(table_schema) || '.'
|| quote_ident(table_name) -- escape identifier and schema-qualify!
FROM information_schema.tables
WHERE table_name LIKE 'prefix' || '%' -- your table name prefix
AND table_schema NOT LIKE 'pg\_%' -- exclude system schemas
LOOP
RAISE NOTICE '%',
-- EXECUTE
'DROP TABLE ' || _tbl; -- see below
END LOOP;
END
$do$;
This includes tables from all schemas the current user has access to. I excluded system schemas for safety.
If you do not escape identifiers properly the code fails for any non-standard identifier that requires double-quoting.
Plus, you run the risk of allowing SQL injection. All user input must be sanitized in dynamic code - that includes identifiers potentially provided by users.
Potentially hazardous! All those tables are dropped for good. I built in a safety. Inspect the generated statements before you actually execute: comment RAISE and uncomment the EXECUTE.
If any other objects (like views etc.) depend on a table you get an informative error message instead, which cancels the whole transaction. If you are confident that all dependents can die, too, append CASCADE:
'DROP TABLE ' || _tbl || ' CASCADE;
Closely related:
Update column in multiple tables
Changing all zeros (if any) across all columns (in a table) to... say 1
Alternatively you could build on the catalog table pg_class, which also provides the oid of the table and is faster:
...
FOR _tbl IN
SELECT c.oid::regclass::text -- escape identifier and schema-qualify!
FROM pg_catalog.pg_class c
JOIN pg_catalog.pg_namespace n ON n.oid = c.relnamespace
WHERE n.nspname NOT LIKE 'pg\_%' -- exclude system schemas
AND c.relname LIKE 'prefix' || '%' -- your table name prefix
AND c.relkind = 'r' -- only tables
...
System catalog or information schema?
How to check if a table exists in a given schema
How does c.oid::regclass defend against SQL injection?
Table name as a PostgreSQL function parameter
Or do it all in a single DROP command. Should be a bit more efficient:
DO
$do$
BEGIN
RAISE NOTICE '%', (
-- EXECUTE (
SELECT 'DROP TABLE ' || string_agg(format('%I.%I', schemaname, tablename), ', ')
-- || ' CASCADE' -- optional
FROM pg_catalog.pg_tables t
WHERE schemaname NOT LIKE 'pg\_%' -- exclude system schemas
AND tablename LIKE 'prefix' || '%' -- your table name prefix
);
END
$do$;
Related:
Is there a postgres command to list/drop all materialized views?
Using the conveniently fitting system catalog pg_tables in the last example. And format() for convenience. See:
How to check if a table exists in a given schema
Table name as a PostgreSQL function parameter
Suppose the prefix is 'sales_'
Step 1: Get all the table names with that prefix
SELECT table_name
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE 'sales_%';
Step 2: Click the "Download as CSV" button.
Step 3: Open the file in an editor and replace "sales_ with ,sales
and " with a space
Step 4: DROP TABLE sales_regist, sales_name, sales_info, sales_somthing;
This is sql server command, can you try this one, is it worked in postgres or not.
This query wil generate the sql script for delete
SELECT 'DROP TABLE "' || TABLE_NAME || '"'
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE '[prefix]%'
[EDIT]
begin
for arow in
SELECT 'DROP TABLE "' || TABLE_NAME || '"' as col1
FROM INFORMATION_SCHEMA.TABLES
WHERE TABLE_NAME LIKE '[prefix]%'
LOOP
--RAISE NOTICE '%',
EXECUTE 'DROP TABLE ' || arow ;
END LOOP;
end;

Is there a way to alter many tables to add default values to a common column name?

We have a set of 45 tables which carry a common column {variety}.
The need is to set all such columns with a default value {comedy}.
The ALTER TABLE (SCHEMA.TABLE_NAME) MODIFY(VARIETY DEFAULT 'COMEDY')
Will get it done, but I am wondering if there is a way to create a sql script in Oracle 11g that will change all tables within the schema which have a common coloumn name to the common default value.
DECLARE
cnt NUMBER;
BEGIN
FOR x IN (
SELECT DISTINCT t.table_name
FROM user_tables t
INNER JOIN user_tab_columns c ON c.table_name = t.table_name
) LOOP
EXECUTE IMMEDIATE 'ALTER TABLE (SCHEMA.' || x.table_name || ') MODIFY(VARIETY DEFAULT ''COMEDY'')';
END LOOP;
END;
The alter table statement can be written as following,
using alternate quoting mechanism.
'alter table ' || x.table_name || q'[ modify (variety default 'COMEDY')]'

PostgreSQL: how to efficiently alter multiple columns from psql?

I have PostgreSQL table with several boolean columns, currently containing only true or null. I want to do the following for all of them:
Add a default value of false
Change all null values to false
Add a not null constraint
ie.:
-- for each column specified:
update my_table set my_column = 'f' where my_column is null;
alter table my_table alter column my_column set default 'f';
alter table my_table alter column my_column set not null;
Is there a feature of psql (or standard SQL) that will iterate over a specified list of columns and apply a sequence of operations to each one?
You can not iterate over all columns, but to be safe you probably don’t want to do that anyway but specify which ones to alter yourself. Another way would be to do a script querying for the column names and then altering them.
To alter them you use ALTER TABLE. See the PgSQL doc:
http://www.postgresql.org/docs/8.4/static/sql-altertable.html
ALTER TABLE xy ALTER COLUMN a SET DEFAULT FALSE, ALTER COLUMN b SET NOT NULL
etc
This will do, needs version 8.4 or higher because of the VARIADIC.
CREATE OR REPLACE FUNCTION setdefaults(
IN _tname TEXT, -- tablename to alter
VARIADIC _cname TEXT[] -- all columnnames to alter
)
RETURNS boolean
LANGUAGE plpgsql
AS
$$
DECLARE
row record;
BEGIN
FOR row IN SELECT unnest(_cname) AS colname LOOP
EXECUTE 'ALTER TABLE ' || quote_ident(_tname) || ' ALTER COLUMN ' || quote_ident(row.colname) || ' SET DEFAULT false;';
EXECUTE 'UPDATE ' || quote_ident(_tname) || ' SET ' || quote_ident(row.colname) || ' = DEFAULT WHERE ' || quote_ident(row.colname) || ' IS NULL;';
EXECUTE 'ALTER TABLE ' || quote_ident(_tname) || ' ALTER COLUMN ' || quote_ident(row.colname) || ' SET NOT NULL;';
END LOOP;
RETURN TRUE;
END;
$$;
SELECT setdefaults('foo', 'x','y','z'); -- alter table "foo"

generic stored procedure in oracle

I want to write a PLSQL stored procedure that accepts a table name as argument. This table is source table. Now inside my procedure i want to manipulate the fields of that table.
EX: I want to insert the records of this source table into another target table whose name is XYZ_<source table name>. The column names for source and target tables are the same. But there may be some extra fields in target table. How do i do it? The order of column names is not same.
You will have to build the INSERT statement dynamically.
create or replace procedure gen_insert
(p_src_table in user_tables.table_name%type
, p_no_of_rows out pls_integer)
is
col_str varchar2(16000);
begin
for rec in ( select column_name
, column_id
from user_tab_columns
where table_name = p_src_table
order by column_id )
loop
if rec.column_id != 1 then
col_str := col_str || ',' || rec.column_name;
else
col_str := rec.column_name;
end if:
end loop;
execute immediate 'insert into xyz_' || p_src_table || '('
|| col_str || ')'
|| ' select ' || col_str
|| ' from ' || p_src_table;
p_no_of_rows := sql%rowcount;
end;
/
Obviously you may want to include some error handling and other improvements.
edit
Having edited your question I see you have a special requirement for naming the target table which was obscured by the SO formatting.
You can do this using Dynamic SQL. Here's a link with basic info on Oracle Dynamic SQL